Paradaux

PAR-187

0

UI RBAC alignment: delete→manage, owner/last-owner protection, roles admin screen

DoneHighUnassignedTesksFeature

Child of PAR-182. UI side of RBAC (tesks-ui).

  • Delete gating bug: deleteIssueAction uses requireWorkspaceWrite — should be manage-level (lib/actions/issues.ts:106); button in issues/[ref]/page.tsx:64 renders 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|viewer union (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 staff super-role: lib/workspace.ts:57-59 injects Keycloak staff as 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-Staff from the UI's server-to-server calls for Keycloak staff users — 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|viewer union in lib/roles.ts. Add the roles management screen (admin-gated) backed by the role/role_permission tables.
  • 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:

  • Workspace carries the viewer's resolved permission keys (from the V19 role/role_permission mirror tables; staff get the admin set). lib/roles exposes canWrite/canDeleteIssues/canManageMembers/canManageSettings/canManageRoles/canManage over them; the old owner|member|viewer union is gone.
  • lib/api forwards X-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}/roles endpoints, gated on role.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