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:
Jens L 2023-03-27 23:08:55 +02:00 committed by GitHub
parent 0d6481c4d5
commit 4218ece2a5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 63 additions and 7 deletions

View file

@ -377,9 +377,7 @@ class AuthenticatorValidateStageView(ChallengeStageView):
def challenge_valid(self, response: AuthenticatorValidationChallengeResponse) -> HttpResponse:
# All validation is done by the serializer
user = self.executor.plan.context.get(PLAN_CONTEXT_PENDING_USER)
if not user:
if "webauthn" not in response.data:
return self.executor.stage_invalid()
if not user and "webauthn" in response.data:
webauthn_device: WebAuthnDevice = response.device
self.logger.debug("Set user from user-less flow", user=webauthn_device.user)
self.executor.plan.context[PLAN_CONTEXT_PENDING_USER] = webauthn_device.user

View file

@ -1,18 +1,22 @@
"""Test validator stage"""
from unittest.mock import MagicMock, patch
from django.contrib.sessions.middleware import SessionMiddleware
from django.test.client import RequestFactory
from django.urls.base import reverse
from rest_framework.exceptions import ValidationError
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.tests import FlowTestCase
from authentik.flows.views.executor import FlowExecutorView
from authentik.lib.generators import generate_id
from authentik.flows.views.executor import SESSION_KEY_PLAN, 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.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 (
SESSION_KEY_DEVICE_CHALLENGES,
AuthenticatorValidationChallengeResponse,
@ -115,3 +119,57 @@ class AuthenticatorValidateStageTests(FlowTestCase):
"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"))