package rbac import ( "context" "net/http" "time" "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/pkg/contexts" "github.com/pkg/errors" ) // LoadPermissionsMiddleware loads user permissions into context after authentication // MUST run AFTER auth.Authenticate() middleware func (c *Checker) LoadPermissionsMiddleware() hws.Middleware { 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 { // No authenticated user - continue without permissions next.ServeHTTP(w, r) return } // Start transaction for loading permissions ctx, cancel := context.WithTimeout(r.Context(), 15*time.Second) defer cancel() tx, err := c.conn.BeginTx(ctx, nil) if err != nil { // Log but don't block - permission checks will fail gracefully c.s.LogError(hws.HWSError{ Message: "Failed to start database transaction", Error: errors.Wrap(err, "c.conn.BeginTx"), Level: hws.ErrorERROR, }) next.ServeHTTP(w, r) return } defer func() { _ = tx.Rollback() }() // Load user's roles_ and permissions roles_, err := user.GetRoles(ctx, tx) if err != nil { c.s.LogError(hws.HWSError{ Message: "Failed to get user roles", Error: errors.Wrap(err, "user.GetRoles"), Level: hws.ErrorERROR, }) next.ServeHTTP(w, r) return } perms, err := user.GetPermissions(ctx, tx) if err != nil { c.s.LogError(hws.HWSError{ Message: "Failed to get user permissions", Error: errors.Wrap(err, "user.GetPermissions"), Level: hws.ErrorERROR, }) next.ServeHTTP(w, r) return } _ = tx.Commit() // read only transaction // Build permission cache cache := &contexts.PermissionCache{ Permissions: make(map[permissions.Permission]bool), Roles: make(map[roles.Role]bool), } // Check for wildcard permission hasWildcard := false for _, perm := range perms { cache.Permissions[perm.Name] = true if perm.Name == permissions.Wildcard { hasWildcard = true } } cache.HasWildcard = hasWildcard for _, role := range roles_ { cache.Roles[role.Name] = true } // Add cache to context (type-safe) ctx = context.WithValue(ctx, contexts.PermissionCacheKey, cache) next.ServeHTTP(w, r.WithContext(ctx)) }) } }