mirror of
https://github.com/osmarks/mycorrhiza.git
synced 2025-01-08 19:00:25 +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"
|
_ "embed"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
"github.com/bouncepaw/mycorrhiza/cfg"
|
"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.
|
// 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.
|
// printHelp prints the help message.
|
||||||
func printHelp() {
|
func printHelp() {
|
||||||
fmt.Fprintf(
|
fmt.Fprintf(
|
||||||
@ -28,13 +28,22 @@ func printHelp() {
|
|||||||
flag.PrintDefaults()
|
flag.PrintDefaults()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CreateUserCommand struct {
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
|
||||||
// parseCliArgs parses CLI options and sets several important global variables. Call it early.
|
// parseCliArgs parses CLI options and sets several important global variables. Call it early.
|
||||||
func parseCliArgs() {
|
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()
|
flag.Parse()
|
||||||
|
|
||||||
args := flag.Args()
|
args := flag.Args()
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
log.Fatal("Error: pass a wiki directory")
|
log.Fatal("error: pass a wiki directory")
|
||||||
}
|
}
|
||||||
|
|
||||||
wikiDir, err := filepath.Abs(args[0])
|
wikiDir, err := filepath.Abs(args[0])
|
||||||
@ -43,4 +52,46 @@ func parseCliArgs() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cfg.WikiDir = wikiDir
|
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/smartystreets/goconvey v1.6.4 // indirect
|
||||||
github.com/valyala/quicktemplate v1.6.3
|
github.com/valyala/quicktemplate v1.6.3
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
|
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
|
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-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-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-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/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
|
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.
|
// 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)
|
username = util.CanonicalName(username)
|
||||||
log.Println("Attempt to register user", username)
|
|
||||||
switch {
|
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):
|
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)
|
hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
u := User{
|
u := User{
|
||||||
Name: username,
|
Name: username,
|
||||||
Group: "editor",
|
Group: group,
|
||||||
Password: string(hash),
|
Password: string(hash),
|
||||||
RegisteredAt: time.Now(),
|
RegisteredAt: time.Now(),
|
||||||
}
|
}
|
||||||
users.Store(username, &u)
|
users.Store(username, &u)
|
||||||
err = SaveUserDatabase()
|
return SaveUserDatabase()
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoginDataHTTP logs such user in and returns string representation of an error if there is any.
|
// 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 (
|
var (
|
||||||
username = rq.PostFormValue("username")
|
username = rq.PostFormValue("username")
|
||||||
password = rq.PostFormValue("password")
|
password = rq.PostFormValue("password")
|
||||||
err = user.Register(username, password)
|
err = user.Register(username, password, "editor", false)
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Printf("Failed to register \"%s\": %s", username, err.Error())
|
||||||
w.Header().Set("Content-Type", mime.TypeByExtension(".html"))
|
w.Header().Set("Content-Type", mime.TypeByExtension(".html"))
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
fmt.Fprint(
|
fmt.Fprint(
|
||||||
@ -62,6 +63,7 @@ func handlerRegister(w http.ResponseWriter, rq *http.Request) {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
log.Printf("Successfully registered \"%s\"", username)
|
||||||
user.LoginDataHTTP(w, rq, username, password)
|
user.LoginDataHTTP(w, rq, username, password)
|
||||||
http.Redirect(w, rq, "/"+rq.URL.RawQuery, http.StatusSeeOther)
|
http.Redirect(w, rq, "/"+rq.URL.RawQuery, http.StatusSeeOther)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user