From 390a693ec6500317a253ee2252082a5f340cc586 Mon Sep 17 00:00:00 2001 From: curo1305 Date: Sat, 30 May 2026 11:37:59 +0200 Subject: [PATCH] feat(05-11): add UserDeleteConfirm model + admin password verification in delete_user - Import verify_password from services.auth - Add UserDeleteConfirm Pydantic model (admin_password field) - delete_user handler now requires body; fails fast with 403 on wrong password - All existing SEC-09 cloud/MinIO purge logic and audit log unchanged - Three new tests pass: 204 on correct pw, 403 on wrong pw, 422 on no body --- backend/api/admin.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/backend/api/admin.py b/backend/api/admin.py index a1aea53..7818db7 100644 --- a/backend/api/admin.py +++ b/backend/api/admin.py @@ -37,7 +37,7 @@ from db.models import CloudConnection, Document, Quota, RefreshToken, Topic, Use from deps.auth import get_current_admin from deps.db import get_db from services.audit import write_audit_log -from services.auth import hash_password, revoke_all_refresh_tokens +from services.auth import hash_password, revoke_all_refresh_tokens, verify_password from storage import get_storage_backend, get_storage_backend_for_document router = APIRouter(prefix="/api/admin", tags=["admin"]) @@ -138,6 +138,12 @@ class SystemTopicCreate(BaseModel): color: str = "#6366f1" +class UserDeleteConfirm(BaseModel): + """Admin password confirmation required before hard-deleting a user (ADMIN-02, T-05-11-01).""" + + admin_password: str + + # ── SEC-08: Safe CloudConnection response model ─────────────────────────────── class CloudConnectionOut(BaseModel): @@ -472,6 +478,7 @@ async def update_ai_config( @router.delete("/users/{user_id}", status_code=status.HTTP_204_NO_CONTENT) async def delete_user( user_id: uuid.UUID, + body: UserDeleteConfirm, request: Request, session: AsyncSession = Depends(get_db), _admin: User = Depends(get_current_admin), @@ -479,11 +486,20 @@ async def delete_user( """Delete a user account and clean up all their MinIO objects (SEC-09, D-19). Security invariants: + - Admin password verified via Argon2 before any deletion (T-05-11-01) - Cannot delete admin accounts (T-04-07-04) - MinIO objects are deleted BEFORE DB records are removed (SEC-09) - MinIO deletion is best-effort (try/except) — DB row is deleted regardless - Audit log written with event_type="admin.user_deleted" """ + # T-05-11-01: Verify admin password before performing any destructive action. + # Fail fast — no DB reads for the target user until the admin is confirmed. + if not verify_password(body.admin_password, _admin.password_hash): + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail="Invalid admin password", + ) + user = await session.get(User, user_id) if user is None: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="User not found")