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>
143 lines
7.7 KiB
Markdown
143 lines
7.7 KiB
Markdown
# 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):
|
|
|
|
```sql
|
|
-- 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:
|
|
```java
|
|
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:
|
|
```java
|
|
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)
|