Files
cve-dashboard/.kiro/specs/admin-page-overhaul/design.md

424 lines
18 KiB
Markdown

# Design Document: Admin Page Overhaul
## Overview
The Admin Page Overhaul replaces the current inline `UserManagement` modal rendering on the admin page with a full-page, themed admin panel. The new `AdminPage` component follows the same layout conventions as `CompliancePage`, `ExportsPage`, and `KnowledgeBasePage` — a top-level page component rendered in the main content area of `App.js` when `currentPage === 'admin'`.
The page consolidates three admin functions into a single tabbed interface:
1. **User Management** — themed table with inline add/edit forms, group badges, and active status toggles
2. **Audit Log** — paginated, filterable log table with action-type badges and date range filters
3. **System Info** — stat cards showing user counts, audit log totals, and recent activity
All sections use the dark tactical intelligence theme defined in `DESIGN_SYSTEM.md` and `App.css``intel-card` containers, `intel-button` controls, `intel-input` form fields, `status-badge` action labels, and `stat-card` stat displays.
### Design Decisions
- **New component, not a wrapper.** The existing `UserManagement.js` and `AuditLog.js` are white-background modals with Tailwind utility classes. Wrapping them would create visual inconsistency. The `AdminPage` component builds themed versions of both panels from scratch, reusing the same backend API endpoints.
- **Existing modals preserved.** The `UserMenu` quick-access links ("Manage Users", "Audit Log") continue to open the existing modal components. This keeps the quick-access workflow intact while the admin page provides the full-featured experience.
- **No new backend endpoints.** All data comes from existing routes: `GET /api/users`, `POST/PATCH/DELETE /api/users/:id`, `GET /api/audit-logs`, `GET /api/audit-logs/actions`.
- **Inline styles + App.css classes.** Follows the project convention of defining style constants in the component file and referencing `App.css` classes (`intel-card`, `intel-button`, `data-row`, etc.) where available.
## Architecture
```mermaid
graph TD
A[App.js] -->|currentPage === 'admin' && isAdmin| B[AdminPage]
B --> C[Tab Navigation]
C -->|"User Management"| D[UserManagementPanel]
C -->|"Audit Log"| E[AuditLogPanel]
C -->|"System Info"| F[SystemInfoPanel]
D -->|GET /api/users| G[Backend: users.js]
D -->|POST /api/users| G
D -->|PATCH /api/users/:id| G
D -->|DELETE /api/users/:id| G
E -->|GET /api/audit-logs| H[Backend: auditLog.js]
E -->|GET /api/audit-logs/actions| H
F -->|GET /api/users| G
F -->|GET /api/audit-logs?limit=10| H
```
### Component Hierarchy
```
AdminPage
├── PageHeader (title + accent glow)
├── TabNavigation (User Management | Audit Log | System Info)
├── UserManagementPanel
│ ├── AddUserButton / InlineForm
│ ├── UserTable
│ │ └── UserRow (group badge, status toggle, edit/delete actions)
│ ├── ErrorBanner
│ └── SuccessToast
├── AuditLogPanel
│ ├── FilterBar (username, action, entity type, start date, end date)
│ ├── LogTable
│ │ └── LogRow (timestamp, user, action badge, entity, details, IP)
│ ├── Pagination
│ ├── EmptyState
│ └── ErrorBanner
└── SystemInfoPanel
├── StatCards (total users, active users, audit entries, recent logins)
└── RecentActivityList (10 most recent audit entries)
```
### Data Flow
1. `App.js` renders `<AdminPage />` when `currentPage === 'admin'` and `isAdmin()` returns true. Non-admin users are redirected to home.
2. `AdminPage` manages the active tab in local state (default: `'users'`).
3. Each panel fetches its own data on mount using `fetch()` with `credentials: 'include'`.
4. Mutations (create, update, delete user) trigger a re-fetch of the user list. Success/error feedback is shown inline.
5. Audit log panel manages its own pagination and filter state, re-fetching on filter apply or page change.
6. System info panel fetches user list and recent audit logs on mount, computing derived stats client-side.
## Components and Interfaces
### AdminPage (main component)
```javascript
// frontend/src/components/pages/AdminPage.js
export default function AdminPage() {
// Props: none (reads auth context internally)
// State:
// activeTab: 'users' | 'audit' | 'system'
// Renders: PageHeader, TabNavigation, conditional panel
}
```
### TabNavigation
```javascript
// Internal to AdminPage
// Props:
// activeTab: string
// onTabChange: (tab: string) => void
// Tabs: [
// { id: 'users', label: 'User Management', icon: Shield },
// { id: 'audit', label: 'Audit Log', icon: Clock },
// { id: 'system', label: 'System Info', icon: Activity },
// ]
```
Styling: monospace uppercase text, `--intel-accent` border and background on active tab, transparent with muted text on inactive tabs. Matches the tab pattern used in `CompliancePage` (team tabs).
### UserManagementPanel
```javascript
// Internal to AdminPage
// State:
// users: Array<User>
// loading: boolean
// error: string | null
// showForm: boolean
// editingUser: User | null
// formData: { username, email, password, group }
// formError: string
// successMessage: string
//
// API calls:
// GET /api/users → fetch all users
// POST /api/users → create user
// PATCH /api/users/:id → update user (fields, group, is_active)
// DELETE /api/users/:id → delete user
//
// Group badge colors (themed):
// Admin: --intel-danger (#EF4444)
// Standard_User: --intel-accent (#0EA5E9)
// Leadership: --intel-warning (#F59E0B)
// Read_Only: --text-muted (#94A3B8)
```
### AuditLogPanel
```javascript
// Internal to AdminPage
// State:
// logs: Array<AuditLogEntry>
// loading: boolean
// error: string | null
// pagination: { page, limit, total, totalPages }
// filters: { user, action, entityType, startDate, endDate }
// actions: string[] (populated from /api/audit-logs/actions)
//
// API calls:
// GET /api/audit-logs?page=&limit=25&user=&action=&entityType=&startDate=&endDate=
// GET /api/audit-logs/actions
//
// Action badge colors (themed):
// login/logout: --intel-success (#10B981)
// *_create: --intel-accent (#0EA5E9)
// *_update/*_edit: --intel-warning (#F59E0B)
// *_delete: --intel-danger (#EF4444)
// default: --text-muted (#94A3B8)
```
### SystemInfoPanel
```javascript
// Internal to AdminPage
// State:
// users: Array<User>
// recentLogs: Array<AuditLogEntry>
// loading: boolean
// errors: { users: string | null, logs: string | null }
//
// Derived stats:
// totalUsers: users.length
// activeUsers: users.filter(u => u.is_active).length
// recentLogins: users.filter(u => u.last_login && withinLast7Days(u.last_login)).length
// totalAuditEntries: fetched from audit-logs pagination.total
//
// API calls:
// GET /api/users
// GET /api/audit-logs?limit=10&page=1
```
### Integration with App.js
```javascript
// In App.js, replace:
// {currentPage === 'admin' && isAdmin() && (
// <div className="space-y-6">
// <UserManagement onClose={() => setCurrentPage('home')} />
// </div>
// )}
//
// With:
// {currentPage === 'admin' && isAdmin() && <AdminPage />}
// {currentPage === 'admin' && !isAdmin() && /* redirect to home */}
//
// Keep existing modal triggers:
// {showUserManagement && <UserManagement onClose={...} />}
// {showAuditLog && <AuditLog onClose={...} />}
```
## Data Models
### User (from GET /api/users)
```javascript
{
id: number,
username: string,
email: string,
group: 'Admin' | 'Standard_User' | 'Leadership' | 'Read_Only',
is_active: 0 | 1,
created_at: string, // ISO datetime
last_login: string | null // ISO datetime
}
```
### AuditLogEntry (from GET /api/audit-logs)
```javascript
{
id: number,
user_id: number,
username: string,
action: string, // e.g. 'login', 'user_create', 'cve_delete'
entity_type: string, // e.g. 'auth', 'user', 'cve', 'document'
entity_id: string | null,
details: string | null, // JSON string
ip_address: string | null,
created_at: string // ISO datetime
}
```
### AuditLogPagination (from GET /api/audit-logs response)
```javascript
{
logs: AuditLogEntry[],
pagination: {
page: number,
limit: number,
total: number,
totalPages: number
}
}
```
### Tab Configuration
```javascript
const TABS = [
{ id: 'users', label: 'User Management', icon: Shield },
{ id: 'audit', label: 'Audit Log', icon: Clock },
{ id: 'system', label: 'System Info', icon: Activity },
];
```
### Group Badge Theme Map
```javascript
const GROUP_BADGE_THEMED = {
Admin: { bg: 'rgba(239,68,68,0.15)', border: '#EF4444', text: '#FCA5A5' },
Standard_User: { bg: 'rgba(14,165,233,0.15)', border: '#0EA5E9', text: '#7DD3FC' },
Leadership: { bg: 'rgba(245,158,11,0.15)', border: '#F59E0B', text: '#FCD34D' },
Read_Only: { bg: 'rgba(148,163,184,0.15)', border: '#94A3B8', text: '#CBD5E1' },
};
```
### Action Badge Theme Map
```javascript
const ACTION_BADGE_THEMED = {
login: { bg: 'rgba(16,185,129,0.15)', border: '#10B981', text: '#6EE7B7' },
logout: { bg: 'rgba(148,163,184,0.15)', border: '#94A3B8', text: '#CBD5E1' },
login_failed: { bg: 'rgba(239,68,68,0.15)', border: '#EF4444', text: '#FCA5A5' },
user_create: { bg: 'rgba(14,165,233,0.15)', border: '#0EA5E9', text: '#7DD3FC' },
user_update: { bg: 'rgba(245,158,11,0.15)', border: '#F59E0B', text: '#FCD34D' },
user_delete: { bg: 'rgba(239,68,68,0.15)', border: '#EF4444', text: '#FCA5A5' },
cve_create: { bg: 'rgba(14,165,233,0.15)', border: '#0EA5E9', text: '#7DD3FC' },
cve_edit: { bg: 'rgba(245,158,11,0.15)', border: '#F59E0B', text: '#FCD34D' },
cve_delete: { bg: 'rgba(239,68,68,0.15)', border: '#EF4444', text: '#FCA5A5' },
document_upload: { bg: 'rgba(139,92,246,0.15)', border: '#8B5CF6', text: '#C4B5FD' },
document_delete: { bg: 'rgba(239,68,68,0.15)', border: '#EF4444', text: '#FCA5A5' },
};
```
## Correctness Properties
*A property is a characteristic or behavior that should hold true across all valid executions of a system — essentially, a formal statement about what the system should do. Properties serve as the bridge between human-readable specifications and machine-verifiable correctness guarantees.*
### Property 1: Group badge color mapping is total and correct
*For any* valid user group string (`Admin`, `Standard_User`, `Leadership`, `Read_Only`), the group badge styling function SHALL return a non-null object with `bg`, `border`, and `text` fields matching the themed color for that group. *For any* string that is not one of the four valid groups, the function SHALL return the default muted styling.
**Validates: Requirements 3.3**
### Property 2: Edit form population preserves user data
*For any* user object with arbitrary `username`, `email`, and `group` values, populating the edit form from that user SHALL result in `formData.username === user.username`, `formData.email === user.email`, and `formData.group === user.group`, with `formData.password` set to an empty string.
**Validates: Requirements 3.5**
### Property 3: Self-modification prevention
*For any* user list that contains the currently authenticated admin user, the admin user's own row SHALL have the group dropdown disabled and the active status toggle disabled. *For any* other user in the list, those controls SHALL be enabled.
**Validates: Requirements 3.8**
### Property 4: Action badge color mapping is total and correct
*For any* known audit log action string (from the set of defined actions: `login`, `logout`, `login_failed`, `user_create`, `user_update`, `user_delete`, `cve_create`, `cve_edit`, `cve_delete`, `document_upload`, `document_delete`), the action badge styling function SHALL return the correct themed color object. *For any* unknown action string, the function SHALL return the default muted styling.
**Validates: Requirements 4.4**
### Property 5: Applying filters resets pagination to page 1
*For any* combination of filter values (username text, action type, entity type, start date, end date) and *for any* current page number, applying the filters SHALL result in a fetch call with `page=1`.
**Validates: Requirements 4.7**
### Property 6: Recent login count computation
*For any* list of user objects with random `last_login` timestamps (including null values), the computed "recent logins" count SHALL equal the number of users whose `last_login` is non-null and falls within the last 7 days from the current time.
**Validates: Requirements 5.1**
### Property 7: Admin-only access control
*For any* user object, the admin page content SHALL be rendered if and only if `user.group === 'Admin'`. When `user.group` is any value other than `'Admin'`, the system SHALL redirect to the home page.
**Validates: Requirements 6.1, 6.2**
## Error Handling
### User Management Panel
| Error Scenario | Handling |
|---|---|
| `GET /api/users` fails | Display error banner with `--intel-danger` styling. User table is hidden. Retry on next tab switch or manual refresh. |
| `POST /api/users` fails (validation) | Display `formError` message below the form in danger color. Form remains open for correction. |
| `POST /api/users` fails (409 conflict) | Display "Username or email already exists" in `formError`. |
| `PATCH /api/users/:id` fails | Display inline error. Revert optimistic UI changes if any. |
| `DELETE /api/users/:id` fails | Display alert with error message. User list unchanged. |
| Self-demotion attempt | Group dropdown disabled for current user. Backend returns 400 if bypassed. |
| Self-deactivation attempt | Toggle disabled for current user. Backend returns 400 if bypassed. |
### Audit Log Panel
| Error Scenario | Handling |
|---|---|
| `GET /api/audit-logs` fails | Display error banner with `--intel-danger` styling. Table hidden. |
| `GET /api/audit-logs/actions` fails | Action filter dropdown shows no options. Non-critical — silently ignored. |
| Invalid date range (start > end) | Client-side: no validation needed — backend handles gracefully by returning empty results. |
| Empty result set | Display "No audit log entries found" message in `--text-muted` color. |
### System Info Panel
| Error Scenario | Handling |
|---|---|
| `GET /api/users` fails | Affected stat cards (total users, active users, recent logins) show "Unable to load" fallback text. |
| `GET /api/audit-logs` fails | Audit entries stat card and recent activity list show "Unable to load" fallback. |
| Partial failure (one endpoint fails) | Only the affected cards show fallback. Successfully loaded cards display normally. |
### Success Feedback
- Create user: green success toast "User created successfully" auto-dismisses after 2 seconds.
- Update user: green success toast "User updated successfully" auto-dismisses after 2 seconds.
- Delete user: green success toast "User deleted" auto-dismisses after 2 seconds.
- Toggle active status: immediate UI update, no toast (inline visual feedback is sufficient).
## Testing Strategy
### Unit Tests (Example-Based)
Unit tests cover specific rendering, interaction, and integration scenarios:
**AdminPage structure:**
- Renders page header with "Admin Panel" title
- Defaults to User Management tab on mount
- Switches panels when tabs are clicked
- Only renders when user is admin (access control)
**UserManagementPanel:**
- Renders user table with all required columns
- Displays themed group badges for each group type
- Shows inline form when "Add User" is clicked
- Populates form with user data when edit is clicked
- Shows confirmation dialog on delete
- Disables self-modification controls for current user
- Displays error banner on API failure
- Displays success toast on successful operations
**AuditLogPanel:**
- Renders log table with all required columns
- Displays themed action badges
- Renders filter controls (username, action, entity type, dates)
- Fetches page 1 when filters are applied
- Navigates pages with pagination controls
- Shows empty state when no results
- Shows error banner on API failure
**SystemInfoPanel:**
- Renders four stat cards with correct labels
- Computes derived stats correctly from mock data
- Shows recent activity list with up to 10 entries
- Shows fallback message when an API call fails
### Property-Based Tests
Property-based tests use [fast-check](https://github.com/dubzzz/fast-check) to verify universal properties across generated inputs. Each test runs a minimum of 100 iterations.
| Property | Test Description | Tag |
|---|---|---|
| Property 1 | Generate random group strings (valid + invalid), verify badge function returns correct colors | Feature: admin-page-overhaul, Property 1: Group badge color mapping is total and correct |
| Property 2 | Generate random user objects, verify edit form population matches user fields exactly | Feature: admin-page-overhaul, Property 2: Edit form population preserves user data |
| Property 3 | Generate random user lists containing the current admin, verify self-edit controls are disabled | Feature: admin-page-overhaul, Property 3: Self-modification prevention |
| Property 4 | Generate random action strings (known + unknown), verify badge function returns correct colors | Feature: admin-page-overhaul, Property 4: Action badge color mapping is total and correct |
| Property 5 | Generate random filter states and current page numbers, verify fetch is called with page=1 | Feature: admin-page-overhaul, Property 5: Applying filters resets pagination to page 1 |
| Property 6 | Generate random user lists with random last_login timestamps, verify recent login count matches manual computation | Feature: admin-page-overhaul, Property 6: Recent login count computation |
| Property 7 | Generate random user objects with random groups, verify admin page renders iff group === 'Admin' | Feature: admin-page-overhaul, Property 7: Admin-only access control |
### Test Configuration
- **Library:** fast-check (JavaScript property-based testing)
- **Runner:** Jest (via react-scripts test)
- **Iterations:** Minimum 100 per property test (`fc.assert(property, { numRuns: 100 })`)
- **Tag format:** Comment at top of each property test referencing the design property