diff --git a/authentik/stages/prompt/stage.py b/authentik/stages/prompt/stage.py index 6a90f66b1..960d2dac4 100644 --- a/authentik/stages/prompt/stage.py +++ b/authentik/stages/prompt/stage.py @@ -89,9 +89,7 @@ class PromptChallengeResponse(ChallengeResponse): if len(all_passwords) > 1: raise ValidationError(_("Passwords don't match.")) - def validate(self, attrs): - if attrs == {}: - return {} + def validate(self, attrs: dict[str, Any]) -> dict[str, Any]: # Check if we have two password fields, and make sure they are the same password_fields: QuerySet[Prompt] = self.stage.fields.filter( type=FieldTypes.PASSWORD @@ -172,7 +170,7 @@ class PromptStageView(ChallengeStageView): return challenge def get_response_instance(self, data: QueryDict) -> ChallengeResponse: - if not self.executor.plan: + if not self.executor.plan: # pragma: no cover raise ValueError return PromptChallengeResponse( instance=None, diff --git a/authentik/stages/prompt/tests.py b/authentik/stages/prompt/tests.py index 4787ce3a6..125b54bcc 100644 --- a/authentik/stages/prompt/tests.py +++ b/authentik/stages/prompt/tests.py @@ -4,6 +4,7 @@ from unittest.mock import MagicMock, patch from django.test import Client, TestCase from django.urls import reverse from django.utils.encoding import force_str +from rest_framework.exceptions import ErrorDetail from authentik.core.models import User from authentik.flows.challenge import ChallengeTypes @@ -29,6 +30,13 @@ class TestPromptStage(TestCase): slug="test-prompt", designation=FlowDesignation.AUTHENTICATION, ) + username_prompt = Prompt.objects.create( + field_key="username_prompt", + label="USERNAME_LABEL", + type=FieldTypes.USERNAME, + required=True, + placeholder="USERNAME_PLACEHOLDER", + ) text_prompt = Prompt.objects.create( field_key="text_prompt", label="TEXT_LABEL", @@ -73,6 +81,7 @@ class TestPromptStage(TestCase): self.stage = PromptStage.objects.create(name="prompt-stage") self.stage.fields.set( [ + username_prompt, text_prompt, email_prompt, password_prompt, @@ -84,6 +93,7 @@ class TestPromptStage(TestCase): self.stage.save() self.prompt_data = { + username_prompt.field_key: "test-username", text_prompt.field_key: "test-input", email_prompt.field_key: "test@test.test", password_prompt.field_key: "test", @@ -182,3 +192,41 @@ class TestPromptStage(TestCase): for prompt in self.stage.fields.all(): prompt: Prompt self.assertEqual(data[prompt.field_key], self.prompt_data[prompt.field_key]) + + def test_invalid_password(self): + """Test challenge_response validation""" + plan = FlowPlan( + flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()] + ) + self.prompt_data["password2_prompt"] = "qwerqwerqr" + challenge_response = PromptChallengeResponse( + None, stage=self.stage, plan=plan, data=self.prompt_data + ) + self.assertEqual(challenge_response.is_valid(), False) + self.assertEqual( + challenge_response.errors, + { + "non_field_errors": [ + ErrorDetail(string="Passwords don't match.", code="invalid") + ] + }, + ) + + def test_invalid_username(self): + """Test challenge_response validation""" + plan = FlowPlan( + flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()] + ) + self.prompt_data["username_prompt"] = "akadmin" + challenge_response = PromptChallengeResponse( + None, stage=self.stage, plan=plan, data=self.prompt_data + ) + self.assertEqual(challenge_response.is_valid(), False) + self.assertEqual( + challenge_response.errors, + { + "username_prompt": [ + ErrorDetail(string="Username is already taken.", code="invalid") + ] + }, + )