// Copyright 2023 wanderer // SPDX-License-Identifier: AGPL-3.0-only package handlers import ( "context" "errors" "net/http" moduser "git.dotya.ml/mirre-mt/pcmt/modules/user" "github.com/gorilla/sessions" "github.com/labstack/echo/v4" ) func InitialPasswordChangePost() echo.HandlerFunc { return func(c echo.Context) error { addHeaders(c) u, ok := c.Get("sessUsr").(moduser.User) if !ok { return renderErrorPage( c, http.StatusUnauthorized, http.StatusText(http.StatusUnauthorized)+", perhaps you need to log in first?", "Username was nil", ) } else if u.IsAdmin { status := http.StatusUnauthorized msg := http.StatusText(status) return renderErrorPage( c, status, msg+": You should not be here", "This endpoint is for users only", ) } ctx, ok := c.Get("sloggerCtx").(context.Context) if !ok { ctx = context.WithValue(context.Background(), moduser.CtxKey{}, slogger) } f, err := moduser.UsrFinishedSetup(ctx, dbclient, u.ID) switch { case err != nil: return renderErrorPage( c, http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError), err.Error(), ) case f: return c.Redirect(http.StatusSeeOther, "/user/init-password-change") } pw := new(initPasswordChange) if err := c.Bind(pw); err != nil { return renderErrorPage( c, http.StatusBadRequest, http.StatusText(http.StatusBadRequest), err.Error(), ) } if pw.NewPassword != pw.RepeatNewPassword { return renderErrorPage( c, http.StatusBadRequest, http.StatusText(http.StatusBadRequest)+" - the passwords were not the same", err.Error(), ) } if err := c.Validate(pw); err != nil { return renderErrorPage( c, http.StatusBadRequest, http.StatusText(http.StatusBadRequest)+" - "+ErrValidationFailed.Error(), err.Error(), ) } err = moduser.ChangePassFirstLogin(ctx, dbclient, u.ID, pw.NewPassword) if err != nil { c.Logger().Errorf("error changing initial user password: %q", err) switch { case errors.Is(err, moduser.ErrPasswordEmpty): return renderErrorPage( c, http.StatusBadRequest, http.StatusText(http.StatusBadRequest), err.Error(), ) case errors.Is(err, moduser.ErrNewPasswordCannotEqual): return renderErrorPage( c, http.StatusBadRequest, http.StatusText(http.StatusBadRequest)+" - the new password needs to be different from the original", err.Error(), ) } return renderErrorPage( c, http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError), err.Error(), ) } log.Info("successfully performed initial password change", "user", u.Username) if sess, ok := c.Get("sess").(*sessions.Session); ok { sess.Values["reauthFlash"] = "Successfully updated your password, log in again, please" if err = sess.Save(c.Request(), c.Response()); err != nil { return renderErrorPage( c, http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)+" - could not change the session cookie", err.Error(), ) } } return c.Redirect(http.StatusSeeOther, "/signin?reauth=true") } } func InitialPasswordChange() echo.HandlerFunc { return func(c echo.Context) error { addHeaders(c) u, ok := c.Get("sessUsr").(moduser.User) if !ok { return renderErrorPage( c, http.StatusUnauthorized, http.StatusText(http.StatusUnauthorized)+", perhaps you need to log in first?", "Username was nil", ) } else if u.IsAdmin { status := http.StatusUnauthorized msg := http.StatusText(status) return renderErrorPage( c, status, msg+": You should not be here", "This endpoint is for users only", ) } ctx, ok := c.Get("sloggerCtx").(context.Context) if !ok { ctx = context.WithValue(context.Background(), moduser.CtxKey{}, slogger) } f, err := moduser.UsrFinishedSetup(ctx, dbclient, u.ID) switch { case err != nil: return renderErrorPage( c, http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError), err.Error(), ) case f: return c.Redirect(http.StatusSeeOther, "/home") } csrf := c.Get("csrf").(string) p := newPage() p.Title = "Initial password change" p.Current = "init-password-change" p.CSRF = csrf p.User = u p.Name = u.Username err = c.Render( http.StatusOK, "user/init-password-change.tmpl", p, ) if err != nil { c.Logger().Errorf("error: %q", err) return renderErrorPage( c, http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError), err.Error(), ) } return nil } }