stages/authenticator_totp: remove single device per user limit

closes #3281

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
Jens Langhammer 2022-07-28 21:21:48 +02:00
parent ade2d4879c
commit 2a42c203b2
2 changed files with 34 additions and 22 deletions

View File

@ -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"
)

View File

@ -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,
)