From 2a42c203b2b7dccb2604af31b9e76b2ed73cf255 Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Thu, 28 Jul 2022 21:21:48 +0200 Subject: [PATCH] stages/authenticator_totp: remove single device per user limit closes #3281 Signed-off-by: Jens Langhammer --- authentik/stages/authenticator_totp/stage.py | 8 ---- .../authenticator_validate/tests/test_duo.py | 48 +++++++++++++------ 2 files changed, 34 insertions(+), 22 deletions(-) diff --git a/authentik/stages/authenticator_totp/stage.py b/authentik/stages/authenticator_totp/stage.py index f431aa1b3..11fcb2fac 100644 --- a/authentik/stages/authenticator_totp/stage.py +++ b/authentik/stages/authenticator_totp/stage.py @@ -74,14 +74,6 @@ class AuthenticatorTOTPStageView(ChallengeStageView): stage: AuthenticatorTOTPStage = self.executor.current_stage - devices = TOTPDevice.objects.filter(user=user) - # Currently, this stage only supports one device per user. If the user already - # has a device, just skip to the next stage - if devices.exists(): - if not any(x.confirmed for x in devices): - return super().get(request, *args, **kwargs) - return self.executor.stage_ok() - TOTPDevice.objects.create( user=user, confirmed=False, digits=stage.digits, name="TOTP Authenticator" ) diff --git a/authentik/stages/authenticator_validate/tests/test_duo.py b/authentik/stages/authenticator_validate/tests/test_duo.py index e22cab9ce..1c793d986 100644 --- a/authentik/stages/authenticator_validate/tests/test_duo.py +++ b/authentik/stages/authenticator_validate/tests/test_duo.py @@ -1,16 +1,20 @@ """Test validator stage""" from unittest.mock import MagicMock, patch +from django.contrib.sessions.middleware import SessionMiddleware from django.test.client import RequestFactory from rest_framework.exceptions import ValidationError from authentik.core.tests.utils import create_test_admin_user +from authentik.flows.planner import FlowPlan from authentik.flows.stage import StageView from authentik.flows.tests import FlowTestCase from authentik.flows.views.executor import FlowExecutorView from authentik.lib.generators import generate_id, generate_key +from authentik.lib.tests.utils import dummy_get_response from authentik.stages.authenticator_duo.models import AuthenticatorDuoStage, DuoDevice from authentik.stages.authenticator_validate.challenge import validate_challenge_duo +from authentik.tenants.utils import get_tenant_for_request class AuthenticatorValidateStageDuoTests(FlowTestCase): @@ -23,6 +27,12 @@ class AuthenticatorValidateStageDuoTests(FlowTestCase): def test_device_challenge_duo(self): """Test duo""" request = self.request_factory.get("/") + + middleware = SessionMiddleware(dummy_get_response) + middleware.process_request(request) + request.session.save() + setattr(request, "tenant", get_tenant_for_request(request)) + stage = AuthenticatorDuoStage.objects.create( name=generate_id(), client_id=generate_id(), @@ -33,35 +43,45 @@ class AuthenticatorValidateStageDuoTests(FlowTestCase): user=self.user, stage=stage, ) - duo_mock = MagicMock( - auth=MagicMock( - return_value={ - "result": "allow", - "status": "allow", - "status_msg": "Success. Logging you in...", - } - ) - ) - failed_duo_mock = MagicMock(auth=MagicMock(return_value={"result": "deny"})) with patch( "authentik.stages.authenticator_duo.models.AuthenticatorDuoStage.client", - duo_mock, + MagicMock( + auth=MagicMock( + return_value={ + "result": "allow", + "status": "allow", + "status_msg": "Success. Logging you in...", + } + ) + ), ): self.assertEqual( duo_device, validate_challenge_duo( duo_device.pk, - StageView(FlowExecutorView(current_stage=stage), request=request), + StageView( + FlowExecutorView( + current_stage=stage, + plan=FlowPlan(generate_id(), [], {}), + ), + request=request, + ), self.user, ), ) with patch( "authentik.stages.authenticator_duo.models.AuthenticatorDuoStage.client", - failed_duo_mock, + MagicMock(auth=MagicMock(return_value={"result": "deny"})), ): with self.assertRaises(ValidationError): validate_challenge_duo( duo_device.pk, - StageView(FlowExecutorView(current_stage=stage), request=request), + StageView( + FlowExecutorView( + current_stage=stage, + plan=FlowPlan(generate_id(), [], {}), + ), + request=request, + ), self.user, )