stages/authenticator_validate: fix stage not working without pending user (#5096)
closes #5094 Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
parent
0d6481c4d5
commit
4218ece2a5
|
@ -377,9 +377,7 @@ class AuthenticatorValidateStageView(ChallengeStageView):
|
||||||
def challenge_valid(self, response: AuthenticatorValidationChallengeResponse) -> HttpResponse:
|
def challenge_valid(self, response: AuthenticatorValidationChallengeResponse) -> HttpResponse:
|
||||||
# All validation is done by the serializer
|
# All validation is done by the serializer
|
||||||
user = self.executor.plan.context.get(PLAN_CONTEXT_PENDING_USER)
|
user = self.executor.plan.context.get(PLAN_CONTEXT_PENDING_USER)
|
||||||
if not user:
|
if not user and "webauthn" in response.data:
|
||||||
if "webauthn" not in response.data:
|
|
||||||
return self.executor.stage_invalid()
|
|
||||||
webauthn_device: WebAuthnDevice = response.device
|
webauthn_device: WebAuthnDevice = response.device
|
||||||
self.logger.debug("Set user from user-less flow", user=webauthn_device.user)
|
self.logger.debug("Set user from user-less flow", user=webauthn_device.user)
|
||||||
self.executor.plan.context[PLAN_CONTEXT_PENDING_USER] = webauthn_device.user
|
self.executor.plan.context[PLAN_CONTEXT_PENDING_USER] = webauthn_device.user
|
||||||
|
|
|
@ -1,18 +1,22 @@
|
||||||
"""Test validator stage"""
|
"""Test validator stage"""
|
||||||
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
from django.contrib.sessions.middleware import SessionMiddleware
|
from django.contrib.sessions.middleware import SessionMiddleware
|
||||||
from django.test.client import RequestFactory
|
from django.test.client import RequestFactory
|
||||||
from django.urls.base import reverse
|
from django.urls.base import reverse
|
||||||
from rest_framework.exceptions import ValidationError
|
from rest_framework.exceptions import ValidationError
|
||||||
|
|
||||||
from authentik.core.tests.utils import create_test_admin_user, create_test_flow
|
from authentik.core.tests.utils import create_test_admin_user, create_test_flow
|
||||||
from authentik.flows.models import FlowStageBinding, NotConfiguredAction
|
from authentik.flows.models import FlowDesignation, FlowStageBinding, NotConfiguredAction
|
||||||
|
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 SESSION_KEY_PLAN, FlowExecutorView
|
||||||
from authentik.lib.generators import generate_id
|
from authentik.lib.generators import generate_id, generate_key
|
||||||
from authentik.lib.tests.utils import dummy_get_response
|
from authentik.lib.tests.utils import dummy_get_response
|
||||||
|
from authentik.stages.authenticator_duo.models import AuthenticatorDuoStage, DuoDevice
|
||||||
from authentik.stages.authenticator_validate.api import AuthenticatorValidateStageSerializer
|
from authentik.stages.authenticator_validate.api import AuthenticatorValidateStageSerializer
|
||||||
from authentik.stages.authenticator_validate.models import AuthenticatorValidateStage
|
from authentik.stages.authenticator_validate.models import AuthenticatorValidateStage, DeviceClasses
|
||||||
from authentik.stages.authenticator_validate.stage import (
|
from authentik.stages.authenticator_validate.stage import (
|
||||||
SESSION_KEY_DEVICE_CHALLENGES,
|
SESSION_KEY_DEVICE_CHALLENGES,
|
||||||
AuthenticatorValidationChallengeResponse,
|
AuthenticatorValidationChallengeResponse,
|
||||||
|
@ -115,3 +119,57 @@ class AuthenticatorValidateStageTests(FlowTestCase):
|
||||||
"device_uid": "1",
|
"device_uid": "1",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@patch(
|
||||||
|
"authentik.stages.authenticator_duo.models.AuthenticatorDuoStage.auth_client",
|
||||||
|
MagicMock(
|
||||||
|
return_value=MagicMock(
|
||||||
|
auth=MagicMock(
|
||||||
|
return_value={
|
||||||
|
"result": "allow",
|
||||||
|
"status": "allow",
|
||||||
|
"status_msg": "Success. Logging you in...",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
def test_non_authentication_flow(self):
|
||||||
|
"""Test full in an authorization flow (no pending user)"""
|
||||||
|
self.client.force_login(self.user)
|
||||||
|
duo_stage = AuthenticatorDuoStage.objects.create(
|
||||||
|
name=generate_id(),
|
||||||
|
client_id=generate_id(),
|
||||||
|
client_secret=generate_key(),
|
||||||
|
api_hostname="",
|
||||||
|
)
|
||||||
|
duo_device = DuoDevice.objects.create(
|
||||||
|
user=self.user,
|
||||||
|
stage=duo_stage,
|
||||||
|
)
|
||||||
|
|
||||||
|
flow = create_test_flow(FlowDesignation.AUTHORIZATION)
|
||||||
|
stage = AuthenticatorValidateStage.objects.create(
|
||||||
|
name=generate_id(),
|
||||||
|
device_classes=[DeviceClasses.DUO],
|
||||||
|
)
|
||||||
|
|
||||||
|
plan = FlowPlan(flow_pk=flow.pk.hex)
|
||||||
|
plan.append(FlowStageBinding.objects.create(target=flow, stage=stage, order=2))
|
||||||
|
session = self.client.session
|
||||||
|
session[SESSION_KEY_PLAN] = plan
|
||||||
|
session.save()
|
||||||
|
|
||||||
|
response = self.client.get(
|
||||||
|
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
|
||||||
|
)
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
|
||||||
|
{"duo": duo_device.pk},
|
||||||
|
follow=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertStageRedirects(response, reverse("authentik_core:root-redirect"))
|
||||||
|
|
Reference in a new issue