mirror of
				https://github.com/osmarks/mycorrhiza.git
				synced 2025-10-31 15:43:00 +00:00 
			
		
		
		
	You can now register, but the new account is not saved on disk
This commit is contained in:
		
							
								
								
									
										16
									
								
								http_auth.go
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								http_auth.go
									
									
									
									
									
								
							| @@ -20,9 +20,7 @@ func init() { | ||||
|  | ||||
| func handlerRegister(w http.ResponseWriter, rq *http.Request) { | ||||
| 	log.Println(rq.URL) | ||||
| 	if util.UseRegistration { | ||||
| 		w.WriteHeader(http.StatusOK) | ||||
| 	} else { | ||||
| 	if !util.UseRegistration { | ||||
| 		w.WriteHeader(http.StatusForbidden) | ||||
| 	} | ||||
| 	if rq.Method == http.MethodGet { | ||||
| @@ -35,7 +33,17 @@ func handlerRegister(w http.ResponseWriter, rq *http.Request) { | ||||
| 			), | ||||
| 		) | ||||
| 	} else if rq.Method == http.MethodPost { | ||||
| 		io.WriteString(w, "Not implemented") | ||||
| 		var ( | ||||
| 			username = rq.PostFormValue("username") | ||||
| 			password = rq.PostFormValue("password") | ||||
| 			err      = user.Register(username, password) | ||||
| 		) | ||||
| 		if err != nil { | ||||
| 			io.WriteString(w, err.Error()) | ||||
| 		} else { | ||||
| 			user.LoginDataHTTP(w, rq, username, password) | ||||
| 			http.Redirect(w, rq, "/"+rq.URL.RawQuery, http.StatusSeeOther) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										32
									
								
								user/net.go
									
									
									
									
									
								
							
							
						
						
									
										32
									
								
								user/net.go
									
									
									
									
									
								
							| @@ -1,11 +1,14 @@ | ||||
| package user | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"log" | ||||
| 	"net/http" | ||||
| 	"strconv" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/bouncepaw/mycorrhiza/util" | ||||
| 	"golang.org/x/crypto/bcrypt" | ||||
| ) | ||||
|  | ||||
| // CanProceed returns `true` if the user in `rq` has enough rights to access `route`. | ||||
| @@ -31,6 +34,35 @@ 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 { | ||||
| 	username = util.CanonicalName(username) | ||||
| 	log.Println("Attempt to register user", username) | ||||
| 	switch { | ||||
| 	case CountRegistered() >= util.LimitRegistration && util.LimitRegistration > 0: | ||||
| 		i := strconv.Itoa(util.LimitRegistration) | ||||
| 		log.Println("Limit reached: " + i) | ||||
| 		return errors.New("Reached the limit of registered users: " + i) | ||||
| 	case HasUsername(username): | ||||
| 		log.Println("Username taken") | ||||
| 		return errors.New("Username " + username + " is taken already.") | ||||
| 	case !util.IsPossibleUsername(username): | ||||
| 		log.Println("Illegal username:", username) | ||||
| 	} | ||||
| 	hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	u := User{ | ||||
| 		Name:           username, | ||||
| 		Group:          "editor", | ||||
| 		HashedPassword: string(hash), | ||||
| 		Source:         SourceRegistration, | ||||
| 	} | ||||
| 	users.Store(username, &u) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // LoginDataHTTP logs such user in and returns string representation of an error if there is any. | ||||
| func LoginDataHTTP(w http.ResponseWriter, rq *http.Request, username, password string) string { | ||||
| 	w.Header().Set("Content-Type", "text/html;charset=utf-8") | ||||
|   | ||||
| @@ -1,8 +1,9 @@ | ||||
| package user | ||||
|  | ||||
| import ( | ||||
| 	"golang.org/x/crypto/bcrypt" | ||||
| 	"sync" | ||||
|  | ||||
| 	"golang.org/x/crypto/bcrypt" | ||||
| ) | ||||
|  | ||||
| // UserSource shows where is the user data gotten from. | ||||
| @@ -25,6 +26,10 @@ type User struct { | ||||
| 	HashedPassword string     `json:"hashed_password"` // for registered | ||||
| 	Source         UserSource `json:"-"` | ||||
| 	sync.RWMutex | ||||
|  | ||||
| 	// A note about why HashedPassword is string and not []byte. The reason is | ||||
| 	// simple: golang's json marshals []byte as slice of numbers, which is not | ||||
| 	// acceptable. | ||||
| } | ||||
|  | ||||
| // Route — Right (more is more right) | ||||
|   | ||||
| @@ -30,6 +30,17 @@ func ListUsersWithGroup(group string) []string { | ||||
| 	return usersWithTheGroup | ||||
| } | ||||
|  | ||||
| func CountRegistered() int { | ||||
| 	i := 0 | ||||
| 	users.Range(func(k, v interface{}) bool { | ||||
| 		if v.(*User).Source == SourceRegistration { | ||||
| 			i++ | ||||
| 		} | ||||
| 		return true | ||||
| 	}) | ||||
| 	return i | ||||
| } | ||||
|  | ||||
| func Count() int { | ||||
| 	i := 0 | ||||
| 	users.Range(func(k, v interface{}) bool { | ||||
|   | ||||
| @@ -82,5 +82,5 @@ func ReadConfigFile(path string) { | ||||
| 	FixedCredentialsPath = cfg.FixedAuthCredentialsPath | ||||
| 	UseRegistration = cfg.UseRegistration | ||||
| 	RegistrationCredentialsPath = cfg.RegistrationCredentialsPath | ||||
| 	LimitRegistration = cfg.LimitRegistration | ||||
| 	LimitRegistration = int(cfg.LimitRegistration) | ||||
| } | ||||
|   | ||||
							
								
								
									
										15
									
								
								util/util.go
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								util/util.go
									
									
									
									
									
								
							| @@ -29,7 +29,7 @@ var ( | ||||
| 	FixedCredentialsPath        string | ||||
| 	UseRegistration             bool | ||||
| 	RegistrationCredentialsPath string | ||||
| 	LimitRegistration           uint64 | ||||
| 	LimitRegistration           int | ||||
| ) | ||||
|  | ||||
| // LettersNumbersOnly keeps letters and numbers only in the given string. | ||||
| @@ -94,13 +94,24 @@ func BeautifulName(uglyName string) string { | ||||
|  | ||||
| // CanonicalName makes sure the `name` is canonical. A name is canonical if it is lowercase and all spaces are replaced with underscores. | ||||
| func CanonicalName(name string) string { | ||||
| 	return strings.ToLower(strings.ReplaceAll(name, " ", "_")) | ||||
| 	return strings.ToLower( | ||||
| 		strings.ReplaceAll( | ||||
| 			strings.TrimRight( | ||||
| 				strings.TrimLeft(name, "_"), | ||||
| 				"_", | ||||
| 			), " ", "_")) | ||||
| } | ||||
|  | ||||
| // HyphaPattern is a pattern which all hyphae must match. | ||||
| var HyphaPattern = regexp.MustCompile(`[^?!:#@><*|"\'&%{}]+`) | ||||
|  | ||||
| var UsernamePattern = regexp.MustCompile(`[^?!:#@><*|"\'&%{}/]+`) | ||||
|  | ||||
| // IsCanonicalName checks if the `name` is canonical. | ||||
| func IsCanonicalName(name string) bool { | ||||
| 	return HyphaPattern.MatchString(name) | ||||
| } | ||||
|  | ||||
| func IsPossibleUsername(username string) bool { | ||||
| 	return UsernamePattern.MatchString(strings.TrimSpace(username)) | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 bouncepaw
					bouncepaw