from datetime import timedelta from django.utils import timezone from infrasynth.security.models import TwoFactorConfig from infrasynth.security.two_factor.middleware import TwoFactorMiddleware from infrasynth.security.two_factor.utils import ( generate_pre_auth_token, get_pre_auth_expiry, ) class TestPreAuthUtils: def test_generate_pre_auth_token_length(self): token = generate_pre_auth_token() assert len(token) == 64 def test_generate_pre_auth_token_unique(self): assert generate_pre_auth_token() != generate_pre_auth_token() def test_get_pre_auth_expiry(self): before = timezone.now() expiry = get_pre_auth_expiry(minutes=5) after = timezone.now() assert before + timedelta(minutes=5) <= expiry <= after + timedelta(minutes=5) class _Request: def __init__(self, user, path="/api/v1/features/active/"): self.user = user self.path = path class TestTwoFactorMiddleware: def test_anonymous_passes(self, rf, db): request = rf.get("/api/v1/features/active/") request.user = type("Anon", (), {"is_authenticated": False})() result = TwoFactorMiddleware(get_response=lambda r: None).process_request(request) assert result is None def test_user_without_config_passes(self, rf, user): request = rf.get("/api/v1/features/active/") request.user = user result = TwoFactorMiddleware(get_response=lambda r: None).process_request(request) assert result is None def test_disabled_config_passes(self, rf, user): TwoFactorConfig.objects.create(user=user, is_enabled=False, is_configured=True) request = rf.get("/api/v1/features/active/") request.user = user result = TwoFactorMiddleware(get_response=lambda r: None).process_request(request) assert result is None def test_unconfigured_enabled_passes(self, rf, user): TwoFactorConfig.objects.create(user=user, is_enabled=True, is_configured=False) request = rf.get("/api/v1/features/active/") request.user = user result = TwoFactorMiddleware(get_response=lambda r: None).process_request(request) assert result is None def test_api_path_returns_403_when_2fa_required(self, rf, user): TwoFactorConfig.objects.create(user=user, is_enabled=True, is_configured=True) request = rf.get("/api/v1/features/active/") request.user = user result = TwoFactorMiddleware(get_response=lambda r: None).process_request(request) assert result is not None assert result.status_code == 403 import json as _json assert _json.loads(result.content)["code"] == "AUTH_2FA_REQUIRED" def test_non_api_path_redirects(self, rf, user): TwoFactorConfig.objects.create(user=user, is_enabled=True, is_configured=True) request = rf.get("/some-page/") request.user = user result = TwoFactorMiddleware(get_response=lambda r: None).process_request(request) assert result is not None assert result.status_code == 302 assert result.url == "/api/v1/auth/2fa/verify/" def test_jwt_authenticated_request_passes(self, rf, user): TwoFactorConfig.objects.create(user=user, is_enabled=True, is_configured=True) request = rf.get("/api/v1/features/active/") request.user = user request.auth = object() # a minted token implies 2FA already happened result = TwoFactorMiddleware(get_response=lambda r: None).process_request(request) assert result is None def test_exempt_paths_not_redirected(self, rf, user): TwoFactorConfig.objects.create(user=user, is_enabled=True, is_configured=True) request = rf.get("/api/v1/auth/check/") request.user = user result = TwoFactorMiddleware(get_response=lambda r: None).process_request(request) assert result is None def test_admin_path_not_redirected(self, rf, user): TwoFactorConfig.objects.create(user=user, is_enabled=True, is_configured=True) request = rf.get("/admin/") request.user = user result = TwoFactorMiddleware(get_response=lambda r: None).process_request(request) assert result is None def test_verified_session_not_blocked(self, rf, user): TwoFactorConfig.objects.create(user=user, is_enabled=True, is_configured=True) request = rf.get("/api/v1/features/active/") request.user = user request.session = {"_2fa_verified": True} result = TwoFactorMiddleware(get_response=lambda r: None).process_request(request) assert result is None