Rename Java packages from com.cameleer3 to com.cameleer, module directories from cameleer3-* to cameleer-*, and all references throughout workflows, Dockerfiles, docs, migrations, and pom.xml. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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
ConfirmDeleteDialogwith the user'sdisplayNameas the confirmation string. CallsuseDeleteUser(). Guard: the currently authenticated user (fromuseAuthStore) 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 callinguseRemoveUserFromGroup(). - 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 viaPromise.allSettled()). Direct role chips gain an "x" button callinguseRemoveRoleFromUser(). 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 (
.btnAddstyle exists). Opens an inline form below the search bar with: name text input, optional parent group dropdown, "Create" button. CallsuseCreateGroup(). Form clears and closes on success. On error: shows error message inline. - Delete button in detail pane header. Uses
ConfirmDeleteDialogwith group name. CallsuseDeleteGroup(). 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(). CallsuseAssignRoleToGroup(). Role chips gain "x" foruseRemoveRoleFromGroup(). - 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 ofchildGroupson eachGroupDetail). 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
ConfirmDeleteDialogwith 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 |
|---|---|
cameleer-server-core/.../rbac/SystemRole.java |
Add ADMINS_GROUP_ID constant |
cameleer-server-app/.../security/UiAuthController.java |
Add admin user to Admins group on login |
Backend — New Migration
| File | Change |
|---|---|
cameleer-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)