1
0
mirror of https://github.com/osmarks/mycorrhiza.git synced 2025-01-08 10:51:09 +00:00

Auth: Do not let users with weird characters in name register

This commit is contained in:
Timur Ismagilov 2022-05-17 16:31:12 +03:00
parent 2a1e6409c8
commit c1ac0bbd16
3 changed files with 55 additions and 43 deletions

View File

@ -4,6 +4,7 @@ import (
"crypto/hmac" "crypto/hmac"
"crypto/sha256" "crypto/sha256"
"encoding/hex" "encoding/hex"
"errors"
"fmt" "fmt"
"log" "log"
"net/http" "net/http"
@ -41,6 +42,9 @@ 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, group, source string, force bool) error { func Register(username, password, group, source string, force bool) error {
if !IsValidUsername(username) {
return fmt.Errorf("illegal username %s", username)
}
username = util.CanonicalName(username) username = util.CanonicalName(username)
switch { switch {
@ -75,26 +79,26 @@ func Register(username, password, group, source string, force bool) error {
// 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.
// //
// The HTTP parameters are used for setting header status (bad request, if it is bad) and saving a cookie. // The HTTP parameters are used for setting header status (bad request, if it is bad) and saving a cookie.
func LoginDataHTTP(w http.ResponseWriter, rq *http.Request, username, password string) string { func LoginDataHTTP(w http.ResponseWriter, username, password string) error {
w.Header().Set("Content-Type", "text/html;charset=utf-8") w.Header().Set("Content-Type", "text/html;charset=utf-8")
if !HasUsername(username) { if !HasUsername(username) {
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
log.Println("Unknown username", username, "was entered") log.Println("Unknown username", username, "was entered")
return "unknown username" return errors.New("unknown username")
} }
if !CredentialsOK(username, password) { if !CredentialsOK(username, password) {
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
log.Println("A wrong password was entered for username", username) log.Println("A wrong password was entered for username", username)
return "wrong password" return errors.New("wrong password")
} }
token, err := AddSession(username) token, err := AddSession(username)
if err != nil { if err != nil {
log.Println(err) log.Println(err)
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
return err.Error() return err
} }
http.SetCookie(w, cookie("token", token, time.Now().Add(365*24*time.Hour))) http.SetCookie(w, cookie("token", token, time.Now().Add(365*24*time.Hour)))
return "" return nil
} }
// AddSession saves a session for `username` and returns a token to use. // AddSession saves a session for `username` and returns a token to use.

View File

@ -1,8 +1,8 @@
package user package user
import ( import (
"fmt"
"net/http" "net/http"
"regexp"
"strings" "strings"
"sync" "sync"
"time" "time"
@ -11,8 +11,6 @@ import (
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
) )
var usernamePattern = regexp.MustCompile(`[^?!:#@><*|"'&%{}/]+`)
// User contains information about a given user required for identification. // User contains information about a given user required for identification.
type User struct { type User struct {
// Name is a username. It must follow hypha naming rules. // Name is a username. It must follow hypha naming rules.
@ -138,8 +136,14 @@ func (user *User) ShowLockMaybe(w http.ResponseWriter, rq *http.Request) bool {
// IsValidUsername checks if the given username is valid. // IsValidUsername checks if the given username is valid.
func IsValidUsername(username string) bool { func IsValidUsername(username string) bool {
return username != "anon" && username != "wikimind" && fmt.Println("Is", username, "ok")
usernamePattern.MatchString(strings.TrimSpace(username)) && for _, r := range username {
if strings.ContainsRune("?!:#@><*|\"'&%{}/", r) {
return false
}
}
return username != "anon" &&
username != "wikimind" &&
usernameIsWhiteListed(username) usernameIsWhiteListed(username)
} }

View File

@ -27,7 +27,7 @@ func initAuth(r *mux.Router) {
return return
} }
if cfg.AllowRegistration { if cfg.AllowRegistration {
r.HandleFunc("/register", handlerRegister) r.HandleFunc("/register", handlerRegister).Methods(http.MethodPost, http.MethodGet)
} }
if cfg.TelegramEnabled { if cfg.TelegramEnabled {
r.HandleFunc("/telegram-login", handlerTelegramLogin) r.HandleFunc("/telegram-login", handlerTelegramLogin)
@ -60,34 +60,38 @@ func handlerRegister(w http.ResponseWriter, rq *http.Request) {
views.Register(rq), views.Register(rq),
), ),
) )
} else if rq.Method == http.MethodPost { return
var (
username = rq.PostFormValue("username")
password = rq.PostFormValue("password")
err = user.Register(username, password, "editor", "local", 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)
_, _ = io.WriteString(
w,
views.Base(
viewutil.MetaFrom(w, rq),
lc.Get("auth.register_title"),
fmt.Sprintf(
`<main class="main-width"><p>%s</p><p><a href="/register">%s<a></p></main>`,
err.Error(),
lc.Get("auth.try_again"),
),
),
)
} else {
log.Printf("Successfully registered %s", username)
user.LoginDataHTTP(w, rq, username, password)
http.Redirect(w, rq, "/"+rq.URL.RawQuery, http.StatusSeeOther)
}
} }
var (
username = rq.PostFormValue("username")
password = rq.PostFormValue("password")
err = user.Register(username, password, "editor", "local", 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)
_, _ = io.WriteString(
w,
views.Base(
viewutil.MetaFrom(w, rq),
lc.Get("auth.register_title"),
fmt.Sprintf(
`<main class="main-width"><p>%s</p><p><a href="/register">%s<a></p></main>`,
err.Error(),
lc.Get("auth.try_again"),
),
),
)
return
}
log.Printf("Successfully registered %s", username)
if err := user.LoginDataHTTP(w, username, password); err != nil {
return
}
http.Redirect(w, rq, "/"+rq.URL.RawQuery, http.StatusSeeOther)
} }
// handlerLogout shows the logout form (GET) or logs the user out (POST). // handlerLogout shows the logout form (GET) or logs the user out (POST).
@ -134,12 +138,12 @@ func handlerLogin(w http.ResponseWriter, rq *http.Request) {
var ( var (
username = util.CanonicalName(rq.PostFormValue("username")) username = util.CanonicalName(rq.PostFormValue("username"))
password = rq.PostFormValue("password") password = rq.PostFormValue("password")
err = user.LoginDataHTTP(w, rq, username, password) err = user.LoginDataHTTP(w, username, password)
) )
if err != "" { if err != nil {
w.Header().Set("Content-Type", "text/html;charset=utf-8") w.Header().Set("Content-Type", "text/html;charset=utf-8")
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)
_, _ = io.WriteString(w, views.Base(viewutil.MetaFrom(w, rq), err, views.LoginError(err, lc))) _, _ = io.WriteString(w, views.Base(viewutil.MetaFrom(w, rq), err.Error(), views.LoginError(err.Error(), lc)))
return return
} }
http.Redirect(w, rq, "/", http.StatusSeeOther) http.Redirect(w, rq, "/", http.StatusSeeOther)
@ -191,8 +195,8 @@ func handlerTelegramLogin(w http.ResponseWriter, rq *http.Request) {
return return
} }
errmsg := user.LoginDataHTTP(w, rq, username, "") errmsg := user.LoginDataHTTP(w, username, "")
if errmsg != "" { if errmsg != nil {
log.Printf("Failed to login %s using Telegram: %s", username, err.Error()) log.Printf("Failed to login %s using Telegram: %s", username, err.Error())
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
_, _ = io.WriteString( _, _ = io.WriteString(