feat(02-04): implement admin API endpoints — user CRUD, quota management, AI config
- GET /api/admin/users: list users (safe fields only, ordered by created_at)
- POST /api/admin/users: create user (password_must_change=True, quota init)
- PATCH /api/admin/users/{id}/status: deactivate/reactivate with sole-admin guard
- POST /api/admin/users/{id}/password-reset: Celery email dispatch (no token returned)
- GET /api/admin/users/{id}/quota: quota view with MB helpers
- PATCH /api/admin/users/{id}/quota: quota adjust with below-usage warning
- PATCH /api/admin/users/{id}/ai-config: assign AI provider/model per user
- _user_to_dict() whitelist helper prevents password_hash/credentials_enc leakage
- No impersonation endpoint (ADMIN-07 enforced by omission)
- get_current_admin Depends() on every handler (SEC-07)
- Updated backend/main.py to include admin_router
- Fixed test: mock send_reset_email.delay to avoid Redis in unit tests
This commit is contained in:
@@ -213,10 +213,19 @@ async def test_cannot_deactivate_only_admin(admin_client):
|
||||
@pytest.mark.asyncio
|
||||
async def test_password_reset_initiates_email(admin_client):
|
||||
"""POST /api/admin/users/{id}/password-reset → 202; no token returned."""
|
||||
from unittest.mock import patch
|
||||
client, _admin, session = admin_client
|
||||
target = await make_regular_user(session)
|
||||
|
||||
resp = await client.post(f"/api/admin/users/{target.id}/password-reset")
|
||||
# Mock Celery task to avoid Redis connection in unit tests
|
||||
with patch("tasks.email_tasks.send_reset_email.delay") as mock_delay:
|
||||
resp = await client.post(f"/api/admin/users/{target.id}/password-reset")
|
||||
# Verify email was dispatched (no return of token to caller)
|
||||
mock_delay.assert_called_once()
|
||||
call_args = mock_delay.call_args[0]
|
||||
assert call_args[0] == target.email # sent to the right address
|
||||
assert "token=" in call_args[1] # reset link contains token
|
||||
|
||||
assert resp.status_code == 202
|
||||
data = resp.json()
|
||||
# No token, no impersonation — just a message
|
||||
|
||||
Reference in New Issue
Block a user