mirror of
				https://github.com/osmarks/mycorrhiza.git
				synced 2025-10-30 23:23:04 +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:
		
							
								
								
									
										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) | ||||
| 		} | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 handlerug
					handlerug