Безопасность довёл до ума — Cursor-генерацию переписал руками. IDOR закрыл, CSRF задушил, refresh rotation теперь как надо. HSTS на staging, ENABLE_DOCS=false, install.env recovery протестил. Backend: - jwt_denylist + auth_epoch: мгновенный revoke access JWT (logout/block/reset) - auth/admin/users: bump epoch, logout с Bearer, forgot_password skip для blocked - install_secrets: путь всегда apps/api/data/secrets/ (bootstrap из корня не ломает Docker) - seed: SEED_DEMO_USERS=false на prod/staging - тесты: jwt revoke, integration, coverage gate 90% Frontend: - logout шлёт Bearer, обработка TOKEN_REVOKED - guards TypeScript fix - E2E: blocked user → 401 сразу после block Infra: - staging/prod compose, TLS nginx, deploy-скрипты - k6 §17.2, backup/health/smoke scripts Docs: - docs/ на русском: project, security, deploy, release (старые md слили) - README короткий + план ТЗ + стандартные логины dev Код готов к плаванию. Капитан может идти писать фронт.
41 lines
1.4 KiB
Python
41 lines
1.4 KiB
Python
from __future__ import annotations
|
|
|
|
from unittest.mock import patch
|
|
|
|
import pytest
|
|
|
|
from app.core.security import hash_password
|
|
from app.modules.auth import repository as auth_repository
|
|
from app.modules.auth.service import issue_refresh_token, refresh
|
|
from app.modules.users.repository import create_user, get_user_by_id, update_user
|
|
|
|
|
|
def test_refresh_returns_account_blocked_for_blocked_user():
|
|
user = create_user("blocked-svc@example.com", hash_password("Valid123"), status="active")
|
|
token = issue_refresh_token(user.id)
|
|
user.status = "blocked"
|
|
update_user(user)
|
|
auth_repository.revoke_user_families(user.id)
|
|
|
|
with pytest.raises(PermissionError, match="ACCOUNT_BLOCKED"):
|
|
refresh(token)
|
|
|
|
|
|
def test_refresh_returns_email_not_verified_for_pending_user():
|
|
user = create_user("pending-svc@example.com", hash_password("Valid123"), status="pending")
|
|
token = issue_refresh_token(user.id)
|
|
|
|
with pytest.raises(PermissionError, match="EMAIL_NOT_VERIFIED"):
|
|
refresh(token)
|
|
|
|
|
|
def test_refresh_rejects_revoked_token_for_active_user():
|
|
user = create_user("active-svc@example.com", hash_password("Valid123"), status="active")
|
|
token = issue_refresh_token(user.id)
|
|
auth_repository.revoke_user_families(user.id)
|
|
|
|
with patch.object(auth_repository, "revoke_family_tokens") as revoke_family:
|
|
with pytest.raises(PermissionError, match="INVALID_REFRESH"):
|
|
refresh(token)
|
|
revoke_family.assert_called_once()
|