Files
cameleer-server/docs/superpowers/specs/2026-03-17-rbac-crud-gaps-design.md
hsiegeln 8ad0016a8e
Some checks failed
CI / build (push) Failing after 40s
CI / cleanup-branch (push) Has been skipped
CI / docker (push) Has been skipped
CI / deploy (push) Has been skipped
CI / deploy-feature (push) Has been skipped
refactor: rename group/groupName to application/applicationName
The execution-related "group" concept actually represents the
application name. Rename all Java fields, API parameters, and frontend
types from groupName→applicationName and group→application for clarity.

- Java records: ExecutionSummary, ExecutionDetail, ExecutionDocument,
  ExecutionRecord, ProcessorRecord
- API params: SearchRequest.group→application, SearchController
  @RequestParam group→application
- Services: IngestionService, DetailService, SearchIndexer, StatsStore
- Frontend: schema.d.ts, Dashboard, ExchangeDetail, RouteDetail,
  executions query hooks

Database column names (group_name) and OpenSearch field names are
unchanged — only the API-facing Java/TS field names are renamed.

RBAC group references (groups table, GroupRepository, GroupsTab) are
a separate domain concept and are NOT affected by this change.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 21:21:38 +01:00

7.7 KiB

RBAC CRUD Gaps — Design Specification

Goal

Add missing CRUD and assignment UI to the RBAC management page, fix date formatting, seed a built-in Admins group, and fix dashboard diagram ordering.

References

  • Parent spec: docs/superpowers/specs/2026-03-17-rbac-management-design.md
  • Visual prototype: examples/RBAC/rbac_management_ui.html

Changes

1. Users Tab — Delete + Assignments

Users cannot be created manually (they arrive via login). The detail pane gains:

  • Delete button in the detail header area. Uses existing ConfirmDeleteDialog with the user's displayName as the confirmation string. Calls useDeleteUser(). Guard: the currently authenticated user (from useAuthStore) cannot delete themselves — button disabled with tooltip "Cannot delete your own account".
  • Group membership section — "+ Add" chip opens a multi-select dropdown listing all groups the user is NOT already a member of. Checkboxes for batch selection, "Apply" button to commit. Calls are batched via Promise.allSettled() — if any fail, show an inline error, invalidate queries regardless to refresh. Existing group chips gain an "x" remove button calling useRemoveUserFromGroup().
  • Direct roles section — the existing "Effective roles" section renders both direct and inherited roles. The "+ Add" multi-select dropdown lists roles not yet directly assigned. Calls useAssignRoleToUser() (batched via Promise.allSettled()). Direct role chips gain an "x" button calling useRemoveRoleFromUser(). Inherited role chips (dashed border) do NOT get remove buttons — they can only be removed by changing group membership or group role assignments.
  • Created field — change from date-only to full date+time: new Date(createdAt).toLocaleString().
  • Mutation button states — all action buttons (delete, remove chip "x") disable while their mutation is in-flight to prevent double-clicks.

2. Groups Tab — CRUD + Assignments

  • "+ Add group" button in the panel header (.btnAdd style exists). Opens an inline form below the search bar with: name text input, optional parent group dropdown, "Create" button. Calls useCreateGroup(). Form clears and closes on success. On error: shows error message inline.
  • Delete button in detail pane header. Uses ConfirmDeleteDialog with group name. Calls useDeleteGroup(). Resets selected group. Guard: the built-in Admins group (SystemRole.ADMINS_GROUP_ID) cannot be deleted — button disabled with tooltip "Built-in group cannot be deleted".
  • Assigned roles section — "+ Add" multi-select dropdown listing roles not yet assigned to this group. Batched via Promise.allSettled(). Calls useAssignRoleToGroup(). Role chips gain "x" for useRemoveRoleFromGroup().
  • Parent group — shown as a dropdown in the detail header area, allowing re-parenting. Calls useUpdateGroup(). The dropdown excludes the group itself and its transitive descendants (cycle prevention — requires recursive traversal of childGroups on each GroupDetail). Setting to empty/none makes it top-level.

3. Roles Tab — CRUD

  • "+ Add role" button in panel header. Opens an inline form: name (required), description (optional), scope (optional, free-text, defaults to "custom"). Calls useCreateRole().
  • Delete button in detail pane header. Disabled for system roles (lock icon + tooltip "System roles cannot be deleted"). Custom roles use ConfirmDeleteDialog with role name → useDeleteRole().
  • No assignment UI on the roles tab — assignments are managed from the User and Group detail panes.

4. Multi-Select Dropdown Component

A reusable component used across all assignment actions:

Props:
  items: { id: string; label: string }[]   — available items to pick from
  onApply: (selectedIds: string[]) => void  — called with all checked IDs
  placeholder?: string                      — search filter placeholder

Behavior:

  • Opens as a positioned dropdown below the "+ Add" chip
  • Search/filter input at top
  • Checkbox list of items (max-height with scroll)
  • "Apply" button at bottom (disabled when nothing selected)
  • Closes on Apply, Escape, or click-outside
  • Shows count badge on Apply button: "Apply (3)"

Styling: background var(--bg-raised), border var(--border), border-radius var(--radius-md), items with var(--bg-hover) on hover, checkboxes with var(--amber) accent.

5. Inline Create Form

A reusable pattern for "Add group" and "Add role":

  • Appears below the search bar in the list pane, pushing content down
  • Input fields with labels
  • "Create" and "Cancel" buttons
  • On success: closes form, clears inputs, new entity appears in list
  • On error: shows error message inline
  • "Create" button disabled while mutation is in-flight

6. Built-in Admins Group Seed

Database migration — new V2__admin_group_seed.sql (V1 is already deployed, V2-V10 were deleted in the migration consolidation so V2 is safe):

-- Built-in Admins group
INSERT INTO groups (id, name) VALUES
    ('00000000-0000-0000-0000-000000000010', 'Admins');

-- Assign ADMIN role to Admins group
INSERT INTO group_roles (group_id, role_id) VALUES
    ('00000000-0000-0000-0000-000000000010', '00000000-0000-0000-0000-000000000004');

SystemRole.java — add constants:

public static final UUID ADMINS_GROUP_ID = UUID.fromString("00000000-0000-0000-0000-000000000010");

UiAuthController.login() — after upserting the user and assigning ADMIN role, also add to Admins group:

rbacService.addUserToGroup(subject, SystemRole.ADMINS_GROUP_ID);

Frontend guard: The Admins group UUID is hardcoded as a constant in the frontend to disable deletion. Alternatively, check if a group's ID matches a known system group ID.

7. Dashboard Diagram Ordering

The inheritance diagram's three columns (Groups → Roles → Users) must show items in a consistent, matching order:

  • Groups column: alphabetical by name, children indented under parents
  • Roles column: iterate groups top-to-bottom, collect their direct roles, deduplicate preserving first-seen order. Roles not assigned to any group are omitted from the diagram.
  • Users column: alphabetical by display name

Sort explicitly in DashboardTab.tsx before rendering.


Files Changed

Frontend — Modified

File Change
ui/src/pages/admin/rbac/UsersTab.tsx Delete button, group/role assignment dropdowns, date format fix, self-delete guard
ui/src/pages/admin/rbac/GroupsTab.tsx Add group form, delete button, role assignment dropdown, parent group dropdown, Admins guard
ui/src/pages/admin/rbac/RolesTab.tsx Add role form, delete button (disabled for system)
ui/src/pages/admin/rbac/DashboardTab.tsx Sort diagram columns consistently
ui/src/pages/admin/rbac/RbacPage.module.css Styles for multi-select dropdown, inline create form, delete button, action chips, remove buttons

Frontend — New

File Responsibility
ui/src/pages/admin/rbac/components/MultiSelectDropdown.tsx Reusable multi-select picker with search, checkboxes, batch apply

Backend — Modified

File Change
cameleer3-server-core/.../rbac/SystemRole.java Add ADMINS_GROUP_ID constant
cameleer3-server-app/.../security/UiAuthController.java Add admin user to Admins group on login

Backend — New Migration

File Change
cameleer3-server-app/src/main/resources/db/migration/V2__admin_group_seed.sql Seed Admins group + ADMIN role assignment

Out of Scope

  • Editing user profile fields (name, email) — users are managed by their identity provider
  • Drag-and-drop group hierarchy management
  • Role permission editing (custom roles have no effect on Spring Security yet)