authentik.stages.authenticator_validate.tests.test_sms
Test validator stage
1"""Test validator stage""" 2 3from unittest.mock import MagicMock, patch 4 5from django.test.client import RequestFactory 6from django.urls.base import reverse 7 8from authentik.core.tests.utils import create_test_admin_user, create_test_flow 9from authentik.flows.models import FlowStageBinding, NotConfiguredAction 10from authentik.flows.tests import FlowTestCase 11from authentik.lib.generators import generate_id 12from authentik.stages.authenticator_sms.models import AuthenticatorSMSStage, SMSDevice, SMSProviders 13from authentik.stages.authenticator_validate.models import AuthenticatorValidateStage, DeviceClasses 14from authentik.stages.authenticator_validate.stage import COOKIE_NAME_MFA 15from authentik.stages.identification.models import IdentificationStage, UserFields 16 17 18class AuthenticatorValidateStageSMSTests(FlowTestCase): 19 """Test validator stage""" 20 21 def setUp(self) -> None: 22 self.user = create_test_admin_user() 23 self.request_factory = RequestFactory() 24 self.stage = AuthenticatorSMSStage.objects.create( 25 name="sms", 26 provider=SMSProviders.GENERIC, 27 from_number="1234", 28 ) 29 30 def test_last_auth_threshold(self): 31 """Test last_auth_threshold""" 32 ident_stage = IdentificationStage.objects.create( 33 name=generate_id(), 34 user_fields=[ 35 UserFields.USERNAME, 36 ], 37 ) 38 device: SMSDevice = SMSDevice.objects.create( 39 user=self.user, 40 confirmed=True, 41 stage=self.stage, 42 ) 43 44 stage = AuthenticatorValidateStage.objects.create( 45 name=generate_id(), 46 last_auth_threshold="milliseconds=0", 47 not_configured_action=NotConfiguredAction.CONFIGURE, 48 device_classes=[DeviceClasses.SMS], 49 ) 50 stage.configuration_stages.set([ident_stage]) 51 flow = create_test_flow() 52 FlowStageBinding.objects.create(target=flow, stage=ident_stage, order=0) 53 FlowStageBinding.objects.create(target=flow, stage=stage, order=1) 54 55 response = self.client.post( 56 reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}), 57 {"uid_field": self.user.username}, 58 ) 59 self.assertEqual(response.status_code, 302) 60 device.generate_token() 61 response = self.client.post( 62 reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}), 63 {"code": device.token}, 64 ) 65 self.assertNotIn(COOKIE_NAME_MFA, response.cookies) 66 67 def test_last_auth_threshold_valid(self): 68 """Test last_auth_threshold""" 69 ident_stage = IdentificationStage.objects.create( 70 name=generate_id(), 71 user_fields=[ 72 UserFields.USERNAME, 73 ], 74 ) 75 device: SMSDevice = SMSDevice.objects.create( 76 user=self.user, 77 confirmed=True, 78 stage=self.stage, 79 ) 80 81 stage = AuthenticatorValidateStage.objects.create( 82 name=generate_id(), 83 last_auth_threshold="hours=1", 84 not_configured_action=NotConfiguredAction.CONFIGURE, 85 device_classes=[DeviceClasses.SMS], 86 ) 87 stage.configuration_stages.set([ident_stage]) 88 flow = create_test_flow() 89 FlowStageBinding.objects.create(target=flow, stage=ident_stage, order=0) 90 FlowStageBinding.objects.create(target=flow, stage=stage, order=1) 91 92 response = self.client.post( 93 reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}), 94 {"uid_field": self.user.username}, 95 follow=True, 96 ) 97 self.assertEqual(response.status_code, 200) 98 send_mock = MagicMock() 99 with patch( 100 "authentik.stages.authenticator_sms.models.AuthenticatorSMSStage.send", send_mock 101 ): 102 response = self.client.post( 103 reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}), 104 { 105 "component": "ak-stage-authenticator-validate", 106 "selected_challenge": { 107 "device_class": "sms", 108 "device_uid": str(device.pk), 109 "challenge": {}, 110 "last_used": None, 111 }, 112 }, 113 ) 114 self.assertEqual(send_mock.call_count, 1) 115 device.generate_token() 116 response = self.client.post( 117 reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}), 118 {"code": device.token}, 119 ) 120 self.assertIn(COOKIE_NAME_MFA, response.cookies) 121 self.assertStageResponse(response, component="xak-flow-redirect", to="/") 122 123 def test_sms_hashed(self): 124 """Test hashed SMS device""" 125 ident_stage = IdentificationStage.objects.create( 126 name=generate_id(), 127 user_fields=[ 128 UserFields.USERNAME, 129 ], 130 ) 131 SMSDevice.objects.create( 132 user=self.user, 133 confirmed=True, 134 stage=self.stage, 135 phone_number="hash:foo", 136 ) 137 138 stage = AuthenticatorValidateStage.objects.create( 139 name=generate_id(), 140 last_auth_threshold="hours=1", 141 not_configured_action=NotConfiguredAction.DENY, 142 device_classes=[DeviceClasses.SMS], 143 ) 144 stage.configuration_stages.set([ident_stage]) 145 flow = create_test_flow() 146 FlowStageBinding.objects.create(target=flow, stage=ident_stage, order=0) 147 FlowStageBinding.objects.create(target=flow, stage=stage, order=1) 148 149 response = self.client.post( 150 reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}), 151 {"uid_field": self.user.username}, 152 follow=True, 153 ) 154 self.assertEqual(response.status_code, 200) 155 self.assertStageResponse(response, flow, self.user, component="ak-stage-access-denied")
19class AuthenticatorValidateStageSMSTests(FlowTestCase): 20 """Test validator stage""" 21 22 def setUp(self) -> None: 23 self.user = create_test_admin_user() 24 self.request_factory = RequestFactory() 25 self.stage = AuthenticatorSMSStage.objects.create( 26 name="sms", 27 provider=SMSProviders.GENERIC, 28 from_number="1234", 29 ) 30 31 def test_last_auth_threshold(self): 32 """Test last_auth_threshold""" 33 ident_stage = IdentificationStage.objects.create( 34 name=generate_id(), 35 user_fields=[ 36 UserFields.USERNAME, 37 ], 38 ) 39 device: SMSDevice = SMSDevice.objects.create( 40 user=self.user, 41 confirmed=True, 42 stage=self.stage, 43 ) 44 45 stage = AuthenticatorValidateStage.objects.create( 46 name=generate_id(), 47 last_auth_threshold="milliseconds=0", 48 not_configured_action=NotConfiguredAction.CONFIGURE, 49 device_classes=[DeviceClasses.SMS], 50 ) 51 stage.configuration_stages.set([ident_stage]) 52 flow = create_test_flow() 53 FlowStageBinding.objects.create(target=flow, stage=ident_stage, order=0) 54 FlowStageBinding.objects.create(target=flow, stage=stage, order=1) 55 56 response = self.client.post( 57 reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}), 58 {"uid_field": self.user.username}, 59 ) 60 self.assertEqual(response.status_code, 302) 61 device.generate_token() 62 response = self.client.post( 63 reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}), 64 {"code": device.token}, 65 ) 66 self.assertNotIn(COOKIE_NAME_MFA, response.cookies) 67 68 def test_last_auth_threshold_valid(self): 69 """Test last_auth_threshold""" 70 ident_stage = IdentificationStage.objects.create( 71 name=generate_id(), 72 user_fields=[ 73 UserFields.USERNAME, 74 ], 75 ) 76 device: SMSDevice = SMSDevice.objects.create( 77 user=self.user, 78 confirmed=True, 79 stage=self.stage, 80 ) 81 82 stage = AuthenticatorValidateStage.objects.create( 83 name=generate_id(), 84 last_auth_threshold="hours=1", 85 not_configured_action=NotConfiguredAction.CONFIGURE, 86 device_classes=[DeviceClasses.SMS], 87 ) 88 stage.configuration_stages.set([ident_stage]) 89 flow = create_test_flow() 90 FlowStageBinding.objects.create(target=flow, stage=ident_stage, order=0) 91 FlowStageBinding.objects.create(target=flow, stage=stage, order=1) 92 93 response = self.client.post( 94 reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}), 95 {"uid_field": self.user.username}, 96 follow=True, 97 ) 98 self.assertEqual(response.status_code, 200) 99 send_mock = MagicMock() 100 with patch( 101 "authentik.stages.authenticator_sms.models.AuthenticatorSMSStage.send", send_mock 102 ): 103 response = self.client.post( 104 reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}), 105 { 106 "component": "ak-stage-authenticator-validate", 107 "selected_challenge": { 108 "device_class": "sms", 109 "device_uid": str(device.pk), 110 "challenge": {}, 111 "last_used": None, 112 }, 113 }, 114 ) 115 self.assertEqual(send_mock.call_count, 1) 116 device.generate_token() 117 response = self.client.post( 118 reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}), 119 {"code": device.token}, 120 ) 121 self.assertIn(COOKIE_NAME_MFA, response.cookies) 122 self.assertStageResponse(response, component="xak-flow-redirect", to="/") 123 124 def test_sms_hashed(self): 125 """Test hashed SMS device""" 126 ident_stage = IdentificationStage.objects.create( 127 name=generate_id(), 128 user_fields=[ 129 UserFields.USERNAME, 130 ], 131 ) 132 SMSDevice.objects.create( 133 user=self.user, 134 confirmed=True, 135 stage=self.stage, 136 phone_number="hash:foo", 137 ) 138 139 stage = AuthenticatorValidateStage.objects.create( 140 name=generate_id(), 141 last_auth_threshold="hours=1", 142 not_configured_action=NotConfiguredAction.DENY, 143 device_classes=[DeviceClasses.SMS], 144 ) 145 stage.configuration_stages.set([ident_stage]) 146 flow = create_test_flow() 147 FlowStageBinding.objects.create(target=flow, stage=ident_stage, order=0) 148 FlowStageBinding.objects.create(target=flow, stage=stage, order=1) 149 150 response = self.client.post( 151 reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}), 152 {"uid_field": self.user.username}, 153 follow=True, 154 ) 155 self.assertEqual(response.status_code, 200) 156 self.assertStageResponse(response, flow, self.user, component="ak-stage-access-denied")
Test validator stage
def
setUp(self) -> None:
22 def setUp(self) -> None: 23 self.user = create_test_admin_user() 24 self.request_factory = RequestFactory() 25 self.stage = AuthenticatorSMSStage.objects.create( 26 name="sms", 27 provider=SMSProviders.GENERIC, 28 from_number="1234", 29 )
Hook method for setting up the test fixture before exercising it.
def
test_last_auth_threshold(self):
31 def test_last_auth_threshold(self): 32 """Test last_auth_threshold""" 33 ident_stage = IdentificationStage.objects.create( 34 name=generate_id(), 35 user_fields=[ 36 UserFields.USERNAME, 37 ], 38 ) 39 device: SMSDevice = SMSDevice.objects.create( 40 user=self.user, 41 confirmed=True, 42 stage=self.stage, 43 ) 44 45 stage = AuthenticatorValidateStage.objects.create( 46 name=generate_id(), 47 last_auth_threshold="milliseconds=0", 48 not_configured_action=NotConfiguredAction.CONFIGURE, 49 device_classes=[DeviceClasses.SMS], 50 ) 51 stage.configuration_stages.set([ident_stage]) 52 flow = create_test_flow() 53 FlowStageBinding.objects.create(target=flow, stage=ident_stage, order=0) 54 FlowStageBinding.objects.create(target=flow, stage=stage, order=1) 55 56 response = self.client.post( 57 reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}), 58 {"uid_field": self.user.username}, 59 ) 60 self.assertEqual(response.status_code, 302) 61 device.generate_token() 62 response = self.client.post( 63 reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}), 64 {"code": device.token}, 65 ) 66 self.assertNotIn(COOKIE_NAME_MFA, response.cookies)
Test last_auth_threshold
def
test_last_auth_threshold_valid(self):
68 def test_last_auth_threshold_valid(self): 69 """Test last_auth_threshold""" 70 ident_stage = IdentificationStage.objects.create( 71 name=generate_id(), 72 user_fields=[ 73 UserFields.USERNAME, 74 ], 75 ) 76 device: SMSDevice = SMSDevice.objects.create( 77 user=self.user, 78 confirmed=True, 79 stage=self.stage, 80 ) 81 82 stage = AuthenticatorValidateStage.objects.create( 83 name=generate_id(), 84 last_auth_threshold="hours=1", 85 not_configured_action=NotConfiguredAction.CONFIGURE, 86 device_classes=[DeviceClasses.SMS], 87 ) 88 stage.configuration_stages.set([ident_stage]) 89 flow = create_test_flow() 90 FlowStageBinding.objects.create(target=flow, stage=ident_stage, order=0) 91 FlowStageBinding.objects.create(target=flow, stage=stage, order=1) 92 93 response = self.client.post( 94 reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}), 95 {"uid_field": self.user.username}, 96 follow=True, 97 ) 98 self.assertEqual(response.status_code, 200) 99 send_mock = MagicMock() 100 with patch( 101 "authentik.stages.authenticator_sms.models.AuthenticatorSMSStage.send", send_mock 102 ): 103 response = self.client.post( 104 reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}), 105 { 106 "component": "ak-stage-authenticator-validate", 107 "selected_challenge": { 108 "device_class": "sms", 109 "device_uid": str(device.pk), 110 "challenge": {}, 111 "last_used": None, 112 }, 113 }, 114 ) 115 self.assertEqual(send_mock.call_count, 1) 116 device.generate_token() 117 response = self.client.post( 118 reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}), 119 {"code": device.token}, 120 ) 121 self.assertIn(COOKIE_NAME_MFA, response.cookies) 122 self.assertStageResponse(response, component="xak-flow-redirect", to="/")
Test last_auth_threshold
def
test_sms_hashed(self):
124 def test_sms_hashed(self): 125 """Test hashed SMS device""" 126 ident_stage = IdentificationStage.objects.create( 127 name=generate_id(), 128 user_fields=[ 129 UserFields.USERNAME, 130 ], 131 ) 132 SMSDevice.objects.create( 133 user=self.user, 134 confirmed=True, 135 stage=self.stage, 136 phone_number="hash:foo", 137 ) 138 139 stage = AuthenticatorValidateStage.objects.create( 140 name=generate_id(), 141 last_auth_threshold="hours=1", 142 not_configured_action=NotConfiguredAction.DENY, 143 device_classes=[DeviceClasses.SMS], 144 ) 145 stage.configuration_stages.set([ident_stage]) 146 flow = create_test_flow() 147 FlowStageBinding.objects.create(target=flow, stage=ident_stage, order=0) 148 FlowStageBinding.objects.create(target=flow, stage=stage, order=1) 149 150 response = self.client.post( 151 reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}), 152 {"uid_field": self.user.username}, 153 follow=True, 154 ) 155 self.assertEqual(response.status_code, 200) 156 self.assertStageResponse(response, flow, self.user, component="ak-stage-access-denied")
Test hashed SMS device