// Copyright 2023 wanderer // SPDX-License-Identifier: AGPL-3.0-only package handlers import ( "context" "errors" "fmt" "net/http" "git.dotya.ml/mirre-mt/pcmt/modules/hibp" moduser "git.dotya.ml/mirre-mt/pcmt/modules/user" "github.com/gorilla/sessions" "github.com/labstack/echo/v4" ) func GetSearchHIBP() echo.HandlerFunc { return func(c echo.Context) error { addHeaders(c) u, ok := c.Get("sessUsr").(moduser.User) if !ok { c.Logger().Warnf("Error getting user from session cookie") } 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", ) } if !u.IsAdmin { ctx := context.WithValue(context.Background(), moduser.CtxKey{}, slogger) f, err := moduser.UsrFinishedSetup(ctx, dbclient, u.ID) if err != nil { return renderErrorPage( c, http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError), err.Error(), ) } if !f { log.Warn("resource access attempt without performing the initial password change", "user", u.Username, "endpoint", "/user/hibp-search") return c.Redirect(http.StatusSeeOther, "/user/initial-password-change") } } csrf := c.Get("csrf").(string) p := newPage() if sess, ok := c.Get("sess").(*sessions.Session); ok { if flash := sess.Values["flash"]; flash != nil { p.Data["flash"] = flash.(string) delete(sess.Values, "flash") _ = sess.Save(c.Request(), c.Response()) } } p.Title = "Search HIBP" p.Current = "hibp-search" p.CSRF = csrf p.User = u err := c.Render( http.StatusOK, "user/hibp-search.tmpl", p, ) if err != nil { c.Logger().Errorf("error: %q", err) return renderErrorPage( c, http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError), err.Error(), ) } return nil } } func SearchHIBP() 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", // ) c.Logger().Warnf("Error getting user from session cookie") } 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", ) } if !u.IsAdmin { ctx := context.WithValue(context.Background(), moduser.CtxKey{}, slogger) f, err := moduser.UsrFinishedSetup(ctx, dbclient, u.ID) if err != nil { return renderErrorPage( c, http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError), err.Error(), ) } if !f { return renderErrorPage( c, http.StatusUnauthorized, http.StatusText(http.StatusUnauthorized)+" - you need to perform your initial password change before accessing this resource", "user never changed password", ) } } csrf := c.Get("csrf").(string) a := new(hibpSearch) if err := c.Bind(a); err != nil { return renderErrorPage( c, http.StatusBadRequest, http.StatusText(http.StatusBadRequest), err.Error(), ) } if err := c.Validate(a); err != nil { return renderErrorPage( c, http.StatusBadRequest, http.StatusText(http.StatusBadRequest)+" - "+ErrValidationFailed.Error(), err.Error(), ) } breachNames, err := hibp.GetAllBreachesForAccount(a.Account) if err != nil { msg := "Error getting breaches for this account" status := http.StatusInternalServerError switch { case errors.Is(err, hibp.ErrRateLimited): msg = http.StatusText(http.StatusTooManyRequests) + " - we have been rate limited from the API. Try again in a while." status = http.StatusTooManyRequests case errors.Is(err, hibp.ErrAuthKeyCheckValue): msg = hibp.ErrAuthKeyCheckValue.Error() status = http.StatusBadRequest } return renderErrorPage( c, status, msg, err.Error(), ) } if len(breachNames) == 0 { if sess, ok := c.Get("sess").(*sessions.Session); ok { sess.Values["flash"] = fmt.Sprintf("There is no mention of account %q in any of the HIBP breaches!", a.Account) _ = sess.Save(c.Request(), c.Response()) } return c.Redirect(http.StatusSeeOther, "/user/hibp-search") } sfx := "breach" bCount := len(breachNames) if bCount > 1 { sfx = "breaches" } p := newPage() p.Title = "Search HIBP" p.Current = "hibp-search" p.CSRF = csrf p.User = u p.Data["breachNames"] = breachNames p.Data["flash"] = fmt.Sprintf( "The account %q was found in %d %s!", a.Account, bCount, sfx, ) err = c.Render( http.StatusOK, "user/hibp-search.tmpl", p, ) if err != nil { c.Logger().Errorf("error: %q", err) return renderErrorPage( c, http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError), err.Error(), ) } return nil } }