Summary
After the group-only authorization migration (#907 via #912 backend + #914 frontend), the frontend canAccess() in frontend/src/permissions.ts is a stub that ignores its action/resource arguments and returns isAdmin() for every gate:
export function canAccess(action: string, resource: string): boolean {
void action; void resource;
// ... until /me/permissions exists, only admins pass
return isAdmin();
}
The backend now computes real per-group effective permissions (union of the user's groups' permission sets), but the frontend has no way to read them, so every non-admin user is locked out of the entire gated UI — a Standard Users member who could previously view/create/execute now sees all gated buttons and sections hidden.
This is safe (the backend still enforces authorization, so it fails closed), but it is a real functional/UX regression: the group model is effectively admin-or-nothing in the UI until this endpoint lands.
Surfaced by the post-merge security review of #912/#914 (2026-06-02).
Scope
Backend: add a public-to-the-authenticated-user endpoint, e.g. GET /api/auth/me/permissions, returning the current user's effective permission set (the {action, resource} pairs from the union of their groups), derived from the same GetUserPermissions/ComputeEffectivePermissions path the server uses for enforcement. No role anything.
Frontend: fetch that set on login/bootstrap, cache it on the current-user state, and make canAccess(action, resource) consult it (admins keep the {admin, *} wildcard). Remove the interim return isAdmin() stub and the void action; void resource;.
Acceptance
- A
Standard Users member sees exactly the actions their groups grant (not empty, not admin).
- Admins unchanged (wildcard).
- The endpoint and the FE gate derive from the identical permission source the backend enforces with (no drift).
- Tests: BE endpoint returns the union for a multi-group user; FE
canAccess tests updated off the "non-admins always false" pin.
Context
Backend permission source: internal/auth/service_group.go (GetUserPermissions), ComputeEffectivePermissions. Frontend gate: frontend/src/permissions.ts (canAccess, isAdmin).
Summary
After the group-only authorization migration (#907 via #912 backend + #914 frontend), the frontend
canAccess()infrontend/src/permissions.tsis a stub that ignores itsaction/resourcearguments and returnsisAdmin()for every gate:The backend now computes real per-group effective permissions (union of the user's groups' permission sets), but the frontend has no way to read them, so every non-admin user is locked out of the entire gated UI — a
Standard Usersmember who could previously view/create/execute now sees all gated buttons and sections hidden.This is safe (the backend still enforces authorization, so it fails closed), but it is a real functional/UX regression: the group model is effectively admin-or-nothing in the UI until this endpoint lands.
Surfaced by the post-merge security review of #912/#914 (2026-06-02).
Scope
Backend: add a public-to-the-authenticated-user endpoint, e.g.
GET /api/auth/me/permissions, returning the current user's effective permission set (the{action, resource}pairs from the union of their groups), derived from the sameGetUserPermissions/ComputeEffectivePermissionspath the server uses for enforcement. No role anything.Frontend: fetch that set on login/bootstrap, cache it on the current-user state, and make
canAccess(action, resource)consult it (admins keep the{admin, *}wildcard). Remove the interimreturn isAdmin()stub and thevoid action; void resource;.Acceptance
Standard Usersmember sees exactly the actions their groups grant (not empty, not admin).canAccesstests updated off the "non-admins always false" pin.Context
Backend permission source:
internal/auth/service_group.go(GetUserPermissions),ComputeEffectivePermissions. Frontend gate:frontend/src/permissions.ts(canAccess,isAdmin).