From backend/api/admin.py:
- Existing delete_user handler: DELETE /api/admin/users/{user_id}, status 204, no request body
- Already purges cloud connections + MinIO objects, writes audit log (SEC-09 — do NOT change this logic)
- Uses Depends(get_current_admin) → resolves to User ORM instance as `_admin`
- verify_password not currently imported; services.auth exports it: `from services.auth import verify_password`
- The handler must add: parse body as UserDeleteConfirm, call verify_password(body.admin_password, _admin.password_hash), raise 403 on failure
From services/auth.py (existing pattern from admin.py imports):
- `hash_password(plain: str) -> str`
- `verify_password(plain: str, hashed: str) -> bool` — uses pwdlib Argon2
From backend/tests/test_admin_api.py:
- `admin_client` fixture at line 71 returns `(client, admin, session)` tuple
- Admin user plaintext password: "AdminPass1!Secret"
- Use this fixture for all three new tests — do NOT recreate admin users manually
From frontend/src/components/admin/AdminUsersTab.vue (confirmDeactivate pattern to mirror):
- `confirmDeactivate = ref(null)` tracks which user ID is awaiting confirmation
- `startDeactivate(id)` sets confirmDeactivate = id
- Inline panel in | renders when `confirmDeactivate === user.id`
- Panel has confirm + cancel buttons
- Model to follow: add parallel state `confirmDelete = ref(null)`, `deletePassword = ref('')`, `deleteError = ref(null)`
From frontend/src/api/client.js:
- All admin functions follow: request(`/api/admin/users/${id}/...`, { method, headers, body })
- DELETE with body: `request(\`/api/admin/users/${id}\`, { method: "DELETE", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ admin_password: adminPassword }) })`
Task 1: Backend — UserDeleteConfirm model + password verification in delete_user
backend/api/admin.py, backend/tests/test_admin_api.py
- DELETE /api/admin/users/{id} with correct admin_password in body returns 204 and user is deleted.
- DELETE /api/admin/users/{id} with wrong admin_password returns 403 {"detail": "Invalid admin password"} and user is NOT deleted.
- DELETE /api/admin/users/{id} with no body returns 422 (Pydantic validation).
- Cannot delete admin accounts (existing guard: 400 "Cannot delete admin accounts") — unchanged.
- Cannot delete non-existent user (existing guard: 404) — unchanged.
- Audit log entry written for successful delete (existing code) — unchanged.
- Cloud credentials purged before DB delete (existing SEC-09 code) — unchanged.
In backend/api/admin.py:
1. Add `UserDeleteConfirm` Pydantic model in the Request models section:
```python
class UserDeleteConfirm(BaseModel):
admin_password: str
```
2. Add `from services.auth import verify_password` to the existing imports from services.auth (currently imports `hash_password, revoke_all_refresh_tokens`).
3. Modify the `delete_user` handler signature to accept the body:
- Change `async def delete_user(user_id, request, session, _admin)` to also accept `body: UserDeleteConfirm`.
- FastAPI will parse the JSON body automatically.
4. Add password verification BEFORE any deletion logic (fail fast):
```python
if not verify_password(body.admin_password, _admin.password_hash):
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Invalid admin password",
)
```
5. All existing deletion logic (cloud purge, MinIO purge, audit log, session.delete) is unchanged.
In backend/tests/test_admin_api.py, add three tests using the existing `admin_client` fixture (line 71, returns `(client, admin, session)`, admin password is "AdminPass1!Secret"):
1. `test_delete_user_correct_password` — use admin_client fixture, create a regular user, call DELETE with `{"admin_password": "AdminPass1!Secret"}`, assert 204, assert user no longer in GET /admin/users.
2. `test_delete_user_wrong_password` — same setup, call DELETE with `{"admin_password": "WrongPass!"}`, assert 403, assert user still in GET /admin/users (not deleted).
3. `test_delete_user_no_body` — call DELETE with no body (or empty body {}), assert 422.
cd /Users/nik/Documents/Progamming/document_scanner/backend && python -m pytest tests/test_admin_api.py::test_delete_user_correct_password tests/test_admin_api.py::test_delete_user_wrong_password tests/test_admin_api.py::test_delete_user_no_body -v
Three tests pass. Delete with correct password returns 204. Delete with wrong password returns 403 and user survives. Delete with no body returns 422.
Task 2: Frontend — adminDeleteUser API function + inline delete confirmation panel
frontend/src/api/client.js, frontend/src/components/admin/AdminUsersTab.vue
### 1. client.js — add adminDeleteUser
Export `adminDeleteUser(id, adminPassword)`:
```javascript
export function adminDeleteUser(id, adminPassword) {
return request(`/api/admin/users/${id}`, {
method: 'DELETE',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ admin_password: adminPassword }),
})
}
```
### 2. AdminUsersTab.vue — add delete confirmation state
In ` |