359 lines
11 KiB
Markdown
359 lines
11 KiB
Markdown
# Keys & Secrets Management Page Implementation
|
|
|
|
**Date:** 2026-02-05
|
|
**Status:** Complete
|
|
|
|
## Overview
|
|
|
|
Implemented a comprehensive Keys & Secrets management page for the Attune web UI, enabling users to perform CRUD operations on encrypted secrets and configuration values with proper scoping and format validation.
|
|
|
|
## Changes Made
|
|
|
|
### New Files Created
|
|
|
|
1. **`attune/web/src/hooks/useKeys.ts`**
|
|
- Custom React hooks for keys/secrets API operations
|
|
- `useKeys()` - Fetch paginated list with filters
|
|
- `useKey(ref)` - Fetch single key by reference (includes decrypted value)
|
|
- `useCreateKey()` - Create new key mutation
|
|
- `useUpdateKey()` - Update existing key mutation
|
|
- `useDeleteKey()` - Delete key mutation
|
|
- Proper query invalidation for cache management
|
|
|
|
2. **`attune/web/src/pages/keys/KeysPage.tsx`**
|
|
- Main list view for keys and secrets
|
|
- Search functionality (by reference or name)
|
|
- Filter by scope (System, User, Pack, Action, Sensor)
|
|
- Client-side search with server-side scope filtering
|
|
- Pagination (20 items per page)
|
|
- Color-coded scope badges
|
|
- Encryption status indicators
|
|
- Inline edit and delete actions
|
|
- Empty states and error handling
|
|
|
|
3. **`attune/web/src/pages/keys/KeyCreateModal.tsx`**
|
|
- Modal dialog for creating new keys
|
|
- **Value format selection** with validation:
|
|
- **Text** (can be encrypted)
|
|
- **JSON** (can be encrypted, validates JSON syntax)
|
|
- **YAML** (can be encrypted, basic validation)
|
|
- **Number** (cannot be encrypted, validates numeric)
|
|
- **Integer** (cannot be encrypted, validates integer)
|
|
- **Boolean** (cannot be encrypted, validates true/false)
|
|
- Encryption toggle (automatically disabled for non-encryptable formats)
|
|
- Scope selection (System, User, Pack, Action, Sensor)
|
|
- Owner identifier field (conditional based on scope)
|
|
- Format-aware validation before submission
|
|
- Reference format validation (alphanumeric, _, -, .)
|
|
|
|
4. **`attune/web/src/pages/keys/KeyEditModal.tsx`**
|
|
- Modal dialog for editing existing keys
|
|
- Shows key metadata (reference, scope, owner) as read-only
|
|
- Editable name and value fields
|
|
- Show/hide value toggle for security
|
|
- Encryption toggle with warning when changing encryption status
|
|
- Validates changes before submission
|
|
- Visual warnings when changing encryption settings
|
|
|
|
### Modified Files
|
|
|
|
1. **`attune/web/src/App.tsx`**
|
|
- Added route for `/keys` (KeysPage)
|
|
- Imported KeysPage component
|
|
|
|
2. **`attune/web/src/components/layout/MainLayout.tsx`**
|
|
- Added "Keys & Secrets" navigation item
|
|
- Positioned before "Pack Management"
|
|
- Uses `KeyRound` icon from lucide-react
|
|
|
|
## Features Implemented
|
|
|
|
### List Page (`/keys`)
|
|
|
|
- **Display Columns:**
|
|
- Reference (with key icon)
|
|
- Name (human-readable)
|
|
- Scope (color-coded badge)
|
|
- Owner (identifier or "—")
|
|
- Encrypted status (Yes/No with icons)
|
|
- Created timestamp
|
|
- Actions (Edit/Delete buttons)
|
|
|
|
- **Search & Filtering:**
|
|
- Real-time search by reference or name (client-side)
|
|
- Scope filter dropdown (System, User, Pack, Action, Sensor)
|
|
- Clear filters button
|
|
- Shows filtered count
|
|
|
|
- **Actions:**
|
|
- Create Key button (top-right)
|
|
- Edit button per row (opens modal)
|
|
- Delete button per row (with confirmation)
|
|
|
|
- **Pagination:**
|
|
- 20 items per page
|
|
- Previous/Next navigation
|
|
- Page indicator
|
|
|
|
### Create Modal
|
|
|
|
- **Required Fields:**
|
|
- Reference (unique identifier with validation)
|
|
- Name (human-readable description)
|
|
- Value (format-dependent validation)
|
|
- Scope (owner type)
|
|
|
|
- **Value Format Options:**
|
|
- **Text:** Plain text, can be encrypted
|
|
- **JSON:** Validates JSON syntax, can be encrypted
|
|
- **YAML:** Basic validation, can be encrypted
|
|
- **Number:** Validates numeric, **cannot be encrypted**
|
|
- **Integer:** Validates integer, **cannot be encrypted**
|
|
- **Boolean:** Validates true/false, **cannot be encrypted**
|
|
|
|
- **Encryption Rules:**
|
|
- Text, JSON, YAML formats: Encryption checkbox enabled
|
|
- Number, Integer, Boolean formats: Encryption checkbox disabled
|
|
- Automatic encryption toggle disable for non-encryptable formats
|
|
- Clear UI indication of encryption capability per format
|
|
|
|
- **Scope Configuration:**
|
|
- System: Global scope, no owner required
|
|
- User (Identity): Optional owner identifier
|
|
- Pack: Optional pack reference
|
|
- Action: Optional action reference
|
|
- Sensor: Optional sensor reference
|
|
|
|
- **Validation:**
|
|
- Reference format: alphanumeric, underscores, hyphens, dots only
|
|
- Value format-specific validation
|
|
- Required field checks
|
|
- Error messages displayed in modal
|
|
|
|
### Edit Modal
|
|
|
|
- **Features:**
|
|
- Load existing key data
|
|
- Display read-only metadata (reference, scope, owner)
|
|
- Edit name and value
|
|
- Show/Hide value toggle (Eye/EyeOff icons)
|
|
- Encryption toggle
|
|
- Warning when changing encryption status
|
|
- Only sends changed fields to API
|
|
|
|
- **Security:**
|
|
- Value masked by default (can be toggled)
|
|
- Clear indication of current encryption status
|
|
- Warnings for encryption changes
|
|
|
|
## Technical Implementation
|
|
|
|
### API Integration
|
|
|
|
Uses `SecretsService` from generated API client:
|
|
- `listKeys({ page, perPage, ownerType, owner })` - List with pagination
|
|
- `getKey({ ref })` - Get single key with decrypted value
|
|
- `createKey({ requestBody })` - Create new key
|
|
- `updateKey({ ref, requestBody })` - Update existing key
|
|
- `deleteKey({ ref })` - Delete key
|
|
|
|
### State Management
|
|
|
|
- React Query for server state and caching
|
|
- Local state for UI (search, filters, modals)
|
|
- Optimistic updates with cache invalidation
|
|
- Proper loading and error states
|
|
|
|
### Scope (Owner Type) Badges
|
|
|
|
Color-coded badges for quick identification:
|
|
- **System**: Purple (bg-purple-100, text-purple-800)
|
|
- **User (Identity)**: Blue (bg-blue-100, text-blue-800)
|
|
- **Pack**: Green (bg-green-100, text-green-800)
|
|
- **Action**: Yellow (bg-yellow-100, text-yellow-800)
|
|
- **Sensor**: Indigo (bg-indigo-100, text-indigo-800)
|
|
|
|
### Encryption Indicators
|
|
|
|
- **Encrypted**: EyeOff icon (green) + "Yes" text
|
|
- **Not Encrypted**: Eye icon (gray) + "No" text
|
|
|
|
### Format Validation Logic
|
|
|
|
```typescript
|
|
export type KeyFormat = "text" | "json" | "yaml" | "number" | "int" | "bool";
|
|
|
|
// Encryptable formats
|
|
const canEncrypt = format === "text" || format === "json" || format === "yaml";
|
|
|
|
// Validation examples:
|
|
- JSON: JSON.parse(value)
|
|
- Number/Int: Number(value), isNaN check
|
|
- Boolean: value.toLowerCase() === "true" || "false"
|
|
```
|
|
|
|
## Design Patterns
|
|
|
|
1. **Consistent Modal Pattern:**
|
|
- Fixed overlay with centered modal
|
|
- Header with title and close button
|
|
- Form with validation
|
|
- Footer with Cancel and Submit buttons
|
|
- Loading states during mutation
|
|
|
|
2. **Format-Aware UI:**
|
|
- Dynamic textarea rows based on format
|
|
- Placeholder text matches expected format
|
|
- Validation errors specific to format
|
|
- Auto-disable encryption for incompatible formats
|
|
|
|
3. **Security-First Approach:**
|
|
- Encryption enabled by default for encryptable formats
|
|
- Values masked in edit modal
|
|
- Confirmation dialogs for destructive actions
|
|
- Clear warnings when changing encryption
|
|
|
|
4. **Responsive Design:**
|
|
- Grid layout for filters
|
|
- Responsive table with horizontal scroll
|
|
- Mobile-friendly pagination
|
|
- Consistent with other pages (Events, Enforcements)
|
|
|
|
## User Flow Examples
|
|
|
|
### Creating a JSON Secret
|
|
|
|
1. Click "Create Key" button
|
|
2. Enter reference: `api_config`
|
|
3. Enter name: "API Configuration"
|
|
4. Select format: **JSON** (encryption checkbox enabled)
|
|
5. Enter value: `{"url": "https://api.example.com", "timeout": 30}`
|
|
6. Check "Encrypt value" (recommended)
|
|
7. Select scope: System
|
|
8. Click "Create Key"
|
|
9. ✅ Key created with encrypted JSON value
|
|
|
|
### Creating a Number Configuration
|
|
|
|
1. Click "Create Key" button
|
|
2. Enter reference: `max_retries`
|
|
3. Enter name: "Maximum Retry Attempts"
|
|
4. Select format: **Number** (encryption checkbox disabled)
|
|
5. Enter value: `3`
|
|
6. Note: Cannot encrypt (checkbox disabled and grayed out)
|
|
7. Select scope: System
|
|
8. Click "Create Key"
|
|
9. ✅ Key created with unencrypted numeric value
|
|
|
|
### Editing a Key
|
|
|
|
1. Click Edit button on any key row
|
|
2. Modal shows key metadata (read-only)
|
|
3. Modify name or value
|
|
4. Toggle show/hide value
|
|
5. Change encryption (warning displayed)
|
|
6. Click "Save Changes"
|
|
7. ✅ Key updated
|
|
|
|
## Known Limitations
|
|
|
|
1. **Format Field Client-Side Only:**
|
|
- The "format" field is not stored in the backend database
|
|
- Format is used for client-side validation only
|
|
- All values are stored as text in the database
|
|
- Future enhancement: Add `format` column to `key` table in backend
|
|
|
|
2. **Search Pagination:**
|
|
- Search is client-side, so only searches current page
|
|
- Consider adding server-side search parameter in future
|
|
|
|
3. **No Bulk Operations:**
|
|
- Delete/update one key at a time
|
|
- Could add multi-select for batch operations
|
|
|
|
4. **Value Masking:**
|
|
- CSS-based masking may not work in all browsers
|
|
- Consider more robust masking solution
|
|
|
|
## Security Considerations
|
|
|
|
1. **Encryption Enforcement:**
|
|
- Only Text, JSON, YAML can be encrypted (per requirements)
|
|
- Number, Integer, Boolean cannot be encrypted
|
|
- UI enforces this with disabled checkbox
|
|
|
|
2. **Value Exposure:**
|
|
- List view: Values are redacted (API behavior)
|
|
- Detail view: Values are decrypted (requires explicit fetch)
|
|
- Edit modal: Values are masked by default with toggle
|
|
|
|
3. **Access Control:**
|
|
- Keys are scoped by owner type
|
|
- Users can only access keys they have permissions for
|
|
- Enforced at API level (frontend follows)
|
|
|
|
## Build Status
|
|
|
|
✅ TypeScript compilation successful
|
|
✅ Vite build successful (584.69 kB gzip: 152.21 kB)
|
|
✅ No compiler warnings or errors
|
|
✅ All routes registered correctly
|
|
|
|
## Testing Recommendations
|
|
|
|
Manual testing should verify:
|
|
|
|
1. ✅ List page loads and displays keys
|
|
2. ✅ Search filters by reference and name
|
|
3. ✅ Scope filter works correctly
|
|
4. ✅ Create modal validates format-specific values
|
|
5. ✅ Encryption checkbox disabled for number/int/bool formats
|
|
6. ✅ JSON validation catches syntax errors
|
|
7. ✅ Boolean values must be "true" or "false"
|
|
8. ✅ Edit modal loads existing key data
|
|
9. ✅ Show/hide value toggle works
|
|
10. ✅ Delete confirms before deleting
|
|
11. ✅ Pagination functions properly
|
|
12. ✅ Empty states display correctly
|
|
13. ✅ Error states handle failures gracefully
|
|
|
|
## Future Enhancements
|
|
|
|
1. **Backend Format Field:**
|
|
- Add `format` column to `key` table
|
|
- Store format in database
|
|
- Display format in list view
|
|
- Validate format on backend
|
|
|
|
2. **Advanced Validation:**
|
|
- More robust YAML parser
|
|
- JSON schema validation
|
|
- Custom format types
|
|
|
|
3. **Bulk Operations:**
|
|
- Multi-select keys
|
|
- Bulk delete
|
|
- Bulk export/import
|
|
|
|
4. **Key Versioning:**
|
|
- Track value history
|
|
- Rollback to previous versions
|
|
- Audit trail
|
|
|
|
5. **Server-Side Search:**
|
|
- Add search parameter to API
|
|
- Search across all pages
|
|
- More efficient for large datasets
|
|
|
|
6. **Enhanced Security:**
|
|
- Role-based key access
|
|
- Key rotation policies
|
|
- Expiration dates
|
|
- Usage logging
|
|
|
|
## Documentation Updates Needed
|
|
|
|
Consider updating:
|
|
- `docs/api/api-secrets.md` - Document format field if added to backend
|
|
- `docs/guides/secrets-management.md` - User guide for keys page
|
|
- Add screenshots to documentation |