# Copyright (c) 2025 Patrick Motsch # All rights reserved. """ Unit tests for modules.auth.mfaService. Tests TOTP generation, verification, encryption round-trip, and the three-rule MFA obligation resolver. """ import pytest from unittest.mock import patch, MagicMock import pyotp from modules.auth.mfaService import ( _generateSecret, buildTotp, generateSetup, confirmSetup, verifyCode, isMfaRequired, _isMfaRequireAdminsEnabled, ) class TestTotpBasics: def test_generateSecret_returns_base32(self): secret = _generateSecret() assert isinstance(secret, str) assert len(secret) >= 16 def testbuildTotp_generates_valid_code(self): secret = _generateSecret() totp = buildTotp(secret) code = totp.now() assert len(code) == 6 assert code.isdigit() def test_verifyCode_accepts_current_code(self): secret = _generateSecret() totp = buildTotp(secret) code = totp.now() encrypted = f"FAKE_ENC:{secret}" with patch("modules.auth.mfaService.decryptSecret", return_value=secret): assert verifyCode(encrypted, code) is True def test_verifyCode_rejects_wrong_code(self): secret = _generateSecret() encrypted = f"FAKE_ENC:{secret}" with patch("modules.auth.mfaService.decryptSecret", return_value=secret): assert verifyCode(encrypted, "000000") is False class TestGenerateSetup: @patch("modules.auth.mfaService._encryptSecret", return_value="ENC_SECRET") def test_returns_uri_and_encrypted_secret(self, _mock_enc): result = generateSetup(userId="u1", username="testuser") assert "encryptedSecret" in result assert "provisioningUri" in result assert result["encryptedSecret"] == "ENC_SECRET" assert "otpauth://totp/" in result["provisioningUri"] assert "PowerOn" in result["provisioningUri"] class TestConfirmSetup: def test_confirmSetup_with_valid_code(self): secret = _generateSecret() totp = buildTotp(secret) code = totp.now() with patch("modules.auth.mfaService.decryptSecret", return_value=secret): assert confirmSetup("ENC", code) is True def test_confirmSetup_with_invalid_code(self): secret = _generateSecret() with patch("modules.auth.mfaService.decryptSecret", return_value=secret): assert confirmSetup("ENC", "999999") is False def test_confirmSetup_handles_decryption_error(self): with patch("modules.auth.mfaService.decryptSecret", side_effect=Exception("decrypt error")): assert confirmSetup("BAD_ENC", "123456") is False class TestIsMfaRequired: def _makeUser(self, mfaEnabled=False, isSysAdmin=False, isPlatformAdmin=False): u = MagicMock() u.mfaEnabled = mfaEnabled u.isSysAdmin = isSysAdmin u.isPlatformAdmin = isPlatformAdmin return u def _makeMandate(self, mfaRequired=False): m = MagicMock() m.mfaRequired = mfaRequired return m def test_mfaEnabled_user_always_required(self): user = self._makeUser(mfaEnabled=True) assert isMfaRequired(user) is True @patch("modules.auth.mfaService._isMfaRequireAdminsEnabled", return_value=True) def test_sysadmin_with_config_key(self, _mock): user = self._makeUser(isSysAdmin=True) assert isMfaRequired(user) is True @patch("modules.auth.mfaService._isMfaRequireAdminsEnabled", return_value=True) def test_platformadmin_with_config_key(self, _mock): user = self._makeUser(isPlatformAdmin=True) assert isMfaRequired(user) is True @patch("modules.auth.mfaService._isMfaRequireAdminsEnabled", return_value=False) def test_admin_without_config_key_not_required(self, _mock): user = self._makeUser(isSysAdmin=True) assert isMfaRequired(user) is False @patch("modules.auth.mfaService._isMfaRequireAdminsEnabled", return_value=False) def test_mandate_with_mfaRequired(self, _mock): user = self._makeUser() mandate = self._makeMandate(mfaRequired=True) assert isMfaRequired(user, mandates=[mandate]) is True @patch("modules.auth.mfaService._isMfaRequireAdminsEnabled", return_value=False) def test_mandate_without_mfaRequired(self, _mock): user = self._makeUser() mandate = self._makeMandate(mfaRequired=False) assert isMfaRequired(user, mandates=[mandate]) is False @patch("modules.auth.mfaService._isMfaRequireAdminsEnabled", return_value=False) def test_regular_user_no_mandate_not_required(self, _mock): user = self._makeUser() assert isMfaRequired(user) is False class TestConfigKey: @patch("modules.auth.mfaService.APP_CONFIG") def test_config_true(self, mock_cfg): mock_cfg.get.return_value = "true" assert _isMfaRequireAdminsEnabled() is True @patch("modules.auth.mfaService.APP_CONFIG") def test_config_false(self, mock_cfg): mock_cfg.get.return_value = "false" assert _isMfaRequireAdminsEnabled() is False @patch("modules.auth.mfaService.APP_CONFIG") def test_config_empty(self, mock_cfg): mock_cfg.get.return_value = "" assert _isMfaRequireAdminsEnabled() is False @patch("modules.auth.mfaService.APP_CONFIG") def test_config_one(self, mock_cfg): mock_cfg.get.return_value = "1" assert _isMfaRequireAdminsEnabled() is True