From bf03105d09b266660f280f38840ad00e051a3706 Mon Sep 17 00:00:00 2001 From: Timur Ismagilov Date: Mon, 3 Jun 2024 23:07:35 +0300 Subject: [PATCH] Move auth routing to web --- auth/web.go | 225 ------------------------------------------------- web/web.go | 238 +++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 225 insertions(+), 238 deletions(-) delete mode 100644 auth/web.go diff --git a/auth/web.go b/auth/web.go deleted file mode 100644 index ee93bc9..0000000 --- a/auth/web.go +++ /dev/null @@ -1,225 +0,0 @@ -package auth - -import ( - "errors" - "fmt" - "io" - "log" - "mime" - "net/http" - "strings" - - "github.com/bouncepaw/mycorrhiza/viewutil" - - "github.com/gorilla/mux" - - "github.com/bouncepaw/mycorrhiza/cfg" - "github.com/bouncepaw/mycorrhiza/l18n" - "github.com/bouncepaw/mycorrhiza/user" - "github.com/bouncepaw/mycorrhiza/util" -) - -func InitAuth(r *mux.Router) { - r.HandleFunc("/user-list", handlerUserList) - r.HandleFunc("/lock", handlerLock) - // The check below saves a lot of extra checks and lines of codes in other places in this file. - if !cfg.UseAuth { - return - } - if cfg.AllowRegistration { - r.HandleFunc("/register", handlerRegister).Methods(http.MethodPost, http.MethodGet) - } - if cfg.TelegramEnabled { - r.HandleFunc("/telegram-login", handlerTelegramLogin) - } - r.HandleFunc("/login", handlerLogin) - r.HandleFunc("/logout", handlerLogout) -} - -func handlerUserList(w http.ResponseWriter, rq *http.Request) { - lc := l18n.FromRequest(rq) - w.Header().Set("Content-Type", mime.TypeByExtension(".html")) - w.WriteHeader(http.StatusOK) - w.Write([]byte(viewutil.Base(viewutil.MetaFrom(w, rq), lc.Get("ui.users_title"), UserList(lc), map[string]string{}))) -} - -func handlerLock(w http.ResponseWriter, rq *http.Request) { - _, _ = io.WriteString(w, Lock(l18n.FromRequest(rq))) -} - -// handlerRegister displays the register form (GET) or registers the user (POST). -func handlerRegister(w http.ResponseWriter, rq *http.Request) { - lc := l18n.FromRequest(rq) - util.PrepareRq(rq) - if rq.Method == http.MethodGet { - _, _ = io.WriteString( - w, - viewutil.Base( - viewutil.MetaFrom(w, rq), - lc.Get("auth.register_title"), - Register(rq), - map[string]string{}, - ), - ) - 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, - viewutil.Base( - viewutil.MetaFrom(w, rq), - lc.Get("auth.register_title"), - fmt.Sprintf( - `

%s

%s

`, - err.Error(), - lc.Get("auth.try_again"), - ), - map[string]string{}, - ), - ) - 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). -func handlerLogout(w http.ResponseWriter, rq *http.Request) { - if rq.Method == http.MethodGet { - var ( - u = user.FromRequest(rq) - can = u != nil - lc = l18n.FromRequest(rq) - ) - w.Header().Set("Content-Type", "text/html;charset=utf-8") - if can { - log.Println("User", u.Name, "tries to log out") - w.WriteHeader(http.StatusOK) - } else { - log.Println("Unknown user tries to log out") - w.WriteHeader(http.StatusForbidden) - } - _, _ = io.WriteString( - w, - viewutil.Base(viewutil.MetaFrom(w, rq), lc.Get("auth.logout_title"), Logout(can, lc), map[string]string{}), - ) - } else if rq.Method == http.MethodPost { - user.LogoutFromRequest(w, rq) - http.Redirect(w, rq, "/", http.StatusSeeOther) - } -} - -// handlerLogin shows the login form (GET) or logs the user in (POST). -func handlerLogin(w http.ResponseWriter, rq *http.Request) { - lc := l18n.FromRequest(rq) - if rq.Method == http.MethodGet { - w.Header().Set("Content-Type", "text/html;charset=utf-8") - w.WriteHeader(http.StatusOK) - _, _ = io.WriteString( - w, - viewutil.Base( - viewutil.MetaFrom(w, rq), - lc.Get("auth.login_title"), - Login(lc), - map[string]string{}, - ), - ) - } else if rq.Method == http.MethodPost { - var ( - username = util.CanonicalName(rq.PostFormValue("username")) - password = rq.PostFormValue("password") - err = user.LoginDataHTTP(w, username, password) - ) - if err != nil { - w.Header().Set("Content-Type", "text/html;charset=utf-8") - w.WriteHeader(http.StatusInternalServerError) - _, _ = io.WriteString(w, viewutil.Base(viewutil.MetaFrom(w, rq), err.Error(), LoginError(err.Error(), lc), map[string]string{})) - return - } - http.Redirect(w, rq, "/", http.StatusSeeOther) - } -} - -func handlerTelegramLogin(w http.ResponseWriter, rq *http.Request) { - // Note there is no lock here. - lc := l18n.FromRequest(rq) - w.Header().Set("Content-Type", "text/html;charset=utf-8") - rq.ParseForm() - var ( - values = rq.URL.Query() - username = strings.ToLower(values.Get("username")) - seemsValid = user.TelegramAuthParamsAreValid(values) - err = user.Register( - username, - "", // Password matters not - "editor", - "telegram", - false, - ) - ) - // If registering a user via Telegram failed, because a Telegram user with this name - // has already registered, then everything is actually ok! - if user.HasUsername(username) && user.ByName(username).Source == "telegram" { - err = nil - } - - if !seemsValid { - err = errors.New("Wrong parameters") - } - - if err != nil { - log.Printf("Failed to register ‘%s’ using Telegram: %s", username, err.Error()) - w.WriteHeader(http.StatusBadRequest) - _, _ = io.WriteString( - w, - viewutil.Base( - viewutil.MetaFrom(w, rq), - lc.Get("ui.error"), - fmt.Sprintf( - `

%s

%s

%s

`, - lc.Get("auth.error_telegram"), - err.Error(), - lc.Get("auth.go_login"), - ), - map[string]string{}, - ), - ) - return - } - - 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( - w, - viewutil.Base( - viewutil.MetaFrom(w, rq), - "Error", - fmt.Sprintf( - `

%s

%s

%s

`, - lc.Get("auth.error_telegram"), - err.Error(), - lc.Get("auth.go_login"), - ), - map[string]string{}, - ), - ) - return - } - log.Printf("Authorize ‘%s’ from Telegram", username) - http.Redirect(w, rq, "/", http.StatusSeeOther) -} diff --git a/web/web.go b/web/web.go index 3b2c0f6..b4d31e1 100644 --- a/web/web.go +++ b/web/web.go @@ -2,9 +2,16 @@ package web import ( + "errors" + "fmt" + "github.com/bouncepaw/mycorrhiza/l18n" + "github.com/bouncepaw/mycorrhiza/viewutil" "io" + "log" + "mime" "net/http" "net/url" + "strings" "github.com/bouncepaw/mycorrhiza/admin" "github.com/bouncepaw/mycorrhiza/auth" @@ -39,11 +46,25 @@ func Handler() http.Handler { // Public routes. They're always accessible regardless of the user status. misc.InitAssetHandlers(router) - auth.InitAuth(router) + + // Auth + router.HandleFunc("/user-list", handlerUserList) + router.HandleFunc("/lock", handlerLock) + // The check below saves a lot of extra checks and lines of codes in other places in this file. + if cfg.UseAuth { + if cfg.AllowRegistration { + router.HandleFunc("/register", handlerRegister).Methods(http.MethodPost, http.MethodGet) + } + if cfg.TelegramEnabled { + router.HandleFunc("/telegram-login", handlerTelegramLogin) + } + router.HandleFunc("/login", handlerLogin) + router.HandleFunc("/logout", handlerLogout) + } // Wiki routes. They may be locked or restricted. - wikiRouter := router.PathPrefix("").Subrouter() - wikiRouter.Use(func(next http.Handler) http.Handler { + r := router.PathPrefix("").Subrouter() + r.Use(func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, rq *http.Request) { user := user.FromRequest(rq) if !user.ShowLockMaybe(w, rq) { @@ -52,35 +73,37 @@ func Handler() http.Handler { }) }) - initReaders(wikiRouter) - initMutators(wikiRouter) - help.InitHandlers(wikiRouter) - categories.InitHandlers(wikiRouter) - misc.InitHandlers(wikiRouter) + initReaders(r) + initMutators(r) + help.InitHandlers(r) + categories.InitHandlers(r) + misc.InitHandlers(r) hypview.Init() - histweb.InitHandlers(wikiRouter) - interwiki.InitHandlers(wikiRouter) + histweb.InitHandlers(r) + interwiki.InitHandlers(r) // Admin routes if cfg.UseAuth { - adminRouter := wikiRouter.PathPrefix("/admin").Subrouter() + adminRouter := r.PathPrefix("/admin").Subrouter() adminRouter.Use(groupMiddleware("admin")) admin.Init(adminRouter) - settingsRouter := wikiRouter.PathPrefix("/settings").Subrouter() + settingsRouter := r.PathPrefix("/settings").Subrouter() // TODO: check if necessary? //settingsRouter.Use(groupMiddleware("settings")) settings.Init(settingsRouter) } // Index page - wikiRouter.HandleFunc("/", func(w http.ResponseWriter, rq *http.Request) { + r.HandleFunc("/", func(w http.ResponseWriter, rq *http.Request) { // Let's pray it never fails addr, _ := url.Parse("/hypha/" + cfg.HomeHypha) rq.URL = addr handlerHypha(w, rq) }) + initPages() + return router } @@ -100,3 +123,192 @@ func groupMiddleware(group string) func(http.Handler) http.Handler { }) } } + +// Auth +func handlerUserList(w http.ResponseWriter, rq *http.Request) { + lc := l18n.FromRequest(rq) + w.Header().Set("Content-Type", mime.TypeByExtension(".html")) + w.WriteHeader(http.StatusOK) + w.Write([]byte(viewutil.Base(viewutil.MetaFrom(w, rq), lc.Get("ui.users_title"), auth.UserList(lc), map[string]string{}))) +} + +func handlerLock(w http.ResponseWriter, rq *http.Request) { + _, _ = io.WriteString(w, auth.Lock(l18n.FromRequest(rq))) +} + +// handlerRegister displays the register form (GET) or registers the user (POST). +func handlerRegister(w http.ResponseWriter, rq *http.Request) { + lc := l18n.FromRequest(rq) + util.PrepareRq(rq) + if rq.Method == http.MethodGet { + _, _ = io.WriteString( + w, + viewutil.Base( + viewutil.MetaFrom(w, rq), + lc.Get("auth.register_title"), + auth.Register(rq), + map[string]string{}, + ), + ) + 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, + viewutil.Base( + viewutil.MetaFrom(w, rq), + lc.Get("auth.register_title"), + fmt.Sprintf( + `

%s

%s

`, + err.Error(), + lc.Get("auth.try_again"), + ), + map[string]string{}, + ), + ) + 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). +func handlerLogout(w http.ResponseWriter, rq *http.Request) { + if rq.Method == http.MethodGet { + var ( + u = user.FromRequest(rq) + can = u != nil + lc = l18n.FromRequest(rq) + ) + w.Header().Set("Content-Type", "text/html;charset=utf-8") + if can { + log.Println("User", u.Name, "tries to log out") + w.WriteHeader(http.StatusOK) + } else { + log.Println("Unknown user tries to log out") + w.WriteHeader(http.StatusForbidden) + } + _, _ = io.WriteString( + w, + viewutil.Base(viewutil.MetaFrom(w, rq), lc.Get("auth.logout_title"), auth.Logout(can, lc), map[string]string{}), + ) + } else if rq.Method == http.MethodPost { + user.LogoutFromRequest(w, rq) + http.Redirect(w, rq, "/", http.StatusSeeOther) + } +} + +// handlerLogin shows the login form (GET) or logs the user in (POST). +func handlerLogin(w http.ResponseWriter, rq *http.Request) { + lc := l18n.FromRequest(rq) + if rq.Method == http.MethodGet { + w.Header().Set("Content-Type", "text/html;charset=utf-8") + w.WriteHeader(http.StatusOK) + _, _ = io.WriteString( + w, + viewutil.Base( + viewutil.MetaFrom(w, rq), + lc.Get("auth.login_title"), + auth.Login(lc), + map[string]string{}, + ), + ) + } else if rq.Method == http.MethodPost { + var ( + username = util.CanonicalName(rq.PostFormValue("username")) + password = rq.PostFormValue("password") + err = user.LoginDataHTTP(w, username, password) + ) + if err != nil { + w.Header().Set("Content-Type", "text/html;charset=utf-8") + w.WriteHeader(http.StatusInternalServerError) + _, _ = io.WriteString(w, viewutil.Base(viewutil.MetaFrom(w, rq), err.Error(), auth.LoginError(err.Error(), lc), map[string]string{})) + return + } + http.Redirect(w, rq, "/", http.StatusSeeOther) + } +} + +func handlerTelegramLogin(w http.ResponseWriter, rq *http.Request) { + // Note there is no lock here. + lc := l18n.FromRequest(rq) + w.Header().Set("Content-Type", "text/html;charset=utf-8") + rq.ParseForm() + var ( + values = rq.URL.Query() + username = strings.ToLower(values.Get("username")) + seemsValid = user.TelegramAuthParamsAreValid(values) + err = user.Register( + username, + "", // Password matters not + "editor", + "telegram", + false, + ) + ) + // If registering a user via Telegram failed, because a Telegram user with this name + // has already registered, then everything is actually ok! + if user.HasUsername(username) && user.ByName(username).Source == "telegram" { + err = nil + } + + if !seemsValid { + err = errors.New("Wrong parameters") + } + + if err != nil { + log.Printf("Failed to register ‘%s’ using Telegram: %s", username, err.Error()) + w.WriteHeader(http.StatusBadRequest) + _, _ = io.WriteString( + w, + viewutil.Base( + viewutil.MetaFrom(w, rq), + lc.Get("ui.error"), + fmt.Sprintf( + `

%s

%s

%s

`, + lc.Get("auth.error_telegram"), + err.Error(), + lc.Get("auth.go_login"), + ), + map[string]string{}, + ), + ) + return + } + + 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( + w, + viewutil.Base( + viewutil.MetaFrom(w, rq), + "Error", + fmt.Sprintf( + `

%s

%s

%s

`, + lc.Get("auth.error_telegram"), + err.Error(), + lc.Get("auth.go_login"), + ), + map[string]string{}, + ), + ) + return + } + log.Printf("Authorize ‘%s’ from Telegram", username) + http.Redirect(w, rq, "/", http.StatusSeeOther) +}