--- 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