mirror of
https://github.com/osmarks/mycorrhiza.git
synced 2025-01-06 10:00:26 +00:00
Add -create-admin flag
It allows for interactive creation of admin account from the terminal for new wikis. Because we have a chicken-and-egg problem here with the user panel -- you need an admin account to appoint someone as an admin, right?
This commit is contained in:
parent
d9008bcb3f
commit
d4fea3d24a
63
flag.go
63
flag.go
@ -4,20 +4,20 @@ import (
|
||||
_ "embed"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
|
||||
"github.com/bouncepaw/mycorrhiza/cfg"
|
||||
"github.com/bouncepaw/mycorrhiza/files"
|
||||
"github.com/bouncepaw/mycorrhiza/user"
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
// CLI options are read and parsed here.
|
||||
|
||||
func init() {
|
||||
flag.StringVar(&cfg.HTTPPort, "port", "", "Listen on another port. This option also updates the config file for your convenience.")
|
||||
flag.Usage = printHelp
|
||||
}
|
||||
|
||||
// printHelp prints the help message.
|
||||
func printHelp() {
|
||||
fmt.Fprintf(
|
||||
@ -28,13 +28,22 @@ func printHelp() {
|
||||
flag.PrintDefaults()
|
||||
}
|
||||
|
||||
type CreateUserCommand struct {
|
||||
name string
|
||||
}
|
||||
|
||||
// parseCliArgs parses CLI options and sets several important global variables. Call it early.
|
||||
func parseCliArgs() {
|
||||
var createAdminName string
|
||||
|
||||
flag.StringVar(&createAdminName, "create-admin", "", "Create a new admin. The password will be prompted in the terminal.")
|
||||
flag.StringVar(&cfg.HTTPPort, "port", "", "Listen on another port. This option also updates the config file for your convenience.")
|
||||
flag.Usage = printHelp
|
||||
flag.Parse()
|
||||
|
||||
args := flag.Args()
|
||||
if len(args) == 0 {
|
||||
log.Fatal("Error: pass a wiki directory")
|
||||
log.Fatal("error: pass a wiki directory")
|
||||
}
|
||||
|
||||
wikiDir, err := filepath.Abs(args[0])
|
||||
@ -43,4 +52,46 @@ func parseCliArgs() {
|
||||
}
|
||||
|
||||
cfg.WikiDir = wikiDir
|
||||
|
||||
if createAdminName != "" {
|
||||
createAdminCommand(createAdminName)
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
|
||||
func createAdminCommand(name string) {
|
||||
wr := log.Writer()
|
||||
log.SetFlags(0)
|
||||
|
||||
if err := files.PrepareWikiRoot(); err != nil {
|
||||
log.Fatal("error: ", err)
|
||||
}
|
||||
cfg.UseAuth = true
|
||||
cfg.AllowRegistration = true
|
||||
|
||||
log.SetOutput(io.Discard)
|
||||
user.InitUserDatabase()
|
||||
log.SetOutput(wr)
|
||||
|
||||
handle := int(syscall.Stdin)
|
||||
if !term.IsTerminal(handle) {
|
||||
log.Fatal("error: not a terminal")
|
||||
}
|
||||
|
||||
fmt.Print("Password: ")
|
||||
passwordBytes, err := term.ReadPassword(handle)
|
||||
fmt.Print("\n")
|
||||
if err != nil {
|
||||
log.Fatal("error: ", err)
|
||||
}
|
||||
|
||||
password := string(passwordBytes)
|
||||
|
||||
log.SetOutput(io.Discard)
|
||||
err = user.Register(name, password, "admin", true)
|
||||
log.SetOutput(wr)
|
||||
|
||||
if err != nil {
|
||||
log.Fatal("error: ", err)
|
||||
}
|
||||
}
|
||||
|
1
go.mod
1
go.mod
@ -10,6 +10,7 @@ require (
|
||||
github.com/smartystreets/goconvey v1.6.4 // indirect
|
||||
github.com/valyala/quicktemplate v1.6.3
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
|
||||
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b
|
||||
gopkg.in/ini.v1 v1.62.0 // indirect
|
||||
)
|
||||
|
||||
|
4
go.sum
4
go.sum
@ -36,6 +36,10 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b h1:9zKuko04nR4gjZ4+DNjHqRlAJqbJETHwiNKDqTfOjfE=
|
||||
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
|
||||
|
27
user/net.go
27
user/net.go
@ -35,36 +35,31 @@ func LogoutFromRequest(w http.ResponseWriter, rq *http.Request) {
|
||||
}
|
||||
|
||||
// Register registers the given user. If it fails, a non-nil error is returned.
|
||||
func Register(username, password string) error {
|
||||
func Register(username, password, group string, force bool) error {
|
||||
username = util.CanonicalName(username)
|
||||
log.Println("Attempt to register user", username)
|
||||
|
||||
switch {
|
||||
case cfg.RegistrationLimit > 0 && Count() >= cfg.RegistrationLimit:
|
||||
log.Printf("Limit reached: %d", cfg.RegistrationLimit)
|
||||
return fmt.Errorf("Reached the limit of registered users: %d", cfg.RegistrationLimit)
|
||||
case HasUsername(username):
|
||||
log.Println("Username taken")
|
||||
return fmt.Errorf("Username \"%s\" is taken already.", username)
|
||||
case !util.IsPossibleUsername(username):
|
||||
log.Println("Illegal username:", username)
|
||||
return fmt.Errorf("Illegal username \"%s\".", username)
|
||||
return fmt.Errorf("illegal username \"%s\"", username)
|
||||
case !force && cfg.RegistrationLimit > 0 && Count() >= cfg.RegistrationLimit:
|
||||
return fmt.Errorf("reached the limit of registered users (%d)", cfg.RegistrationLimit)
|
||||
case !force && HasUsername(username):
|
||||
return fmt.Errorf("username \"%s\" is already taken", username)
|
||||
}
|
||||
|
||||
hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
u := User{
|
||||
Name: username,
|
||||
Group: "editor",
|
||||
Group: group,
|
||||
Password: string(hash),
|
||||
RegisteredAt: time.Now(),
|
||||
}
|
||||
users.Store(username, &u)
|
||||
err = SaveUserDatabase()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return SaveUserDatabase()
|
||||
}
|
||||
|
||||
// LoginDataHTTP logs such user in and returns string representation of an error if there is any.
|
||||
|
@ -45,9 +45,10 @@ func handlerRegister(w http.ResponseWriter, rq *http.Request) {
|
||||
var (
|
||||
username = rq.PostFormValue("username")
|
||||
password = rq.PostFormValue("password")
|
||||
err = user.Register(username, password)
|
||||
err = user.Register(username, password, "editor", false)
|
||||
)
|
||||
if err != nil {
|
||||
log.Printf("Failed to register \"%s\": %s", username, err.Error())
|
||||
w.Header().Set("Content-Type", mime.TypeByExtension(".html"))
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
fmt.Fprint(
|
||||
@ -62,6 +63,7 @@ func handlerRegister(w http.ResponseWriter, rq *http.Request) {
|
||||
),
|
||||
)
|
||||
} else {
|
||||
log.Printf("Successfully registered \"%s\"", username)
|
||||
user.LoginDataHTTP(w, rq, username, password)
|
||||
http.Redirect(w, rq, "/"+rq.URL.RawQuery, http.StatusSeeOther)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user