package middleware import ( "context" "errors" "net/http" "github.com/prabhupadasays/spsays-bff/internal/rbac" "github.com/prabhupadasays/spsays-bff/internal/repository" "github.com/prabhupadasays/spsays-bff/pkg/response" ) type staffCtxKey struct{} // StaffContext is the caller's resolved admin access, set by RequireStaff. type StaffContext struct { RoleKey string RoleName string Perms rbac.Set } // Staff returns the caller's staff context, or nil outside RequireStaff. func Staff(ctx context.Context) *StaffContext { s, _ := ctx.Value(staffCtxKey{}).(*StaffContext) return s } // RequireStaff admits accounts that hold an admin role and aren't blocked from // the panel. It MUST run after RequireAuth. // // It resolves the role's permissions once per request and puts them in // context, so the RequirePermission checks on each route are map lookups // rather than further queries. Being staff gets you through the door only; // every route then names the permission it needs. func RequireStaff(staff *repository.StaffRepo) func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { uid := UserID(r.Context()) if uid == "" { response.Error(w, http.StatusUnauthorized, "not authenticated") return } access, err := staff.Access(r.Context(), uid) if errors.Is(err, repository.ErrNotStaff) { response.Error(w, http.StatusForbidden, "admin access required") return } if err != nil { response.Error(w, http.StatusInternalServerError, "auth check failed") return } if access.Blocked { response.Error(w, http.StatusForbidden, "your admin access has been suspended") return } ctx := context.WithValue(r.Context(), staffCtxKey{}, &StaffContext{ RoleKey: access.RoleKey, RoleName: access.RoleName, Perms: rbac.NewSet(access.Permissions), }) next.ServeHTTP(w, r.WithContext(ctx)) }) } } // RequirePermission gates a route to staff holding perm. Must run inside // RequireStaff; outside it there is no staff context and everything is refused. func RequirePermission(perm string) func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { s := Staff(r.Context()) if s == nil || !s.Perms.Has(perm) { response.Error(w, http.StatusForbidden, "you don't have permission to do that") return } next.ServeHTTP(w, r) }) } }