Skip to content

feat(rbac): granular roles — admin, editor, viewer - #11

Merged
dfradehubs merged 4 commits into
mainfrom
feat/rbac-roles
Jul 24, 2026
Merged

dfradehubs merged 4 commits into
mainfrom
feat/rbac-roles

Conversation

@dfradehubs

Copy link
Copy Markdown
Owner

Summary

Replaces the binary admin/non-admin gate on /admin with hierarchical roles: admin > editor > viewer > user.

  • editor: view, create and edit Agents, MCP servers and Skills. Cannot delete them, cannot manage their permissions, and cannot access Observability, Audit, Settings, Users, LLM/Providers or Groups.
  • viewer: no admin-panel access today (role reserved for future read-only use).
  • admin: unchanged — full access.

Backend

  • models: role constants + HasRole/NormalizeRole/IsValidRole (hierarchy: admin 3 > editor 2 > viewer 1 > user 0).
  • config: editor_groups / viewer_groups (Google Workspace groups, like admin_groups).
  • UserService.ResolveRole(email, groups): effective role = highest of config-granted (admin users/groups, editor/viewer groups) and the DB role.
  • middleware.RequireRole(min): hierarchical gate (replaces the binary admin middleware on /admin).
  • routes: /admin split into two zones —
    • editor zone: GET/POST/PUT of agents, MCP servers, skills.
    • admin-only zone: DELETE + permissions of those, plus settings, groups, audit, observability/metrics, LLM, providers, users, integrations.
  • /api/me now returns the resolved role; PUT /admin/users/{email}/role accepts editor/viewer.

Web

  • UserContext exposes role; header link and admin layout allow editors in.
  • AdminNav shows only Agents / MCP Servers / Skills to editors.
  • Delete buttons hidden for non-admins on the agents/mcp/skills pages.

Assigning roles

Via config (editor_groups/viewer_groups) or per-user in Admin → Users.

Test plan

  • Unit: HasRole/NormalizeRole/IsValidRole and ResolveRole (config-admin/editor/viewer, DB role, highest-wins, case-insensitive) — green.
  • go build, full go test ./..., web npm run lint — green.
  • Dev: /api/me returns role; admin panel still reachable (auth disabled → anonymous resolves to admin).
  • E2E with a real editor account requires auth (Keycloak) — not exercisable in the auth-disabled dev stack. Verify in a real environment: an editor can create/edit but not delete agents/mcp/skills and gets 403 on audit/settings/users; nav hides those sections.

Notes

  • Backend enforces every rule (UI hiding is defense-in-depth). Editors hitting an admin-only endpoint or URL get 403.
  • The /admin/agents page also hosts the Groups tab; Groups endpoints are admin-only, so an editor reaching /admin/groups by URL gets 403 (and it's absent from their nav).

🤖 Generated with Claude Code

Move admin authorization from a binary admin/non-admin gate to hierarchical
roles (admin > editor > viewer > user).

- editor: view, create and edit agents, MCP servers and skills — cannot delete
  them, cannot manage permissions, and cannot access observability, audit,
  settings, users, LLM/providers or groups.
- viewer: no admin-panel access today (reserved).
- admin: unchanged (full access).

Backend:
- models: role constants + HasRole/NormalizeRole/IsValidRole hierarchy
- config: editor_groups / viewer_groups
- UserService.ResolveRole: highest of config (admin/editor/viewer groups) and DB role
- middleware.RequireRole: hierarchical role gate
- routes: /admin split into an editor zone (agents/mcp/skills read+create+edit)
  and an admin-only zone (delete + permissions of those, plus every other section)
- /api/me now returns the resolved role; UpdateRole accepts editor/viewer
- tests for HasRole/NormalizeRole/IsValidRole and ResolveRole

Web:
- UserContext exposes role; header + admin layout allow editors in
- AdminNav shows only Agents/MCP/Skills to editors
- delete buttons hidden for non-admins on agents/mcp/skills
Replace the binary admin toggle with a dropdown to assign any of the four roles
per user. Protected users (role set by config) still show 'Protected'.
…anup

- admin layout redirects editors away from admin-only sections reached by
  direct URL (editors only reach /admin/agents, /admin/mcp, /admin/skills)
- add RoleGate middleware tests (401 no claims, 403 insufficient, pass on met)
- remove dead middleware/admin.go (superseded by RequireRole)
- document editor_groups/viewer_groups in configs/config.yaml
- type UserContext role as UserRole instead of bare string
The useUser hook re-exported a subset of the context and omitted role, so
components reading useUser().role (admin layout, header) failed the production
typecheck. npm run build now passes.
@dfradehubs
dfradehubs merged commit a18701b into main Jul 24, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant