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