PAR-185
Enforce RBAC server-side at the REST API + service layer
Child of PAR-182.
Today the backend authorizes nothing — security/ActorContext.java and api/ApiAuthFilter.java explicitly defer policy to the UI, so any holder of a valid workspace API key (or the static service key) can perform every admin write, demote an owner, or remove the last owner.
Fix: enforce permissions server-side, keyed off the resolved actor's role in the active tenant (TenantContext + the new role model).
- Resolve the acting member's permissions from
tenant_member.role_id(after the role-model migration). - Add a permission check at the service layer (
WorkspaceAdminService, task mutations, releases, integrations) — e.g.requirePermission(MEMBER_MANAGE)beforesetMemberRole/removeMember;ISSUE_DELETEbefore delete;SETTINGS_MANAGEbefore status/label/role edits. - Last-owner / owner-immutability guard in
setMemberRole/removeMember. - Distinguish the static service key (full trust, internal) from per-workspace keys (subject to RBAC).
Acceptance: a viewer/contributor key cannot perform manage-level writes; a non-owner cannot demote/remove an owner; the last owner cannot be removed. Service-layer tests per permission.
Resources
Comments
tesks · Jun 15, 2026, 8:04 PM
Partial — admin surface done on develop (commit 25680a9). PermissionService resolves the actor's grants from ActorContext + TenantContext; require(...) throws ForbiddenException → 403. Enforced across WorkspaceAdminService: member.manage on add/set-role/remove member, settings.manage on labels/teams/projects/statuses. Owner protections: only an owner can grant the owner role or modify/remove another owner; last owner can't be demoted/removed. 7 RBAC tests added.
Remaining for this issue:
- Enforce on the other controllers/services:
IssueApiController(issue.create/update/delete),ReleaseApiController(release.manage),IntegrationApiController+ GitHub repo-link config (settings.manage),VoteService(stays open to any signed-in user by design). - Decide how the global Keycloak
staffrole interacts: backend now denies non-members, so a staff user who isn't a workspace member will get 403 on managed writes (previously the UI let staff act on any workspace). Either add staff as members or add an explicit backend break-glass — coordinate with PAR-187.
tesks · Jun 15, 2026, 8:22 PM
Done on develop (commit c7d4aa0). Enforcement now spans the whole REST surface: IssueApiController (issue.create/update/comment; delete → soft-archive on issue.delete), ReleaseApiController + IntegrationApiController (settings.manage), in addition to the admin surface from 25680a9. Staff decision implemented: a forwarded X-Actor-Staff actor resolves to admin-equivalent permissions in any workspace (logged), minus the owner-only powers (issue.destroy / workspace.manage). 108 tests green.
tesks · Jun 16, 2026, 8:10 PM
Verified LIVE. perms.require(...) enforced across every mutating surface — WorkspaceAdminService (member/role/settings/team/project/state/sprint), IssueApiController (create/update/move/reorder/delete→archive/comment), ReleaseApiController (cut/addIssue/removeIssue/freeze → settings.manage), IntegrationApiController (guild/keys → settings.manage), WorkspaceSettingsService. Last-owner guard + owner-immutability present; static key = full trust, per-workspace keys scoped to role. No unguarded mutating endpoint found. Acceptance met.
Activity
- tesks changed status to Status → Done
- tesks commented
- ParadauxIO linked a commit — Commit c7d4aa0 — Enforce RBAC on the REST + MCP surfaces; staff break-glass; soft-archive (PAR-185, PAR-186)
- ParadauxIO linked a commit — Commit 25680a9 — Add configurable per-workspace RBAC + enforce it on the admin surface (PAR-184, PAR-185)
- tesks commented
- tesks changed status to Status → Pending Release
- tesks commented
- tesks changed status to Status → In Progress
- tesks created the issue