package rbac import ( "context" "net/http" "git.haelnorr.com/h/golib/cookies" "git.haelnorr.com/h/golib/hws" "git.haelnorr.com/h/oslstats/internal/db" "git.haelnorr.com/h/oslstats/internal/permissions" "git.haelnorr.com/h/oslstats/internal/roles" "git.haelnorr.com/h/oslstats/internal/throw" "github.com/pkg/errors" "github.com/uptrace/bun" ) // RequirePermission creates middleware that requires a specific permission func (c *Checker) RequirePermission(s *hws.Server, permission permissions.Permission) func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { user := db.CurrentUser(r.Context()) if user == nil { // Not logged in - redirect to login with page_from cookies.SetPageFrom(w, r, r.URL.Path) http.Redirect(w, r, "/login", http.StatusSeeOther) return } has, err := c.UserHasPermission(r.Context(), user, permission) if err != nil { throw.InternalServiceError(s, w, r, "Permission check failed", errors.Wrap(err, "c.UserHasPermission")) return } if !has { throw.Forbidden(s, w, r, "You don't have permission to access this resource", errors.New("invalid permissions")) return } next.ServeHTTP(w, r) }) } } // RequireRole creates middleware that requires a specific role func (c *Checker) RequireRole(s *hws.Server, role roles.Role) func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { user := db.CurrentUser(r.Context()) if user == nil { // Not logged in - redirect to login cookies.SetPageFrom(w, r, r.URL.Path) http.Redirect(w, r, "/login", http.StatusSeeOther) return } has, err := c.UserHasRole(r.Context(), user, role) if err != nil { throw.InternalServiceError(s, w, r, "Role check failed", errors.Wrap(err, "c.UserHasRole")) return } if !has { throw.Forbidden(s, w, r, "You don't have the required role to access this resource", errors.New("missing role")) return } next.ServeHTTP(w, r) }) } } // RequireAdmin is a convenience middleware for admin-only routes func (c *Checker) RequireAdmin(server *hws.Server) func(http.Handler) http.Handler { return c.RequireRole(server, roles.Admin) } // RequireActualAdmin checks if the user's ACTUAL role is admin, ignoring preview mode // This is used for critical operations like stopping preview mode func (c *Checker) RequireActualAdmin(s *hws.Server) func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { user := db.CurrentUser(r.Context()) if user == nil { // Not logged in - redirect to login cookies.SetPageFrom(w, r, r.URL.Path) http.Redirect(w, r, "/login", http.StatusSeeOther) return } // Check user's ACTUAL role in database, bypassing preview mode var hasAdmin bool if ok := c.conn.WithReadTx(s, w, r, func(ctx context.Context, tx bun.Tx) (bool, error) { var err error hasAdmin, err = user.HasRole(ctx, tx, roles.Admin) if err != nil { return false, errors.Wrap(err, "user.HasRole") } return true, nil }); !ok { return } if !hasAdmin { throw.Forbidden(s, w, r, "You don't have the required role to access this resource", errors.New("missing admin role")) return } next.ServeHTTP(w, r) }) } }