From aee2f23b5e9c993544eb1196cfff4f3730bf1f0a Mon Sep 17 00:00:00 2001 From: bouncepaw Date: Mon, 19 Apr 2021 21:39:25 +0500 Subject: [PATCH] You can now register, but the new account is not saved on disk --- http_auth.go | 16 ++++++++++++---- user/net.go | 32 ++++++++++++++++++++++++++++++++ user/user.go | 7 ++++++- user/users.go | 11 +++++++++++ util/config.go | 2 +- util/util.go | 15 +++++++++++++-- 6 files changed, 75 insertions(+), 8 deletions(-) diff --git a/http_auth.go b/http_auth.go index 52f6260..b248b62 100644 --- a/http_auth.go +++ b/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) + } } } diff --git a/user/net.go b/user/net.go index caec383..c2822af 100644 --- a/user/net.go +++ b/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") diff --git a/user/user.go b/user/user.go index efce8a2..e2d7235 100644 --- a/user/user.go +++ b/user/user.go @@ -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) diff --git a/user/users.go b/user/users.go index 9a56212..adf883d 100644 --- a/user/users.go +++ b/user/users.go @@ -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 { diff --git a/util/config.go b/util/config.go index c70f37b..e6c5fe9 100644 --- a/util/config.go +++ b/util/config.go @@ -82,5 +82,5 @@ func ReadConfigFile(path string) { FixedCredentialsPath = cfg.FixedAuthCredentialsPath UseRegistration = cfg.UseRegistration RegistrationCredentialsPath = cfg.RegistrationCredentialsPath - LimitRegistration = cfg.LimitRegistration + LimitRegistration = int(cfg.LimitRegistration) } diff --git a/util/util.go b/util/util.go index a88cba3..5a685ca 100644 --- a/util/util.go +++ b/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)) +}