Files
cve-dashboard/.kiro/specs/user-profile/tasks.md

120 lines
8.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Implementation Plan: User Profile
## Overview
This plan implements the user profile feature in three phases: backend API routes first (profile endpoint and password change endpoint on the existing auth router), then the frontend components (UserProfilePanel modal and UserMenu theming/integration), and finally wiring everything together. Each task builds incrementally on the previous one, and testing tasks are placed close to the code they validate.
## Tasks
- [ ] 1. Add backend profile and password change routes to `routes/auth.js`
- [x] 1.1 Add `GET /api/auth/profile` route
- Add a new route inside `createAuthRouter` that queries the `users` table for `id, username, email, user_group, created_at, last_login` using the session user's ID
- Return `{ id, username, email, group, created_at, last_login }` on success
- Return 401 if the account is inactive (with `is_active = 0`), clearing the session cookie
- Use the existing `requireAuth(db)` middleware for authentication
- _Requirements: 4.1, 4.2, 4.3_
- [x] 1.2 Add `POST /api/auth/change-password` route with rate limiting
- Add `express-rate-limit` middleware scoped to this route: 5 requests per 15-minute window, keyed by `req.cookies.session_id`
- Validate request body has `currentPassword` and `newPassword` fields; return 400 if missing
- Validate `newPassword` is at least 8 characters; return 400 if too short
- Query the user's `password_hash` and `is_active` from the database; return 401 if account is inactive
- Use `bcrypt.compare` to verify `currentPassword`; return 401 if incorrect
- Hash the new password with `bcrypt.hash(newPassword, 10)` and update the `password_hash` column
- Call `logAudit` with action `password_change`, entityType `auth`
- Return `{ message: 'Password changed successfully' }` on success
- Return 429 with appropriate message when rate limit is exceeded
- _Requirements: 2.2, 2.3, 2.7, 2.8, 5.1, 5.2, 5.3, 5.4_
- [x] 1.3 Write property tests for password change round-trip (backend)
- **Property 3: Password change round-trip** — For any valid current password and any new password of 8+ characters, after a successful change, `bcrypt.compare(newPassword, storedHash)` returns true
- **Validates: Requirements 2.2, 2.7**
- [x] 1.4 Write property tests for incorrect password rejection (backend)
- **Property 4: Incorrect current password is always rejected** — For any password string that does not match the user's current password, the endpoint returns 401 and the stored hash remains unchanged
- **Validates: Requirements 2.3**
- [x] 1.5 Write property tests for short password rejection (backend)
- **Property 6 (server-side): Short passwords are rejected** — For any string of length 07, `POST /api/auth/change-password` returns 400 and the stored hash remains unchanged
- **Validates: Requirements 2.5, 5.4**
- [x] 2. Checkpoint — Verify backend routes
- Ensure all tests pass, ask the user if questions arise.
- [x] 3. Create `UserProfilePanel.js` frontend component
- [x] 3.1 Create the `UserProfilePanel` modal component
- Create `frontend/src/components/UserProfilePanel.js`
- Accept `isOpen` and `onClose` props
- On open, fetch `GET /api/auth/profile` with `credentials: 'include'` and display loading state
- Render profile info section showing: username, email, group, created_at (formatted), last_login (formatted)
- Use dark theme inline styles matching the design system (intel-card gradient background, accent borders, light text colors from `DESIGN_SYSTEM.md`)
- Include a close button (X icon from lucide-react) and click-outside-to-close behavior
- Display error state with retry option if profile fetch fails
- Use icons from lucide-react (User, Mail, Shield, Calendar, Clock)
- _Requirements: 1.1, 1.2, 1.3, 1.4_
- [x] 3.2 Add password change form to `UserProfilePanel`
- Add a password change section below the profile info with three fields: current password, new password, confirm new password
- Implement client-side validation: new password must match confirm password; new password must be >= 8 characters
- Display inline validation errors below the relevant fields
- On submit, call `POST /api/auth/change-password` with `{ currentPassword, newPassword }`
- Handle API error responses (401 wrong password, 429 rate limited, 400 validation, 500 server error) and display appropriate messages
- On success, show success message and clear all form fields
- Style the form with dark theme inline styles (intel-input styling, intel-button-primary for submit)
- _Requirements: 2.1, 2.2, 2.3, 2.4, 2.5, 2.6_
- [x] 3.3 Write property test for profile panel field rendering
- **Property 1: Profile panel displays all required fields** — For any valid profile object with arbitrary username, email, group, created_at, and last_login values, rendering UserProfilePanel displays all five values in the output
- **Validates: Requirements 1.2**
- [x] 3.4 Write property test for mismatched password confirmation
- **Property 5: Mismatched password confirmation is rejected client-side** — For any two distinct strings used as newPassword and confirmPassword, the form displays a validation error and does not submit a request
- **Validates: Requirements 2.4**
- [x] 3.5 Write property test for short password client-side rejection
- **Property 6 (client-side): Short passwords are rejected** — For any string of length 07, the form displays a minimum-length validation error and does not submit a request
- **Validates: Requirements 2.5**
- [x] 4. Checkpoint — Verify frontend component
- Ensure all tests pass, ask the user if questions arise.
- [x] 5. Modify `UserMenu.js` for dark theming and profile integration
- [x] 5.1 Convert `UserMenu.js` from light theme to dark theme
- Replace Tailwind light-theme classes with inline dark-theme style objects
- Button: `hover:bg-gray-100``rgba(14, 165, 233, 0.1)` hover background
- Username text: `text-gray-900``#F8FAFC` (design system `--text-primary`)
- Group label text: `text-gray-500``#E2E8F0` (design system `--text-secondary`)
- Chevron icon: `text-gray-500``#E2E8F0`
- Dropdown panel: `bg-white` → intel-card gradient background; `border-gray-200``rgba(14, 165, 233, 0.3)`
- Dropdown items: `text-gray-700``#F8FAFC`; `hover:bg-gray-50``rgba(14, 165, 233, 0.1)`
- Sign out text: `text-red-600``#F87171`; `hover:bg-red-50``rgba(239, 68, 68, 0.1)`
- Dropdown header: `text-gray-900``#F8FAFC`; `text-gray-500``#94A3B8`; `border-gray-100``rgba(14, 165, 233, 0.2)`
- Retain existing group badge color-coding logic
- _Requirements: 3.1, 3.2, 3.3, 3.4, 6.1, 6.2, 6.3, 6.4, 6.5_
- [x] 5.2 Add "My Profile" menu item and wire `UserProfilePanel`
- Import `UserProfilePanel` component
- Add `showProfile` state variable
- Add a "My Profile" menu item with `User` icon between the dropdown header and the admin-only actions
- On click, close the dropdown and set `showProfile` to `true`
- Render `<UserProfilePanel isOpen={showProfile} onClose={() => setShowProfile(false)} />` in the component output
- _Requirements: 1.1, 1.3_
- [x] 5.3 Write property test for profile API data completeness
- **Property 2: Profile API returns complete user data matching database** — For any active user record, a GET request to `/api/auth/profile` with that user's valid session returns an object with `id`, `username`, `email`, `group`, `created_at`, and `last_login` fields matching the database
- **Validates: Requirements 4.1**
- [x] 6. Final checkpoint — Ensure all tests pass
- Ensure all tests pass, ask the user if questions arise.
## Notes
- Tasks marked with `*` are optional and can be skipped for faster MVP
- Each task references specific requirements for traceability
- Checkpoints ensure incremental validation
- Property tests use `fast-check` (already installed in `frontend/package.json` as a devDependency)
- Backend tests for properties 3, 4, and 6 (server-side) will need `fast-check` added to the root `package.json` devDependencies
- The existing `express-rate-limit` package (already in root `package.json`) is used for the password change rate limiter
- No database migrations are needed — the existing `users` table has all required columns
- All styling follows the dark theme design system documented in `DESIGN_SYSTEM.md`