PAR-187
UI RBAC alignment: delete→manage, owner/last-owner protection, roles admin screen
Child of PAR-182. UI side of RBAC (tesks-ui).
- Delete gating bug:
deleteIssueActionusesrequireWorkspaceWrite— should be manage-level (lib/actions/issues.ts:106); button inissues/[ref]/page.tsx:64renders for any writer. - Owner/last-owner protection: none today — any owner can demote/remove any other owner via
setMemberRoleAction/removeMemberAction(lib/actions/members.ts). Add guards mirroring the backend. - Configurable roles: replace the hard-coded
owner|member|viewerunion (lib/roles.ts:3) with roles fetched from the workspace; add a roles management screen in settings to create/edit roles + permissions (admin-gated). Defaults admin/contributor/viewer. - Global
staffsuper-role:lib/workspace.ts:57-59injects Keycloakstaffas a member of every tenant — fine for internal ops but document/flag it so it isn't mistaken for tenant isolation. - Permission predicates (
lib/roles.ts) become permission-keyed, sourced from the backend rather than hard-coded unions.
Acceptance: UI affordances match server-side permissions; non-admins can't see/perform manage actions; owners are protected; an admin can define a custom role and assign it.
Resources
Comments
tesks · Jun 15, 2026, 8:22 PM
Backend dependencies for the UI work are now in place (commits 25680a9, c7d4aa0). When building this:
- Forward
X-Actor-Stafffrom the UI's server-to-server calls for Keycloakstaffusers — the backend grants them admin-equivalent rights in any workspace only when this header is present (otherwise staff who aren't members get 403 on managed writes). - Roles are now backend-driven — fetch the workspace's roles + the actor's permission set from the API instead of the hard-coded
owner|member|viewerunion inlib/roles.ts. Add the roles management screen (admin-gated) backed by therole/role_permissiontables. - Delete → archive: the REST delete is now a soft-archive; add archive/restore UX and gate the button on the (manage/owner) permission. A future owner-only "permanently delete" (issue.destroy) endpoint is still to be added.
- API-key mint: expose a role selector (defaults to contributor) —
ApiKeyService.create(name, by, roleSlug)already supports it. - 403s from the API now carry
{ "error": ... }; surface them.
tesks · Jun 16, 2026, 3:49 AM
Phase 1 done on develop (tesks-ui commit 87698db) — RBAC is now permission-driven end-to-end:
Workspacecarries the viewer's resolved permission keys (from the V19role/role_permissionmirror tables; staff get the admin set).lib/rolesexposescanWrite/canDeleteIssues/canManageMembers/canManageSettings/canManageRoles/canManageover them; the oldowner|member|viewerunion is gone.lib/apiforwardsX-Actor-Staff→ backend staff break-glass works.- Gates aligned to backend permissions: settings-domain actions (statuses/labels/teams/projects/releases/integrations/API keys) →
settings.manage; issue delete (now a soft Archive) →issue.delete; members →member.manage. Settings area opens to anyone with member- or settings-management. - Member role pickers are backend-driven (the workspace's configurable roles), not a fixed enum.
- Kysely types gained the new tables; unit tests rewritten for the permission model (typecheck + vitest green).
Remaining — Phase 2: custom-role CRUD UI. Members can now be assigned any of the seeded roles, but there's no screen yet to create/edit/delete custom roles. That needs new backend endpoints (WorkspaceAdminService create/update/delete role + WorkspaceApiController routes, gated on role.manage, protecting the owner/system roles + in-use roles) plus a Roles section in settings with per-permission checkboxes. Owner-only powers (issue.destroy, workspace.manage) stay non-assignable to custom roles.
tesks · Jun 16, 2026, 3:57 AM
Phase 2 done on develop — custom-role CRUD shipped (tesks commit 061dba7, tesks-ui commit 096c5c7):
- Backend:
WorkspaceAdminService.createRole/updateRole/deleteRole+/api/w/{slug}/rolesendpoints, gated onrole.manage. Owner role immutable, built-in roles undeletable, in-use roles undeletable; owner-only powers (issue.destroy/workspace.manage) not assignable to custom roles. 4 tests (backend suite 112 green). - UI: a Roles section in Settings (gated on
role.manage) — create custom roles, edit a role's permissions via checkboxes, delete unused ones; owner shows as fixed.
Verified: tesks-ui tsc + vitest + next build all green.
Core RBAC for PAR-187 is complete and shipped → moving to Pending Release. The two remaining minor polish items (API-key mint role selector, friendlier 403 surfacing) are split out as PAR-195 (Low).
tesks · Jun 16, 2026, 8:10 PM
Verified LIVE — all four criteria: (1) deleteIssueAction gated by requireWorkspaceDelete/canDeleteIssues (issue.delete, matching the backend ISSUE_DELETE archive gate) and the delete button only renders for canDelete; (2) owner/last-owner protection enforced server-side (member actions defer to backend rules); (3) lib/roles.ts no longer a hard-coded union (Role = string), roles fetched per workspace via lib/sql/roles.listRoles; (4) roles management screen in settings/page.tsx (create/edit/delete + permission checkboxes, canManageRoles-gated). Acceptance met.
Activity
- tesks changed status to Status → Done
- tesks commented
- ParadauxIO linked a commit — Commit 061dba7 — Add custom role CRUD (role.manage) for configurable workspace roles (PAR-184)
- 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 commented
- tesks created the issue