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:
parent
ade2d4879c
commit
2a42c203b2
|
@ -74,14 +74,6 @@ class AuthenticatorTOTPStageView(ChallengeStageView):
|
||||||
|
|
||||||
stage: AuthenticatorTOTPStage = self.executor.current_stage
|
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(
|
TOTPDevice.objects.create(
|
||||||
user=user, confirmed=False, digits=stage.digits, name="TOTP Authenticator"
|
user=user, confirmed=False, digits=stage.digits, name="TOTP Authenticator"
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,16 +1,20 @@
|
||||||
"""Test validator stage"""
|
"""Test validator stage"""
|
||||||
from unittest.mock import MagicMock, patch
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
|
from django.contrib.sessions.middleware import SessionMiddleware
|
||||||
from django.test.client import RequestFactory
|
from django.test.client import RequestFactory
|
||||||
from rest_framework.exceptions import ValidationError
|
from rest_framework.exceptions import ValidationError
|
||||||
|
|
||||||
from authentik.core.tests.utils import create_test_admin_user
|
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.stage import StageView
|
||||||
from authentik.flows.tests import FlowTestCase
|
from authentik.flows.tests import FlowTestCase
|
||||||
from authentik.flows.views.executor import FlowExecutorView
|
from authentik.flows.views.executor import FlowExecutorView
|
||||||
from authentik.lib.generators import generate_id, generate_key
|
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_duo.models import AuthenticatorDuoStage, DuoDevice
|
||||||
from authentik.stages.authenticator_validate.challenge import validate_challenge_duo
|
from authentik.stages.authenticator_validate.challenge import validate_challenge_duo
|
||||||
|
from authentik.tenants.utils import get_tenant_for_request
|
||||||
|
|
||||||
|
|
||||||
class AuthenticatorValidateStageDuoTests(FlowTestCase):
|
class AuthenticatorValidateStageDuoTests(FlowTestCase):
|
||||||
|
@ -23,6 +27,12 @@ class AuthenticatorValidateStageDuoTests(FlowTestCase):
|
||||||
def test_device_challenge_duo(self):
|
def test_device_challenge_duo(self):
|
||||||
"""Test duo"""
|
"""Test duo"""
|
||||||
request = self.request_factory.get("/")
|
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(
|
stage = AuthenticatorDuoStage.objects.create(
|
||||||
name=generate_id(),
|
name=generate_id(),
|
||||||
client_id=generate_id(),
|
client_id=generate_id(),
|
||||||
|
@ -33,35 +43,45 @@ class AuthenticatorValidateStageDuoTests(FlowTestCase):
|
||||||
user=self.user,
|
user=self.user,
|
||||||
stage=stage,
|
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(
|
with patch(
|
||||||
"authentik.stages.authenticator_duo.models.AuthenticatorDuoStage.client",
|
"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(
|
self.assertEqual(
|
||||||
duo_device,
|
duo_device,
|
||||||
validate_challenge_duo(
|
validate_challenge_duo(
|
||||||
duo_device.pk,
|
duo_device.pk,
|
||||||
StageView(FlowExecutorView(current_stage=stage), request=request),
|
StageView(
|
||||||
|
FlowExecutorView(
|
||||||
|
current_stage=stage,
|
||||||
|
plan=FlowPlan(generate_id(), [], {}),
|
||||||
|
),
|
||||||
|
request=request,
|
||||||
|
),
|
||||||
self.user,
|
self.user,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
with patch(
|
with patch(
|
||||||
"authentik.stages.authenticator_duo.models.AuthenticatorDuoStage.client",
|
"authentik.stages.authenticator_duo.models.AuthenticatorDuoStage.client",
|
||||||
failed_duo_mock,
|
MagicMock(auth=MagicMock(return_value={"result": "deny"})),
|
||||||
):
|
):
|
||||||
with self.assertRaises(ValidationError):
|
with self.assertRaises(ValidationError):
|
||||||
validate_challenge_duo(
|
validate_challenge_duo(
|
||||||
duo_device.pk,
|
duo_device.pk,
|
||||||
StageView(FlowExecutorView(current_stage=stage), request=request),
|
StageView(
|
||||||
|
FlowExecutorView(
|
||||||
|
current_stage=stage,
|
||||||
|
plan=FlowPlan(generate_id(), [], {}),
|
||||||
|
),
|
||||||
|
request=request,
|
||||||
|
),
|
||||||
self.user,
|
self.user,
|
||||||
)
|
)
|
||||||
|
|
Reference in New Issue