Skip to content

Commit 97b08ed

Browse files
authored
Admin: add src admin create command (#957)
* Add `src admin create` command * Add command to setup an initial admin account on a new deployment of Sourcegraph. * Add option to use env var for admin pass * Fixing issues pointed out in PR
1 parent db398d0 commit 97b08ed

File tree

4 files changed

+596
-0
lines changed

4 files changed

+596
-0
lines changed

cmd/src/admin.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package main
2+
3+
import (
4+
"flag"
5+
"fmt"
6+
)
7+
8+
var adminCommands commander
9+
10+
func init() {
11+
usage := `'src admin' is a tool that manages an initial admin user on a new Sourcegraph instance.
12+
13+
Usage:
14+
15+
src admin create [command options]
16+
17+
The commands are:
18+
19+
create create an initial admin user
20+
21+
Use "src admin [command] -h" for more information about a command.
22+
`
23+
24+
flagSet := flag.NewFlagSet("admin", flag.ExitOnError)
25+
handler := func(args []string) error {
26+
adminCommands.run(flagSet, "srv admin", usage, args)
27+
return nil
28+
}
29+
30+
commands = append(commands, &command{
31+
flagSet: flagSet,
32+
aliases: []string{"admin"},
33+
handler: handler,
34+
usageFunc: func() {
35+
fmt.Println(usage)
36+
},
37+
})
38+
}

cmd/src/admin_create.go

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package main
2+
3+
import (
4+
"flag"
5+
"fmt"
6+
"os"
7+
8+
"github.com/sourcegraph/src-cli/internal/users"
9+
10+
"github.com/sourcegraph/sourcegraph/lib/errors"
11+
)
12+
13+
func init() {
14+
usage := `
15+
Examples:
16+
17+
Create an initial admin user on a new Sourcegraph deployment:
18+
19+
$ src admin create -url https://your-sourcegraph-url -username admin -email admin@yourcompany.com -with-token
20+
21+
Create an initial admin user on a new Sourcegraph deployment using '-password' flag.
22+
WARNING: for security purposes we strongly recommend using the SRC_ADMIN_PASS environment variable when possible.
23+
24+
$ src admin create -url https://your-sourcegraph-url -username admin -email admin@yourcompany.com -password p@55w0rd -with-token
25+
26+
Environmental variables
27+
28+
SRC_ADMIN_PASS The new admin user's password
29+
`
30+
31+
flagSet := flag.NewFlagSet("create", flag.ExitOnError)
32+
usageFunc := func() {
33+
fmt.Fprintf(flag.CommandLine.Output(), "Usage of 'src users %s':\n", flagSet.Name())
34+
flagSet.PrintDefaults()
35+
fmt.Println(usage)
36+
}
37+
38+
var (
39+
urlFlag = flagSet.String("url", "", "The base URL for the Sourcegraph instance.")
40+
usernameFlag = flagSet.String("username", "", "The new admin user's username.")
41+
emailFlag = flagSet.String("email", "", "The new admin user's email address.")
42+
passwordFlag = flagSet.String("password", "", "The new admin user's password.")
43+
tokenFlag = flagSet.Bool("with-token", false, "Optionally create and output an admin access token.")
44+
)
45+
46+
handler := func(args []string) error {
47+
if err := flagSet.Parse(args); err != nil {
48+
return err
49+
}
50+
51+
ok, _, err := users.NeedsSiteInit(*urlFlag)
52+
if err != nil {
53+
return err
54+
}
55+
if !ok {
56+
return errors.New("failed to create admin, site already initialized")
57+
}
58+
59+
envAdminPass := os.Getenv("SRC_ADMIN_PASS")
60+
61+
var client *users.Client
62+
63+
switch {
64+
case envAdminPass != "" && *passwordFlag == "":
65+
client, err = users.SiteAdminInit(*urlFlag, *emailFlag, *usernameFlag, envAdminPass)
66+
if err != nil {
67+
return err
68+
}
69+
case envAdminPass == "" && *passwordFlag != "":
70+
client, err = users.SiteAdminInit(*urlFlag, *emailFlag, *usernameFlag, *passwordFlag)
71+
if err != nil {
72+
return err
73+
}
74+
case envAdminPass != "" && *passwordFlag != "":
75+
return errors.New("failed to read admin password: environment variable and -password flag both set")
76+
case envAdminPass == "" && *passwordFlag == "":
77+
return errors.New("failed to read admin password from 'SRC_ADMIN_PASS' environment variable or -password flag")
78+
}
79+
80+
if *tokenFlag {
81+
token, err := client.CreateAccessToken("", []string{"user:all", "site-admin:sudo"}, "src-cli")
82+
if err != nil {
83+
return err
84+
}
85+
86+
_, err = fmt.Fprintf(flag.CommandLine.Output(), "%s\n", token)
87+
if err != nil {
88+
return err
89+
}
90+
}
91+
92+
return nil
93+
}
94+
95+
adminCommands = append(adminCommands, &command{
96+
flagSet: flagSet,
97+
handler: handler,
98+
usageFunc: usageFunc,
99+
})
100+
}

internal/lazyregexp/lazyregexp.go

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
// Copyright 2018 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// Package lazyregexp is a thin wrapper over regexp, allowing the use of global
6+
// regexp variables without forcing them to be compiled at init.
7+
package lazyregexp
8+
9+
import (
10+
"os"
11+
"strings"
12+
"sync"
13+
14+
"github.com/grafana/regexp"
15+
)
16+
17+
// Regexp is a wrapper around regexp.Regexp, where the underlying regexp will be
18+
// compiled the first time it is needed.
19+
type Regexp struct {
20+
str string
21+
posix bool
22+
once sync.Once
23+
rx *regexp.Regexp
24+
}
25+
26+
func (r *Regexp) Re() *regexp.Regexp {
27+
r.once.Do(r.build)
28+
return r.rx
29+
}
30+
31+
func (r *Regexp) build() {
32+
if r.posix {
33+
r.rx = regexp.MustCompilePOSIX(r.str)
34+
} else {
35+
r.rx = regexp.MustCompile(r.str)
36+
}
37+
r.str = ""
38+
}
39+
40+
func (r *Regexp) FindSubmatch(s []byte) [][]byte {
41+
return r.Re().FindSubmatch(s)
42+
}
43+
44+
func (r *Regexp) FindStringSubmatch(s string) []string {
45+
return r.Re().FindStringSubmatch(s)
46+
}
47+
48+
func (r *Regexp) FindStringSubmatchIndex(s string) []int {
49+
return r.Re().FindStringSubmatchIndex(s)
50+
}
51+
52+
func (r *Regexp) ReplaceAllString(src, repl string) string {
53+
return r.Re().ReplaceAllString(src, repl)
54+
}
55+
56+
func (r *Regexp) FindString(s string) string {
57+
return r.Re().FindString(s)
58+
}
59+
60+
func (r *Regexp) FindAllString(s string, n int) []string {
61+
return r.Re().FindAllString(s, n)
62+
}
63+
64+
func (r *Regexp) MatchString(s string) bool {
65+
return r.Re().MatchString(s)
66+
}
67+
68+
func (r *Regexp) SubexpNames() []string {
69+
return r.Re().SubexpNames()
70+
}
71+
72+
func (r *Regexp) FindAllStringSubmatch(s string, n int) [][]string {
73+
return r.Re().FindAllStringSubmatch(s, n)
74+
}
75+
76+
func (r *Regexp) Split(s string, n int) []string {
77+
return r.Re().Split(s, n)
78+
}
79+
80+
func (r *Regexp) ReplaceAllLiteralString(src, repl string) string {
81+
return r.Re().ReplaceAllLiteralString(src, repl)
82+
}
83+
84+
func (r *Regexp) FindAllIndex(b []byte, n int) [][]int {
85+
return r.Re().FindAllIndex(b, n)
86+
}
87+
88+
func (r *Regexp) Match(b []byte) bool {
89+
return r.Re().Match(b)
90+
}
91+
92+
func (r *Regexp) ReplaceAllStringFunc(src string, repl func(string) string) string {
93+
return r.Re().ReplaceAllStringFunc(src, repl)
94+
}
95+
96+
func (r *Regexp) ReplaceAll(src, repl []byte) []byte {
97+
return r.Re().ReplaceAll(src, repl)
98+
}
99+
100+
func (r *Regexp) SubexpIndex(s string) int {
101+
return r.Re().SubexpIndex(s)
102+
}
103+
104+
var inTest = len(os.Args) > 0 && strings.HasSuffix(strings.TrimSuffix(os.Args[0], ".exe"), ".test")
105+
106+
// New creates a new lazy regexp, delaying the compiling work until it is first
107+
// needed. If the code is being run as part of tests, the regexp compiling will
108+
// happen immediately.
109+
func New(str string) *Regexp {
110+
lr := &Regexp{str: str}
111+
if inTest {
112+
// In tests, always compile the regexps early.
113+
lr.Re()
114+
}
115+
return lr
116+
}
117+
118+
// NewPOSIX creates a new lazy regexp, delaying the compiling work until it is
119+
// first needed. If the code is being run as part of tests, the regexp
120+
// compiling will happen immediately.
121+
func NewPOSIX(str string) *Regexp {
122+
lr := &Regexp{str: str, posix: true}
123+
if inTest {
124+
// In tests, always compile the regexps early.
125+
lr.Re()
126+
}
127+
return lr
128+
}

0 commit comments

Comments
 (0)