From a084425e12809dc0c3bd381fa40a4cdcc2b570f5 Mon Sep 17 00:00:00 2001 From: Ahmad Al Tamimi Date: Fri, 21 Aug 2026 17:49:05 +0400 Subject: [PATCH] docs: document the permission-gating contract on hasPermission GET /rbac/my/permissions returns role-derived grants only, so the backend's is_admin bypass is invisible to client-side gates. Record the gap, why it fails closed, and what to inspect first. Signed-off-by: Ahmad Al Tamimi --- src/auth/AuthContext.tsx | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/auth/AuthContext.tsx b/src/auth/AuthContext.tsx index 75a8667..cf7d1ce 100644 --- a/src/auth/AuthContext.tsx +++ b/src/auth/AuthContext.tsx @@ -68,6 +68,17 @@ interface AuthContextValue extends AuthState { * Presentational gate: true when the caller holds `perm` (or the `*` wildcard). * NOT a security boundary — endpoints enforce RBAC server-side. Fails closed * on error and on an empty permission set. + * + * Contract caveat: GET /rbac/my/permissions returns role-derived grants only; + * the backend's is_admin bypass (allow_admin_bypass in permission_service) is + * not reflected here. A DB is_admin user without the wildcard platform_admin + * role therefore fails these gates even though the API would serve the data. + * Bootstrap keeps role and flag in sync, so the gap only appears when that + * sync is bypassed (direct DB promotion, an SSO admin mapping, a failed + * bootstrap role assignment). It fails closed and is invisible to the + * 403-based PermissionDenied layer, since a fetch-gated card never fires the + * request. If a card is unexpectedly hidden for an admin, inspect + * GET /rbac/my/permissions before suspecting the gate. */ hasPermission: (perm: string) => boolean; }