// Copyright 2023 wanderer // SPDX-License-Identifier: AGPL-3.0-only package handlers import ( "context" "net/http" "strings" moduser "git.dotya.ml/mirre-mt/pcmt/modules/user" "github.com/CAFxX/httpcompression" "github.com/CAFxX/httpcompression/contrib/andybalholm/brotli" "github.com/CAFxX/httpcompression/contrib/compress/gzip" "github.com/labstack/echo-contrib/session" "github.com/labstack/echo/v4" ) func MiddlewareSession(next echo.HandlerFunc) echo.HandlerFunc { return func(c echo.Context) error { if c.Request().URL.Path == "/logout" && c.Request().Method == "POST" { log.Debug("skipping auth middleware on /logout POST", "module", "handlers/middleware") return next(c) } sess, _ := session.Get(setting.SessionCookieName(), c) if sess == nil { return renderErrorPage( c, http.StatusUnauthorized, http.StatusText(http.StatusUnauthorized)+" you need to log in again", "you need to log in again", ) } if username, ok := sess.Values["username"].(string); ok { log.Info("Refreshing session cookie", "username", username, "module", "middleware", "maxAge", setting.SessionMaxAge(), "secure", setting.HTTPSecure(), "domain", setting.HTTPDomain(), ) refreshSession( sess, "/", setting.SessionMaxAge(), true, setting.HTTPSecure(), http.SameSiteStrictMode, ) sess.Values["username"] = username c.Set("sess", sess) var u moduser.User ctx := context.WithValue(context.Background(), moduser.CtxKey{}, slogger) if usr, err := moduser.QueryUser(ctx, dbclient, username); err == nil && usr != nil { u.ID = usr.ID u.Username = usr.Username u.IsAdmin = usr.IsAdmin u.CreatedAt = usr.CreatedAt u.IsActive = usr.IsActive u.IsLoggedIn = true } else { c.Logger().Error(http.StatusText(http.StatusInternalServerError) + " - " + err.Error()) return renderErrorPage( c, http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError), err.Error(), ) } c.Set("sloggerCtx", ctx) c.Set("sessUsr", u) if err := sess.Save(c.Request(), c.Response()); err != nil { log.Error("Failed to save session", "module", "middleware") return renderErrorPage( c, http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)+" (make sure you've got cookies enabled)", err.Error(), ) } return next(c) } log.Warn("Could not get username from the cookie", "module", "handlers/middleware") if !sess.IsNew { log.Warn("Expired session cookie (without a username) found, redirecting to sign in", "module", "handlers/middleware") sess.Values["info"] = "Log in again, please." if err := sess.Save(c.Request(), c.Response()); err != nil { log.Error("Failed to save session", "module", "middleware") return renderErrorPage( c, http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)+" could not save the session cookie", err.Error(), ) } return c.Redirect(http.StatusTemporaryRedirect, "/signin") } return renderErrorPage( c, http.StatusUnauthorized, http.StatusText(http.StatusUnauthorized), ErrNoSession.Error(), ) } } var cacheExtensions = [2]string{".png", ".svg"} func MiddlewareCache(next echo.HandlerFunc) echo.HandlerFunc { return func(c echo.Context) error { cache := false for _, v := range cacheExtensions { if strings.HasSuffix(c.Request().URL.Path, v) { cache = true break } } if cache { c.Response().Header().Set(echo.HeaderCacheControl, "300") } return next(c) } } func WrapMiddlewareCompress() (echo.MiddlewareFunc, error) { brEnc, err := brotli.New(brotli.Options{}) if err != nil { return nil, err } gzEnc, err := gzip.New(gzip.Options{}) if err != nil { return nil, err } blocklist := true a, _ := httpcompression.Adapter( httpcompression.Compressor(brotli.Encoding, 1, brEnc), httpcompression.Compressor(gzip.Encoding, 0, gzEnc), httpcompression.Prefer(httpcompression.PreferServer), httpcompression.MinSize(100), httpcompression.ContentTypes([]string{ "image/jpeg", "image/gif", "image/png", }, blocklist), ) return echo.WrapMiddleware(a), nil }