docs(05-11): complete admin hard-delete with password confirmation plan
- UserDeleteConfirm Pydantic model + Argon2 password verification in delete_user - adminDeleteUser(id, adminPassword) exported from client.js - AdminUsersTab inline delete confirmation panel with password field - Three new tests pass: 204/403/422 scenarios - Full 21-test admin suite green; frontend build clean
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
---
|
||||
phase: 05-cloud-storage-backends
|
||||
plan: 11
|
||||
subsystem: admin
|
||||
tags: [admin, security, delete, password-verification, frontend]
|
||||
dependency_graph:
|
||||
requires: []
|
||||
provides: [admin-hard-delete-with-password-confirmation]
|
||||
affects: [backend/api/admin.py, frontend/src/components/admin/AdminUsersTab.vue]
|
||||
tech_stack:
|
||||
added: []
|
||||
patterns: [Pydantic body model for DELETE, Argon2 password verification before destructive action, Vue inline confirmation panel]
|
||||
key_files:
|
||||
created: []
|
||||
modified:
|
||||
- backend/api/admin.py
|
||||
- frontend/src/api/client.js
|
||||
- frontend/src/components/admin/AdminUsersTab.vue
|
||||
- backend/tests/test_admin_api.py
|
||||
decisions:
|
||||
- "Password verification added as fail-fast check before user lookup — admin cannot fish for user existence via timing"
|
||||
- "Delete panel and deactivate panel are mutually exclusive (each clears the other on open)"
|
||||
- "Tests added to existing test_admin_api.py (not a separate file) — plan referenced test_admin.py but actual file is test_admin_api.py"
|
||||
metrics:
|
||||
duration: "2m"
|
||||
completed_date: "2026-05-30T09:39:26Z"
|
||||
tasks_completed: 2
|
||||
files_modified: 4
|
||||
requirements: [ADMIN-02, SEC-09]
|
||||
---
|
||||
|
||||
# Phase 05 Plan 11: Admin Hard-Delete with Password Confirmation Summary
|
||||
|
||||
Admin users can now permanently delete non-admin user accounts with Argon2 password verification — wrong or missing password returns 403 without touching any data; correct password triggers the existing SEC-09 cloud/MinIO purge pipeline.
|
||||
|
||||
## Tasks Completed
|
||||
|
||||
| Task | Name | Commits | Files |
|
||||
|------|------|---------|-------|
|
||||
| 1 (RED) | Failing tests for delete_user password verification | 8727592 | backend/tests/test_admin_api.py |
|
||||
| 1 (GREEN) | UserDeleteConfirm model + password verification | 390a693 | backend/api/admin.py |
|
||||
| 2 | adminDeleteUser API + inline delete confirmation panel | 7268721 | frontend/src/api/client.js, frontend/src/components/admin/AdminUsersTab.vue |
|
||||
|
||||
## What Was Built
|
||||
|
||||
**Backend (admin.py):**
|
||||
- `UserDeleteConfirm` Pydantic model with `admin_password: str` field added to the Request models section
|
||||
- `verify_password` imported from `services.auth` (alongside existing `hash_password`, `revoke_all_refresh_tokens`)
|
||||
- `delete_user` handler signature updated to accept `body: UserDeleteConfirm`
|
||||
- Fail-fast password check placed before any DB reads for the target user — 403 "Invalid admin password" on failure
|
||||
- All existing SEC-09 cloud credential purge, MinIO object cleanup, and audit log logic is unchanged
|
||||
|
||||
**Frontend (client.js):**
|
||||
- `adminDeleteUser(id, adminPassword)` exported — calls `DELETE /api/admin/users/{id}` with `{ admin_password }` JSON body
|
||||
|
||||
**Frontend (AdminUsersTab.vue):**
|
||||
- Added state: `confirmDelete`, `deletePassword`, `deleteError`
|
||||
- Added functions: `startDelete`, `cancelDelete`, `confirmDoDelete`
|
||||
- `startDeactivate` updated to clear delete panel when deactivate panel opens (mutual exclusion)
|
||||
- Delete button added to active user row (after Deactivate) and deactivated user row (after Reactivate)
|
||||
- Inline password confirmation panel: warning text, password input with Enter shortcut, error display, "Delete permanently" / Cancel buttons with loading spinner
|
||||
|
||||
## Verification
|
||||
|
||||
- `pytest test_admin_api.py::test_delete_user_correct_password` — PASSED (204, user removed from list)
|
||||
- `pytest test_admin_api.py::test_delete_user_wrong_password` — PASSED (403, user survives)
|
||||
- `pytest test_admin_api.py::test_delete_user_no_body` — PASSED (422, Pydantic validation)
|
||||
- Full `test_admin_api.py` suite — 21/21 PASSED
|
||||
- `npm run build` — zero errors, built in 689ms
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Minor Filename Discrepancy (auto-handled)
|
||||
|
||||
**Found during:** Task 1
|
||||
**Issue:** Plan references `backend/tests/test_admin.py` but the actual file is `backend/tests/test_admin_api.py`
|
||||
**Fix:** Tests added to `backend/tests/test_admin_api.py` (the existing correct file)
|
||||
**Impact:** None — tests run and pass correctly
|
||||
|
||||
## TDD Gate Compliance
|
||||
|
||||
- RED gate: commit `8727592` — `test(05-11): add failing tests for delete_user password verification`
|
||||
- GREEN gate: commit `390a693` — `feat(05-11): add UserDeleteConfirm model + admin password verification in delete_user`
|
||||
- 2/2 tests failed in RED phase (correct_password passed because old endpoint had no auth check; wrong_password and no_body failed correctly)
|
||||
|
||||
## Threat Surface Scan
|
||||
|
||||
No new network endpoints introduced. The DELETE `/api/admin/users/{id}` endpoint existed before this plan. Changes add a body requirement (reducing attack surface — anonymous DELETE calls now return 422 instead of 204). No new trust boundaries.
|
||||
|
||||
## Known Stubs
|
||||
|
||||
None — adminDeleteUser wired directly to the backend endpoint; delete panel uses live API with real error propagation.
|
||||
|
||||
## Self-Check: PASSED
|
||||
|
||||
- `backend/api/admin.py` — modified, contains `UserDeleteConfirm` and `verify_password` check
|
||||
- `frontend/src/api/client.js` — modified, exports `adminDeleteUser`
|
||||
- `frontend/src/components/admin/AdminUsersTab.vue` — modified, contains delete panel
|
||||
- `backend/tests/test_admin_api.py` — modified, contains 3 new tests
|
||||
- Commits 8727592, 390a693, 7268721 — all present in git log
|
||||
Reference in New Issue
Block a user