Files
oslstats/internal/rbac/preview_middleware.go
2026-02-13 20:51:39 +11:00

97 lines
2.5 KiB
Go

package rbac
import (
"context"
"net/http"
"strconv"
"git.haelnorr.com/h/golib/hws"
"git.haelnorr.com/h/oslstats/internal/contexts"
"git.haelnorr.com/h/oslstats/internal/db"
"github.com/pkg/errors"
"github.com/uptrace/bun"
)
// LoadPreviewRoleMiddleware loads the preview role from the session cookie if present
// and adds it to the request context. This must run after authentication but before
// the RBAC cache middleware.
func LoadPreviewRoleMiddleware(s *hws.Server, conn *bun.DB) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Check if there's a preview role in the cookie
roleID := getPreviewRoleCookie(r)
if roleID == 0 {
// No preview role, continue normally
next.ServeHTTP(w, r)
return
}
// Load the preview role from the database
var previewRole *db.Role
if ok := db.WithReadTx(s, w, r, conn, func(ctx context.Context, tx bun.Tx) (bool, error) {
var err error
previewRole, err = db.GetRoleByID(ctx, tx, roleID)
if err != nil {
return false, errors.Wrap(err, "db.GetRoleByID")
}
if previewRole == nil {
// Role doesn't exist anymore, clear the cookie
ClearPreviewRoleCookie(w)
return true, nil
}
return true, nil
}); !ok {
return
}
// If role was found, add it to context
if previewRole != nil {
ctx := contexts.WithPreviewRole(r.Context(), previewRole)
r = r.WithContext(ctx)
}
next.ServeHTTP(w, r)
})
}
}
// SetPreviewRoleCookie sets the preview role ID in a session cookie
func SetPreviewRoleCookie(w http.ResponseWriter, roleID int, ssl bool) {
http.SetCookie(w, &http.Cookie{
Name: "preview_role",
Value: strconv.Itoa(roleID),
Path: "/",
MaxAge: 0, // Session cookie - expires when browser closes or session times out
HttpOnly: true,
Secure: ssl,
SameSite: http.SameSiteLaxMode,
})
}
// getPreviewRoleCookie retrieves the preview role ID from the cookie
// Returns 0 if not present or invalid
func getPreviewRoleCookie(r *http.Request) int {
if r == nil {
return 0
}
cookie, err := r.Cookie("preview_role")
if err != nil {
return 0
}
roleID, err := strconv.Atoi(cookie.Value)
if err != nil {
return 0
}
return roleID
}
// ClearPreviewRoleCookie removes the preview role cookie
func ClearPreviewRoleCookie(w http.ResponseWriter) {
http.SetCookie(w, &http.Cookie{
Name: "preview_role",
Value: "",
Path: "/",
MaxAge: -1,
})
}