diff --git a/user/net.go b/user/net.go
index 831c722..d1799a3 100644
--- a/user/net.go
+++ b/user/net.go
@@ -4,6 +4,7 @@ import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
+ "errors"
"fmt"
"log"
"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.
func Register(username, password, group, source string, force bool) error {
+ if !IsValidUsername(username) {
+ return fmt.Errorf("illegal username ‘%s’", username)
+ }
username = util.CanonicalName(username)
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.
//
// 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")
if !HasUsername(username) {
w.WriteHeader(http.StatusBadRequest)
log.Println("Unknown username", username, "was entered")
- return "unknown username"
+ return errors.New("unknown username")
}
if !CredentialsOK(username, password) {
w.WriteHeader(http.StatusBadRequest)
log.Println("A wrong password was entered for username", username)
- return "wrong password"
+ return errors.New("wrong password")
}
token, err := AddSession(username)
if err != nil {
log.Println(err)
w.WriteHeader(http.StatusBadRequest)
- return err.Error()
+ return err
}
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.
diff --git a/user/user.go b/user/user.go
index 731e679..c87388b 100644
--- a/user/user.go
+++ b/user/user.go
@@ -1,8 +1,8 @@
package user
import (
+ "fmt"
"net/http"
- "regexp"
"strings"
"sync"
"time"
@@ -11,8 +11,6 @@ import (
"golang.org/x/crypto/bcrypt"
)
-var usernamePattern = regexp.MustCompile(`[^?!:#@><*|"'&%{}/]+`)
-
// User contains information about a given user required for identification.
type User struct {
// 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.
func IsValidUsername(username string) bool {
- return username != "anon" && username != "wikimind" &&
- usernamePattern.MatchString(strings.TrimSpace(username)) &&
+ fmt.Println("Is", username, "ok")
+ for _, r := range username {
+ if strings.ContainsRune("?!:#@><*|\"'&%{}/", r) {
+ return false
+ }
+ }
+ return username != "anon" &&
+ username != "wikimind" &&
usernameIsWhiteListed(username)
}
diff --git a/web/auth.go b/web/auth.go
index f4ea069..4fd550b 100644
--- a/web/auth.go
+++ b/web/auth.go
@@ -27,7 +27,7 @@ func initAuth(r *mux.Router) {
return
}
if cfg.AllowRegistration {
- r.HandleFunc("/register", handlerRegister)
+ r.HandleFunc("/register", handlerRegister).Methods(http.MethodPost, http.MethodGet)
}
if cfg.TelegramEnabled {
r.HandleFunc("/telegram-login", handlerTelegramLogin)
@@ -60,34 +60,38 @@ func handlerRegister(w http.ResponseWriter, rq *http.Request) {
views.Register(rq),
),
)
- } else if rq.Method == http.MethodPost {
- 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(
- `%s
%s
`,
- 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)
- }
+ 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(
+ `%s
%s
`,
+ 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).
@@ -134,12 +138,12 @@ func handlerLogin(w http.ResponseWriter, rq *http.Request) {
var (
username = util.CanonicalName(rq.PostFormValue("username"))
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.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
}
http.Redirect(w, rq, "/", http.StatusSeeOther)
@@ -191,8 +195,8 @@ func handlerTelegramLogin(w http.ResponseWriter, rq *http.Request) {
return
}
- errmsg := user.LoginDataHTTP(w, rq, username, "")
- if errmsg != "" {
+ errmsg := user.LoginDataHTTP(w, username, "")
+ if errmsg != nil {
log.Printf("Failed to login ‘%s’ using Telegram: %s", username, err.Error())
w.WriteHeader(http.StatusBadRequest)
_, _ = io.WriteString(