diff --git a/handlers/handlers.go b/handlers/handlers.go index 0ef939e..1213fbe 100644 --- a/handlers/handlers.go +++ b/handlers/handlers.go @@ -1,17 +1,12 @@ package handlers import ( - "context" - "errors" "html/template" "io/fs" "net/http" "path/filepath" "strings" - "git.dotya.ml/mirre-mt/pcmt/ent" - moduser "git.dotya.ml/mirre-mt/pcmt/modules/user" - "github.com/gorilla/sessions" "github.com/labstack/echo-contrib/session" "github.com/labstack/echo/v4" "github.com/microcosm-cc/bluemonday" @@ -146,293 +141,6 @@ func Index() echo.HandlerFunc { } } -func Signup() echo.HandlerFunc { - return func(c echo.Context) error { - sess, _ := session.Get(setting.SessionCookieName(), c) - if sess != nil { - log.Info("gorilla session", "endpoint", "signup") - - username := sess.Values["username"] - if username != nil { - return c.Redirect(http.StatusFound, "/home") - } - } - - csrf := c.Get("csrf").(string) - - // secure := c.Request().URL.Scheme == "https" - // cookieCSRF := &http.Cookie{ - // Name: "_csrf", - // Value: csrf, - // // SameSite: http.SameSiteStrictMode, - // SameSite: http.SameSiteLaxMode, - // MaxAge: 3600, - // Secure: secure, - // HttpOnly: true, - // } - // c.SetCookie(cookieCSRF) - - err := c.Render( - http.StatusOK, - "signup.tmpl", - page{ - AppName: setting.AppName(), - AppVer: appver, - Title: "Sign up", - CSRF: csrf, - DevelMode: setting.IsDevel(), - Current: "signup", - }, - ) - if err != nil { - log.Warnf("error: %q", err) - - return renderErrorPage( - c, - http.StatusInternalServerError, - http.StatusText(http.StatusInternalServerError), - err.Error(), - ) - } - - return nil - } -} - -func SignupPost(client *ent.Client) echo.HandlerFunc { - return func(c echo.Context) error { - err := c.Request().ParseForm() - if err != nil { - return err - } - - var username string - - var email string - - uname := c.Request().FormValue("username") - if uname == "" { - c.Logger().Error("signup: username was not set, returning to /signup") - return c.Redirect(http.StatusSeeOther, "/signup") - } - - username = uname - - mail := c.Request().FormValue("email") - if mail == "" { - c.Logger().Error("signup: email not set") - return c.Redirect(http.StatusSeeOther, "/signup") - } - - email = mail - - passwd := c.Request().FormValue("password") - if passwd == "" { - log.Info("signup: password was not set, returning to /signup") - return c.Redirect(http.StatusSeeOther, "/signup") - } - - ctx := context.WithValue(context.Background(), moduser.CtxKey{}, slogger) - - exists, err := moduser.Exists(ctx, client, username, email) - if err != nil { - c.Logger().Error("error checking whether user exists", err) - - return renderErrorPage( - c, - http.StatusInternalServerError, - http.StatusText(http.StatusInternalServerError), - err.Error(), - ) - } - - if exists { - c.Logger().Error("username/email already taken") - - return c.Redirect(http.StatusSeeOther, "/signup") - } - - u, err := moduser.CreateUser( - ctx, - client, - email, - username, - passwd, - ) - if err != nil { - if errors.Is(err, errors.New("username is not unique")) { - c.Logger().Error("username already taken") - // TODO: re-render signup page with a flash message - // stating what went wrong. - return c.Redirect(http.StatusSeeOther, "/signup") - } - - return renderErrorPage( - c, - http.StatusInternalServerError, - http.StatusText(http.StatusInternalServerError)+" - failed to create schema resources", - err.Error(), - ) - } - - log.Infof("successfully registered user '%s'", username) - log.Debug("user details", "id", u.ID, "email", u.Email, "isAdmin", u.IsAdmin) - - secure := c.Request().URL.Scheme == "https" //nolint:goconst - - sess, _ := session.Get(setting.SessionCookieName(), c) - sess.Options = &sessions.Options{ - Path: "/", - MaxAge: 3600, - HttpOnly: true, - Secure: secure, - SameSite: http.SameSiteStrictMode, - } - sess.Values["foo"] = "bar" - sess.Values["username"] = username - - err = sess.Save(c.Request(), c.Response()) - if err != nil { - c.Logger().Error("failed to save session") - - return renderErrorPage( - c, - http.StatusInternalServerError, - http.StatusText(http.StatusInternalServerError)+" (make sure you've got cookies enabled)", - err.Error(), - ) - } - - return c.Redirect(http.StatusMovedPermanently, "/home") - } -} - -func Home(client *ent.Client) echo.HandlerFunc { - return func(c echo.Context) error { - var username string - - sess, _ := session.Get(setting.SessionCookieName(), c) - if sess == nil { - log.Info("no session, redirecting to /signin", "endpoint", "/home") - return c.Redirect(http.StatusPermanentRedirect, "/signin") - } - - if sess.Values["foo"] != nil { - log.Info("gorilla session", "custom field test", sess.Values["foo"].(string)) - } - - uname := sess.Values["username"] - if uname == nil { - log.Info("session cookie found but username invalid, redirecting to signin", "endpoint", "/home") - - return c.Redirect(http.StatusSeeOther, "/signin") - } - - log.Info("gorilla session", "username", sess.Values["username"].(string)) - username = sess.Values["username"].(string) - - // example denial. - // if _, err := c.Cookie("aha"); err != nil { - // log.Printf("error: %q", err) - // return echo.NewHTTPError(http.StatusUnauthorized, http.StatusText(http.StatusUnauthorized)) - // } - - var u moduser.User - - ctx := context.WithValue(context.Background(), moduser.CtxKey{}, slogger) - if usr, err := moduser.QueryUser(ctx, client, username); err == nil && usr != nil { - c.Logger().Debug("got usr: ", usr.Username) - c.Logger().Debug("admin? ", usr.IsAdmin) - - u.ID = usr.ID - u.Username = usr.Username - u.IsActive = usr.IsActive - u.IsLoggedIn = true - } else { - c.Logger().Error("failed to query usr", username) - - return renderErrorPage( - c, - http.StatusInternalServerError, - http.StatusText(http.StatusInternalServerError)+" failed to query usr (make sure you've got cookies enabled)", - err.Error(), - ) - } - - err := c.Render(http.StatusInternalServerError, "home.tmpl", - page{ - AppName: setting.AppName(), - AppVer: appver, - Title: "Home", - Name: username, - DevelMode: setting.IsDevel(), - Current: "home", - User: u, - }, - ) - if err != nil { - c.Logger().Errorf("error: %q", err) - - return renderErrorPage( - c, - http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError), - err.Error(), - ) - } - - return nil - } -} - -func Logout() echo.HandlerFunc { - return func(c echo.Context) error { - switch { - case c.Request().Method == "POST": - sess, _ := session.Get(setting.SessionCookieName(), c) - if sess != nil { - log.Infof("max-age before logout: %d", sess.Options.MaxAge) - sess.Options.MaxAge = -1 - - if username := sess.Values["username"]; username != nil { - sess.Values["username"] = "" - } - - err := sess.Save(c.Request(), c.Response()) - if err != nil { - c.Logger().Error("could not delete session cookie") - } - } - - return c.Redirect(http.StatusMovedPermanently, "/logout") - - case c.Request().Method == "GET": - err := c.Render( - http.StatusOK, - "logout.tmpl", - page{ - AppName: setting.AppName(), - AppVer: appver, - Title: "Logout", - DevelMode: setting.IsDevel(), - Current: "logout", - }, - ) - if err != nil { - c.Logger().Errorf("error: %q", err) - - return renderErrorPage( - c, - http.StatusInternalServerError, - http.StatusText(http.StatusInternalServerError), - err.Error(), - ) - } - } - - return nil - } -} - // experimental global redirect handler? // http.HandleFunc("/redirect", func(w http.ResponseWriter, r *http.Request) { // loc, err := url.QueryUnescape(r.URL.Query().Get("loc")) diff --git a/handlers/home.go b/handlers/home.go new file mode 100644 index 0000000..c9d03f0 --- /dev/null +++ b/handlers/home.go @@ -0,0 +1,88 @@ +package handlers + +import ( + "context" + "net/http" + + "git.dotya.ml/mirre-mt/pcmt/ent" + moduser "git.dotya.ml/mirre-mt/pcmt/modules/user" + "github.com/labstack/echo-contrib/session" + "github.com/labstack/echo/v4" +) + +func Home(client *ent.Client) echo.HandlerFunc { + return func(c echo.Context) error { + var username string + + sess, _ := session.Get(setting.SessionCookieName(), c) + if sess == nil { + log.Info("no session, redirecting to /signin", "endpoint", "/home") + return c.Redirect(http.StatusMovedPermanently, "/signin") + } + + if sess.Values["foo"] != nil { + log.Info("gorilla session", "custom field test", sess.Values["foo"].(string)) + } + + uname := sess.Values["username"] + if uname == nil { + log.Info("session cookie found but username invalid, redirecting to signin", "endpoint", "/home") + + return c.Redirect(http.StatusSeeOther, "/signin") + } + + log.Info("gorilla session", "username", sess.Values["username"].(string)) + username = sess.Values["username"].(string) + + // example denial. + // if _, err := c.Cookie("aha"); err != nil { + // log.Printf("error: %q", err) + // return echo.NewHTTPError(http.StatusUnauthorized, http.StatusText(http.StatusUnauthorized)) + // } + + var u moduser.User + + ctx := context.WithValue(context.Background(), moduser.CtxKey{}, slogger) + if usr, err := moduser.QueryUser(ctx, client, username); err == nil && usr != nil { + c.Logger().Debug("got usr: ", usr.Username) + c.Logger().Debug("admin? ", usr.IsAdmin) + + u.ID = usr.ID + u.Username = usr.Username + u.IsActive = usr.IsActive + u.IsLoggedIn = true + } else { + c.Logger().Error("failed to query usr", username) + + return renderErrorPage( + c, + http.StatusInternalServerError, + http.StatusText(http.StatusInternalServerError)+" failed to query usr (make sure you've got cookies enabled)", + err.Error(), + ) + } + + err := c.Render(http.StatusInternalServerError, "home.tmpl", + page{ + AppName: setting.AppName(), + AppVer: appver, + Title: "Home", + Name: username, + DevelMode: setting.IsDevel(), + Current: "home", + User: u, + }, + ) + if err != nil { + c.Logger().Errorf("error: %q", err) + + return renderErrorPage( + c, + http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError), + err.Error(), + ) + } + + return nil + } +} diff --git a/handlers/logout.go b/handlers/logout.go new file mode 100644 index 0000000..2f919ff --- /dev/null +++ b/handlers/logout.go @@ -0,0 +1,57 @@ +package handlers + +import ( + "net/http" + + "github.com/labstack/echo-contrib/session" + "github.com/labstack/echo/v4" +) + +func Logout() echo.HandlerFunc { + return func(c echo.Context) error { + switch { + case c.Request().Method == "POST": + sess, _ := session.Get(setting.SessionCookieName(), c) + if sess != nil { + log.Infof("max-age before logout: %d", sess.Options.MaxAge) + sess.Options.MaxAge = -1 + + if username := sess.Values["username"]; username != nil { + sess.Values["username"] = "" + } + + err := sess.Save(c.Request(), c.Response()) + if err != nil { + c.Logger().Error("could not delete session cookie") + } + } + + return c.Redirect(http.StatusMovedPermanently, "/logout") + + case c.Request().Method == "GET": + err := c.Render( + http.StatusOK, + "logout.tmpl", + page{ + AppName: setting.AppName(), + AppVer: appver, + Title: "Logout", + DevelMode: setting.IsDevel(), + Current: "logout", + }, + ) + if err != nil { + c.Logger().Errorf("error: %q", err) + + return renderErrorPage( + c, + http.StatusInternalServerError, + http.StatusText(http.StatusInternalServerError), + err.Error(), + ) + } + } + + return nil + } +} diff --git a/handlers/signup.go b/handlers/signup.go new file mode 100644 index 0000000..3a6a85c --- /dev/null +++ b/handlers/signup.go @@ -0,0 +1,176 @@ +package handlers + +import ( + "context" + "errors" + "net/http" + + "git.dotya.ml/mirre-mt/pcmt/ent" + moduser "git.dotya.ml/mirre-mt/pcmt/modules/user" + "github.com/gorilla/sessions" + "github.com/labstack/echo-contrib/session" + "github.com/labstack/echo/v4" +) + +func Signup() echo.HandlerFunc { + return func(c echo.Context) error { + sess, _ := session.Get(setting.SessionCookieName(), c) + if sess != nil { + log.Info("gorilla session", "endpoint", "signup") + + username := sess.Values["username"] + if username != nil { + return c.Redirect(http.StatusFound, "/home") + } + } + + // tpl := getTmpl("signup.tmpl") + + csrf := c.Get("csrf").(string) + + // secure := c.Request().URL.Scheme == "https" + // cookieCSRF := &http.Cookie{ + // Name: "_csrf", + // Value: csrf, + // // SameSite: http.SameSiteStrictMode, + // SameSite: http.SameSiteLaxMode, + // MaxAge: 3600, + // Secure: secure, + // HttpOnly: true, + // } + // c.SetCookie(cookieCSRF) + + err := c.Render( + http.StatusOK, + "signup.tmpl", + page{ + AppName: setting.AppName(), + AppVer: appver, + Title: "Sign up", + CSRF: csrf, + DevelMode: setting.IsDevel(), + Current: "signup", + }, + ) + if err != nil { + log.Warnf("error: %q", err) + + return renderErrorPage( + c, + http.StatusInternalServerError, + http.StatusText(http.StatusInternalServerError), + err.Error(), + ) + } + + return nil + } +} + +func SignupPost(client *ent.Client) echo.HandlerFunc { + return func(c echo.Context) error { + err := c.Request().ParseForm() + if err != nil { + return err + } + + var username string + + var email string + + uname := c.Request().FormValue("username") + if uname == "" { + c.Logger().Error("signup: username was not set, returning to /signup") + return c.Redirect(http.StatusSeeOther, "/signup") + } + + username = uname + + mail := c.Request().FormValue("email") + if mail == "" { + c.Logger().Error("signup: email not set") + return c.Redirect(http.StatusSeeOther, "/signup") + } + + email = mail + + passwd := c.Request().FormValue("password") + if passwd == "" { + log.Info("signup: password was not set, returning to /signup") + return c.Redirect(http.StatusSeeOther, "/signup") + } + + ctx := context.WithValue(context.Background(), moduser.CtxKey{}, slogger) + + exists, err := moduser.Exists(ctx, client, username, email) + if err != nil { + c.Logger().Error("error checking whether user exists", err) + + return renderErrorPage( + c, + http.StatusInternalServerError, + http.StatusText(http.StatusInternalServerError), + err.Error(), + ) + } + + if exists { + c.Logger().Error("username/email already taken") + + return c.Redirect(http.StatusSeeOther, "/signup") + } + + u, err := moduser.CreateUser( + ctx, + client, + email, + username, + passwd, + ) + if err != nil { + if errors.Is(err, errors.New("username is not unique")) { + c.Logger().Error("username already taken") + // TODO: re-render signup page with a flash message + // stating what went wrong. + return c.Redirect(http.StatusSeeOther, "/signup") + } + + return renderErrorPage( + c, + http.StatusInternalServerError, + http.StatusText(http.StatusInternalServerError)+" - failed to create schema resources", + err.Error(), + ) + } + + log.Infof("successfully registered user '%s'", username) + log.Debug("user details", "id", u.ID, "email", u.Email, "isAdmin", u.IsAdmin) + + secure := c.Request().URL.Scheme == "https" //nolint:goconst + + sess, _ := session.Get(setting.SessionCookieName(), c) + sess.Options = &sessions.Options{ + Path: "/", + MaxAge: 3600, + HttpOnly: true, + Secure: secure, + SameSite: http.SameSiteStrictMode, + } + sess.Values["foo"] = "bar" + sess.Values["username"] = username + + err = sess.Save(c.Request(), c.Response()) + if err != nil { + c.Logger().Error("failed to save session") + + return renderErrorPage( + c, + http.StatusInternalServerError, + http.StatusText(http.StatusInternalServerError)+" (make sure you've got cookies enabled)", + err.Error(), + ) + } + + return c.Redirect(http.StatusMovedPermanently, "/home") + } +}