mirror of
				https://github.com/osmarks/mycorrhiza.git
				synced 2025-10-31 07:33: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) { | func handlerRegister(w http.ResponseWriter, rq *http.Request) { | ||||||
| 	log.Println(rq.URL) | 	log.Println(rq.URL) | ||||||
| 	if util.UseRegistration { | 	if !util.UseRegistration { | ||||||
| 		w.WriteHeader(http.StatusOK) |  | ||||||
| 	} else { |  | ||||||
| 		w.WriteHeader(http.StatusForbidden) | 		w.WriteHeader(http.StatusForbidden) | ||||||
| 	} | 	} | ||||||
| 	if rq.Method == http.MethodGet { | 	if rq.Method == http.MethodGet { | ||||||
| @@ -35,7 +33,17 @@ func handlerRegister(w http.ResponseWriter, rq *http.Request) { | |||||||
| 			), | 			), | ||||||
| 		) | 		) | ||||||
| 	} else if rq.Method == http.MethodPost { | 	} 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 | package user | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"errors" | ||||||
| 	"log" | 	"log" | ||||||
| 	"net/http" | 	"net/http" | ||||||
|  | 	"strconv" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	"github.com/bouncepaw/mycorrhiza/util" | 	"github.com/bouncepaw/mycorrhiza/util" | ||||||
|  | 	"golang.org/x/crypto/bcrypt" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // CanProceed returns `true` if the user in `rq` has enough rights to access `route`. | // 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. | // 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 { | func LoginDataHTTP(w http.ResponseWriter, rq *http.Request, username, password string) string { | ||||||
| 	w.Header().Set("Content-Type", "text/html;charset=utf-8") | 	w.Header().Set("Content-Type", "text/html;charset=utf-8") | ||||||
|   | |||||||
| @@ -1,8 +1,9 @@ | |||||||
| package user | package user | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"golang.org/x/crypto/bcrypt" |  | ||||||
| 	"sync" | 	"sync" | ||||||
|  |  | ||||||
|  | 	"golang.org/x/crypto/bcrypt" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // UserSource shows where is the user data gotten from. | // UserSource shows where is the user data gotten from. | ||||||
| @@ -25,6 +26,10 @@ type User struct { | |||||||
| 	HashedPassword string     `json:"hashed_password"` // for registered | 	HashedPassword string     `json:"hashed_password"` // for registered | ||||||
| 	Source         UserSource `json:"-"` | 	Source         UserSource `json:"-"` | ||||||
| 	sync.RWMutex | 	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) | // Route — Right (more is more right) | ||||||
|   | |||||||
| @@ -30,6 +30,17 @@ func ListUsersWithGroup(group string) []string { | |||||||
| 	return usersWithTheGroup | 	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 { | func Count() int { | ||||||
| 	i := 0 | 	i := 0 | ||||||
| 	users.Range(func(k, v interface{}) bool { | 	users.Range(func(k, v interface{}) bool { | ||||||
|   | |||||||
| @@ -82,5 +82,5 @@ func ReadConfigFile(path string) { | |||||||
| 	FixedCredentialsPath = cfg.FixedAuthCredentialsPath | 	FixedCredentialsPath = cfg.FixedAuthCredentialsPath | ||||||
| 	UseRegistration = cfg.UseRegistration | 	UseRegistration = cfg.UseRegistration | ||||||
| 	RegistrationCredentialsPath = cfg.RegistrationCredentialsPath | 	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 | 	FixedCredentialsPath        string | ||||||
| 	UseRegistration             bool | 	UseRegistration             bool | ||||||
| 	RegistrationCredentialsPath string | 	RegistrationCredentialsPath string | ||||||
| 	LimitRegistration           uint64 | 	LimitRegistration           int | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // LettersNumbersOnly keeps letters and numbers only in the given string. | // 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. | // 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 { | 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. | // HyphaPattern is a pattern which all hyphae must match. | ||||||
| var HyphaPattern = regexp.MustCompile(`[^?!:#@><*|"\'&%{}]+`) | var HyphaPattern = regexp.MustCompile(`[^?!:#@><*|"\'&%{}]+`) | ||||||
|  |  | ||||||
|  | var UsernamePattern = regexp.MustCompile(`[^?!:#@><*|"\'&%{}/]+`) | ||||||
|  |  | ||||||
| // IsCanonicalName checks if the `name` is canonical. | // IsCanonicalName checks if the `name` is canonical. | ||||||
| func IsCanonicalName(name string) bool { | func IsCanonicalName(name string) bool { | ||||||
| 	return HyphaPattern.MatchString(name) | 	return HyphaPattern.MatchString(name) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func IsPossibleUsername(username string) bool { | ||||||
|  | 	return UsernamePattern.MatchString(strings.TrimSpace(username)) | ||||||
|  | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 bouncepaw
					bouncepaw