diff --git a/kobo/apps/audit_log/tests/test_models.py b/kobo/apps/audit_log/tests/test_models.py index 94cb8f1a93..ce0b0b16fa 100644 --- a/kobo/apps/audit_log/tests/test_models.py +++ b/kobo/apps/audit_log/tests/test_models.py @@ -17,7 +17,7 @@ @patch('kobo.apps.audit_log.models.get_human_readable_client_user_agent', return_value='source') @patch('kobo.apps.audit_log.models.get_client_ip', return_value='127.0.0.1') -class AuditLogTestCase(BaseTestCase): +class AuditLogModelTestCase(BaseTestCase): @classmethod def setUpClass(cls): @@ -47,16 +47,16 @@ def _check_common_fields(self, audit_log: AuditLog, user): def test_basic_create_auth_log_from_request(self, patched_ip, patched_source): request = self._create_request( - '/accounts/login/', AnonymousUser(), AuditLogTestCase.super_user + '/accounts/login/', AnonymousUser(), AuditLogModelTestCase.super_user ) log: AuditLog = AuditLog.create_access_log_for_request(request) - self._check_common_fields(log, AuditLogTestCase.super_user) + self._check_common_fields(log, AuditLogModelTestCase.super_user) self.assertDictEqual( log.metadata, { 'ip_address': '127.0.0.1', 'source': 'source', - 'auth_type': AuditLogTestCase.super_user.backend, + 'auth_type': AuditLogModelTestCase.super_user.backend, }, ) @@ -67,7 +67,7 @@ def test_create_auth_log_from_loginas_request(self, patched_ip, patched_source): second_user.save() request = self._create_request( f'/admin/login/user/{second_user.id}/', - AuditLogTestCase.super_user, + AuditLogModelTestCase.super_user, second_user, ) log: AuditLog = AuditLog.create_access_log_for_request(request) @@ -78,19 +78,19 @@ def test_create_auth_log_from_loginas_request(self, patched_ip, patched_source): 'ip_address': '127.0.0.1', 'source': 'source', 'auth_type': LOGINAS_AUTH_TYPE, - 'initial_user_uid': AuditLogTestCase.super_user.extra_details.uid, - 'initial_user_username': AuditLogTestCase.super_user.username, + 'initial_user_uid': AuditLogModelTestCase.super_user.extra_details.uid, + 'initial_user_username': AuditLogModelTestCase.super_user.username, }, ) def test_create_auth_log_with_different_auth_type(self, patched_ip, patched_source): request = self._create_request( - '/api/v2/assets/', AnonymousUser(), AuditLogTestCase.super_user + '/api/v2/assets/', AnonymousUser(), AuditLogModelTestCase.super_user ) log: AuditLog = AuditLog.create_access_log_for_request( request, authentication_type='Token' ) - self._check_common_fields(log, AuditLogTestCase.super_user) + self._check_common_fields(log, AuditLogModelTestCase.super_user) self.assertDictEqual( log.metadata, { @@ -107,7 +107,7 @@ def test_create_auth_log_unknown_authenticator(self, patched_ip, patched_source) ) second_user.save() request = self._create_request( - f'/api/v2/assets/', AuditLogTestCase.super_user, second_user + f'/api/v2/assets/', AuditLogModelTestCase.super_user, second_user ) log: AuditLog = AuditLog.create_access_log_for_request(request) self._check_common_fields(log, second_user) diff --git a/kobo/apps/audit_log/tests/test_signals.py b/kobo/apps/audit_log/tests/test_signals.py index 594909ec0c..7fb84b237b 100644 --- a/kobo/apps/audit_log/tests/test_signals.py +++ b/kobo/apps/audit_log/tests/test_signals.py @@ -3,18 +3,20 @@ from allauth.account.models import EmailAddress from django.contrib.auth import get_user_model from django.urls import resolve, reverse +from trench.utils import get_mfa_model +from kobo.apps.accounts.mfa.forms import MfaTokenForm from kobo.apps.audit_log.models import AuditAction, AuditLog from kpi.tests.base_test_case import BaseTestCase -class AuditLogTestCase(BaseTestCase): +class AuditLogSignalsTestCase(BaseTestCase): """ Class for testing that logins produce AuditLogs. Here we just test that AuditLogs are produced, not necessarily what they contain. More tests for what they contain - are in test_models.py. Also, AuditLogs for more complicated login flows are tested as part of the tests for those - flows to avoid copying lots of complicated setup. + are in test_models.py. Also, AuditLogs for SSO logins are tested as part of the SSO tests + to avoid copying lots of complicated setup. """ @classmethod @@ -37,7 +39,7 @@ def test_audit_log_created_on_login(self, patched_create): def test_simple_login(self): count = AuditLog.objects.count() self.assertEqual(count, 0) - user = AuditLogTestCase.user + user = AuditLogSignalsTestCase.user data = { 'login': 'user', 'password': 'pass', @@ -52,7 +54,7 @@ def test_simple_login(self): self.assertEqual(audit_log.action, AuditAction.AUTH) def test_login_with_email_verification(self): - user = AuditLogTestCase.user + user = AuditLogSignalsTestCase.user data = { 'login': 'user', 'password': 'pass', @@ -68,3 +70,35 @@ def test_login_with_email_verification(self): audit_log = AuditLog.objects.first() self.assertEqual(audit_log.user.id, user.id) self.assertEqual(audit_log.action, AuditAction.AUTH) + + def test_mfa_login(self): + mfa_object = get_mfa_model().objects.create( + user=AuditLogSignalsTestCase.user, + secret='dummy_mfa_secret', + name='app', + is_primary=True, + is_active=True, + _backup_codes='dummy_encoded_codes', + ) + mfa_object.save() + email_address, _ = EmailAddress.objects.get_or_create( + user=AuditLogSignalsTestCase.user + ) + email_address.primary = True + email_address.verified = True + email_address.save() + data = { + 'login': 'user', + 'password': 'pass', + } + self.client.post(reverse('kobo_login'), data=data, follow=True) + # no audit log should be created yet because the MFA code hasn't been entered + self.assertEqual(AuditLog.objects.count(), 0) + + with patch('kobo.apps.accounts.mfa.forms.authenticate_second_step_command', + return_value=AuditLogSignalsTestCase.user): + self.client.post(reverse('mfa_token'), data={'code': '123456', 'ephemeral_token': 'dummy'}, follow=True) + self.assertEqual(AuditLog.objects.count(), 1) + audit_log = AuditLog.objects.first() + self.assertEqual(audit_log.user.id, AuditLogSignalsTestCase.user.id) + self.assertEqual(audit_log.action, AuditAction.AUTH)