package middleware import ( "context" "net/http" "net/url" "time" "code.nonshy.com/nonshy/website/pkg/config" "code.nonshy.com/nonshy/website/pkg/controller/photo" "code.nonshy.com/nonshy/website/pkg/log" "code.nonshy.com/nonshy/website/pkg/models" "code.nonshy.com/nonshy/website/pkg/session" "code.nonshy.com/nonshy/website/pkg/templates" ) // LoginRequired middleware. func LoginRequired(handler http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // User must be logged in. user, err := session.CurrentUser(r) if err != nil { log.Error("LoginRequired: %s", err) session.FlashError(w, r, "You must be signed in to view this page.") templates.Redirect(w, "/login?next="+url.QueryEscape(r.URL.String())) return } // Are they banned? if user.Status == models.UserStatusBanned { session.LogoutUser(w, r) session.FlashError(w, r, "Your account has been banned and you are now logged out.") templates.Redirect(w, "/") return } // Is their account disabled? if DisabledAccount(user, w, r) { return } // Is the site under a Maintenance Mode restriction? if MaintenanceMode(user, w, r) { return } // Ping LastLoginAt for long lived sessions, but not if impersonated. var pingLastLoginAt bool if time.Since(user.LastLoginAt) > config.LastLoginAtCooldown && !session.Impersonated(r) { pingLastLoginAt = true if err := user.PingLastLoginAt(); err != nil { log.Error("LoginRequired: couldn't refresh LastLoginAt for user %s: %s", user.Username, err) } } // Log the last visit of their current IP address. if err := models.PingIPAddress(r, user, pingLastLoginAt); err != nil { log.Error("LoginRequired: couldn't ping user %s IP address: %s", user.Username, err) } // Ask the user for their birthdate? if AgeGate(user, w, r) { return } // Stick the CurrentUser in the request context so future calls to session.CurrentUser can read it. ctx := context.WithValue(r.Context(), session.CurrentUserKey, user) handler.ServeHTTP(w, r.WithContext(ctx)) }) } // AdminRequired middleware. func AdminRequired(scope string, handler http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // User must be logged in. currentUser, err := session.CurrentUser(r) if err != nil { log.Error("AdminRequired: %s", err) session.FlashError(w, r, "You must be signed in to view this page.") templates.Redirect(w, "/login?next="+url.QueryEscape(r.URL.String())) return } // Stick the CurrentUser in the request context so future calls to session.CurrentUser can read it. ctx := context.WithValue(r.Context(), session.CurrentUserKey, currentUser) // Admin required. if !currentUser.IsAdmin { errhandler := templates.MakeErrorPage("Admin Required", "You do not have permission for this page.", http.StatusForbidden) errhandler.ServeHTTP(w, r.WithContext(ctx)) return } // Ensure the admin scope. if scope != "" && !currentUser.HasAdminScope(scope) { errhandler := templates.MakeErrorPage( "Admin Scope Required", "Missing required admin scope: "+scope, http.StatusForbidden, ) errhandler.ServeHTTP(w, r.WithContext(ctx)) return } handler.ServeHTTP(w, r.WithContext(ctx)) }) } // CertRequired middleware: like LoginRequired but user must also have their verification pic certified. func CertRequired(handler http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // User must be logged in. currentUser, err := session.CurrentUser(r) if err != nil { log.Error("LoginRequired: %s", err) session.FlashError(w, r, "You must be signed in to view this page.") templates.Redirect(w, "/login?next="+url.QueryEscape(r.URL.String())) return } // Log the last visit of their current IP address. if err := models.PingIPAddress(r, currentUser, false); err != nil { log.Error("CertRequired: couldn't ping user %s IP address: %s", currentUser.Username, err) } // Are they banned? if currentUser.Status == models.UserStatusBanned { session.LogoutUser(w, r) session.FlashError(w, r, "Your account has been banned and you are now logged out.") templates.Redirect(w, "/") return } // Is their account disabled? if DisabledAccount(currentUser, w, r) { return } // Is the site under a Maintenance Mode restriction? if MaintenanceMode(currentUser, w, r) { return } // User must be certified. if !currentUser.Certified || currentUser.ProfilePhoto.ID == 0 { log.Error("CertRequired: user is not certified") photo.CertificationRequiredError().ServeHTTP(w, r) return } // Ask the user for their birthdate? if AgeGate(currentUser, w, r) { return } handler.ServeHTTP(w, r) }) }