Skip to main content
Version: main 🚧

Security Review Checklist: Enterprise RBAC (098)

Purpose: Repeatable verification of default-deny, fail-closed behavior, delegation boundaries, and bypass testing.
Evidence: Code paths in ui/src/lib/api-middleware.ts, ui/src/lib/rbac/keycloak-authz.ts, ui/src/lib/rbac/cel-evaluator.ts, and related BFF routes.

1. Default deny (FR-002)​

#CheckExpectedEvidence
1.1Call a BFF route protected by requireRbacPermission with no accessToken on session401 Authentication required; audit DENY_NO_TOKENrequireRbacPermission: early deny when no token
1.2Authenticated user lacks Keycloak permission and role fallback does not apply403 with denial payload; audit DENY_NO_CAPABILITYrequireRbacPermission after checkPermission
1.3Empty / missing CEL_RBAC_EXPRESSIONS entry for a resource#scopeCEL layer skipped (only configured keys run)parseCelRbacExpressions() + conditional evalCel
1.4CEL expression configured but evaluation throws403 Policy denied (CEL); evaluator returns false on errorcel-evaluator.ts catch β†’ false; middleware DENY_CEL

2. Fail-closed when Keycloak PDP is unavailable​

#CheckExpectedEvidence
2.1Simulate Keycloak token endpoint unreachable (network drop) for UMA decisioncheckPermission returns DENY_PDP_UNAVAILABLEkeycloak-authz.ts catch block
2.2Same scenario inside requireRbacPermission503 "Authorization service unavailable β€” access denied (fail-closed)"; no role fallback for PDP unavailableapi-middleware.ts: branch on result.reason === 'DENY_PDP_UNAVAILABLE'
2.3Keycloak returns HTTP 403 for permissionTreated as normal deny β†’ 403 (or fallback if role matches), not 503checkPermission maps 403 to DENY_NO_CAPABILITY

Note: Role fallback (realm role minimum for admin_ui, supervisor, rag) applies when PDP returns denial for other reasonsβ€”operators must not rely on fallback to compensate for a down PDP; the code explicitly fails closed for DENY_PDP_UNAVAILABLE.

3. Fail-closed when Agent Gateway is unavailable​

#CheckExpected
3.1MCP client cannot reach AGConnection failure; no alternate unauthenticated path through AG
3.2Invalid / wrong-audience JWTAG jwtAuth strict mode rejects request

(AG behavior is upstream; align monitoring with FR-013.)

4. No privilege escalation in OBO / delegation chain (FR-019)​

#CheckExpected
4.1Slack bot obtains OBO token for user AEffective tool/MCP permissions must reflect user A entitlements, not bot service account breadth
4.2Bearer JWT path in getAuthFromBearerOrSessionComment in code: "Bearer users get 'user' role by default; admin escalation is session-only" β€” direct API Bearer calls do not gain UI bootstrap/Mongo admin by default
4.3ASP + RBACIf enterprise RBAC allows but ASP denies tool invocation β†’ deny (deny-wins)

Code reference (Bearer path):

    const identity = await validateBearerJWT(token);
// Bearer users get 'user' role by default; admin escalation is session-only
const user = { email: identity.email, name: identity.name, role: 'user' };
return { user, session: { role: 'user' } };

5. RBAC bypass scenarios to test​

#ScenarioPass criteria
5.1Direct RAG server call without BFFRAG must enforce JWT + per-KB / team rules (defense in depth per FR-026/FR-027)
5.2MCP call bypassing Agent GatewayNot supported in target architecture; verify nothing in prod routes MCP around AG
5.3UI Admin routes without requireRbacPermission / admin checksCode review: each admin route should use middleware or explicit check
5.4ALLOW_DEV_ADMIN_WHEN_SSO_DISABLED in productionMust be false / unset in prod (config.ts)
5.5Stale session after role revocationUser’s next permission check uses current token; cache TTL RBAC_CACHE_TTL_SECONDS (allows brief stalenessβ€”document for security reviews)
5.6Cross-tenant header x_tenant_id vs jwt.orgAG CEL denies mismatch when both present (config.yaml)

6. MongoDB and admin elevation​

#CheckExpected
6.1users.metadata.role === 'admin'getAuthenticatedUser can promote to admin after OIDC session checksβ€”ensure only trusted admins can write this field
6.2MongoDB unreachable during admin checkLog warning; user may remain non-admin (does not grant access)

Sign-off​

RoleNameDateResult
Engineer
Security