def test_user_create(self): """Test creation of user""" password = "".join(SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(8)) plan = FlowPlan(flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()]) plan.context[PLAN_CONTEXT_PROMPT] = { "username": "******", "name": "name", "email": "*****@*****.**", "password": password, } session = self.client.session session[SESSION_KEY_PLAN] = plan session.save() response = self.client.get( reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug})) self.assertEqual(response.status_code, 200) self.assertJSONEqual( force_str(response.content), { "to": reverse("authentik_core:root-redirect"), "type": "redirect" }, ) user_qs = User.objects.filter( username=plan.context[PLAN_CONTEXT_PROMPT]["username"]) self.assertTrue(user_qs.exists()) self.assertTrue(user_qs.first().check_password(password))
def test_send_error(self): """Test error during sending (sending will be retried)""" plan = FlowPlan(flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()]) plan.context[PLAN_CONTEXT_PENDING_USER] = self.user session = self.client.session session[SESSION_KEY_PLAN] = plan session.save() url = reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}) with self.settings( EMAIL_BACKEND="django.core.mail.backends.locmem.EmailBackend"): with patch( "django.core.mail.backends.locmem.EmailBackend.send_messages", MagicMock(side_effect=[ SMTPException, EmailBackend.send_messages ]), ): response = self.client.post(url) response = self.client.post(url) self.assertEqual(response.status_code, 200) self.assertTrue(len(mail.outbox) >= 1) self.assertEqual(mail.outbox[0].subject, "authentik")
def test_with_invitation_get(self): """Test with invitation, check data in session""" plan = FlowPlan(flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()]) session = self.client.session session[SESSION_KEY_PLAN] = plan session.save() data = {"foo": "bar"} invite = Invitation.objects.create(created_by=get_anonymous_user(), fixed_data=data) with patch("authentik.flows.views.FlowExecutorView.cancel", MagicMock()): base_url = reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}) args = urlencode({INVITATION_TOKEN_KEY: invite.pk.hex}) response = self.client.get(base_url + f"?query={args}") session = self.client.session plan: FlowPlan = session[SESSION_KEY_PLAN] self.assertEqual(plan.context[PLAN_CONTEXT_PROMPT], data) self.assertEqual(response.status_code, 200) self.assertJSONEqual( force_str(response.content), { "to": reverse("authentik_core:root-redirect"), "type": "redirect" }, )
def test_with_blank_username(self): """Test with blank username results in error""" plan = FlowPlan(flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()]) session = self.client.session plan.context[PLAN_CONTEXT_PROMPT] = { "username": "", "attribute_some-custom-attribute": "test", "some_ignored_attribute": "bar", } session[SESSION_KEY_PLAN] = plan session.save() response = self.client.get( reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug})) self.assertEqual(response.status_code, 200) self.assertJSONEqual( force_str(response.content), { "component": "ak-stage-access-denied", "error_message": None, "title": "", "type": ChallengeTypes.NATIVE.value, }, )
def test_always_required(self): """Test always required consent""" flow = Flow.objects.create( name="test-consent", slug="test-consent", designation=FlowDesignation.AUTHENTICATION, ) stage = ConsentStage.objects.create(name="consent", mode=ConsentMode.ALWAYS_REQUIRE) FlowStageBinding.objects.create(target=flow, stage=stage, order=2) plan = FlowPlan(flow_pk=flow.pk.hex, stages=[stage], markers=[StageMarker()]) session = self.client.session session[SESSION_KEY_PLAN] = plan session.save() response = self.client.post( reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}), {}, ) self.assertEqual(response.status_code, 200) self.assertJSONEqual( force_str(response.content), { "component": "xak-flow-redirect", "to": reverse("authentik_core:root-redirect"), "type": ChallengeTypes.REDIRECT.value, }, ) self.assertFalse(UserConsent.objects.filter(user=self.user).exists())
def test_without_user(self): """Test without user""" plan = FlowPlan(flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()]) session = self.client.session session[SESSION_KEY_PLAN] = plan session.save() response = self.client.post( reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}), # Still have to send the password so the form is valid {"password": self.password}, ) self.assertEqual(response.status_code, 200) self.assertJSONEqual( force_str(response.content), { "component": "ak-stage-access-denied", "error_message": None, "title": "", "type": ChallengeTypes.NATIVE.value, }, )
def test_valid_challenge_request(self): """Test a request with valid challenge_response data""" plan = FlowPlan(flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]) session = self.client.session session[SESSION_KEY_PLAN] = plan session.save() challenge_response = self.test_valid_challenge_with_policy() with patch("authentik.flows.views.executor.FlowExecutorView.cancel", MagicMock()): response = self.client.post( reverse( "authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}, ), challenge_response.validated_data, ) self.assertEqual(response.status_code, 200) self.assertStageRedirects(response, reverse("authentik_core:root-redirect")) # Check that valid data has been saved session = self.client.session plan: FlowPlan = session[SESSION_KEY_PLAN] data = plan.context[PLAN_CONTEXT_PROMPT] for prompt in self.stage.fields.all(): prompt: Prompt self.assertEqual(data[prompt.field_key], self.prompt_data[prompt.field_key])
def _build_plan( self, user: User, request: HttpRequest, default_context: Optional[dict[str, Any]], ) -> FlowPlan: """Build flow plan by checking each stage in their respective order and checking the applied policies""" with Hub.current.start_span( op="authentik.flow.planner.build_plan", description=self.flow.slug, ) as span, HIST_FLOWS_PLAN_TIME.labels(flow_slug=self.flow.slug).time(): span: Span span.set_data("flow", self.flow) span.set_data("user", user) span.set_data("request", request) plan = FlowPlan(flow_pk=self.flow.pk.hex) if default_context: plan.context = default_context # Check Flow policies for binding in FlowStageBinding.objects.filter(target__pk=self.flow.pk).order_by( "order" ): binding: FlowStageBinding stage = binding.stage marker = StageMarker() if binding.evaluate_on_plan: self._logger.debug( "f(plan): evaluating on plan", stage=binding.stage, ) engine = PolicyEngine(binding, user, request) engine.request.context = plan.context engine.build() if engine.passing: self._logger.debug( "f(plan): stage passing", stage=binding.stage, ) else: stage = None else: self._logger.debug( "f(plan): not evaluating on plan", stage=binding.stage, ) if binding.re_evaluate_policies and stage: self._logger.debug( "f(plan): stage has re-evaluate marker", stage=binding.stage, ) marker = ReevaluateMarker(binding=binding) if stage: plan.append(binding, marker) HIST_FLOWS_PLAN_TIME.labels(flow_slug=self.flow.slug) self._logger.debug( "f(plan): finished building", ) return plan
def test_user_create(self): """Test creation of user""" password = "".join(SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(8)) plan = FlowPlan(flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]) plan.context[PLAN_CONTEXT_PROMPT] = { "username": "******", "name": "name", "email": "*****@*****.**", "password": password, } plan.context[PLAN_CONTEXT_SOURCES_CONNECTION] = UserSourceConnection( source=self.source) session = self.client.session session[SESSION_KEY_PLAN] = plan session.save() response = self.client.get( reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug})) self.assertEqual(response.status_code, 200) self.assertStageRedirects(response, reverse("authentik_core:root-redirect")) user_qs = User.objects.filter( username=plan.context[PLAN_CONTEXT_PROMPT]["username"]) self.assertTrue(user_qs.exists()) self.assertTrue(user_qs.first().check_password(password)) self.assertEqual(list(user_qs.first().ak_groups.all()), [self.group]) self.assertEqual(user_qs.first().attributes, {USER_ATTRIBUTE_SOURCES: [self.source.name]})
def test_duplicate_data(self): """Test with duplicate data, should trigger error""" user = create_test_admin_user() plan = FlowPlan(flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]) session = self.client.session plan.context[PLAN_CONTEXT_PROMPT] = { "username": user.username, "attribute_some-custom-attribute": "test", "some_ignored_attribute": "bar", } session[SESSION_KEY_PLAN] = plan session.save() response = self.client.get( reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug})) self.assertEqual(response.status_code, 200) self.assertStageResponse( response, self.flow, component="ak-stage-access-denied", )
def test_permission_denied(self): """Test with a valid pending user and valid password. Backend is patched to return PermissionError""" plan = FlowPlan(flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]) plan.context[PLAN_CONTEXT_PENDING_USER] = self.user session = self.client.session session[SESSION_KEY_PLAN] = plan session.save() response = self.client.post( reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}), # Form data {"password": self.password + "test"}, ) self.assertEqual(response.status_code, 200) self.assertStageResponse( response, self.flow, component="ak-stage-access-denied", error_message="Unknown error", )
def test_user_update(self): """Test update of existing user""" new_password = "".join(SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(8)) plan = FlowPlan(flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]) plan.context[PLAN_CONTEXT_PENDING_USER] = User.objects.create( username="******", email="*****@*****.**") plan.context[PLAN_CONTEXT_PROMPT] = { "username": "******", "password": new_password, "attribute.some.custom-attribute": "test", "some_ignored_attribute": "bar", } session = self.client.session session[SESSION_KEY_PLAN] = plan session.save() response = self.client.post( reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug})) self.assertEqual(response.status_code, 200) self.assertStageRedirects(response, reverse("authentik_core:root-redirect")) user_qs = User.objects.filter( username=plan.context[PLAN_CONTEXT_PROMPT]["username"]) self.assertTrue(user_qs.exists()) self.assertTrue(user_qs.first().check_password(new_password)) self.assertEqual( user_qs.first().attributes["some"]["custom-attribute"], "test") self.assertNotIn("some_ignored_attribute", user_qs.first().attributes)
def test_permanent(self): """Test permanent consent from user""" self.client.force_login(self.user) flow = Flow.objects.create( name="test-consent", slug="test-consent", designation=FlowDesignation.AUTHENTICATION, ) stage = ConsentStage.objects.create(name="consent", mode=ConsentMode.PERMANENT) FlowStageBinding.objects.create(target=flow, stage=stage, order=2) plan = FlowPlan( flow_pk=flow.pk.hex, stages=[stage], markers=[StageMarker()], context={PLAN_CONTEXT_APPLICATION: self.application}, ) session = self.client.session session[SESSION_KEY_PLAN] = plan session.save() response = self.client.post( reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}), {}, ) self.assertEqual(response.status_code, 200) self.assertJSONEqual( force_str(response.content), {"to": reverse("authentik_core:root-redirect"), "type": "redirect"}, ) self.assertTrue( UserConsent.objects.filter( user=self.user, application=self.application ).exists() )
def test_with_invitation_prompt_data(self): """Test with invitation, check data in session""" data = {"foo": "bar"} invite = Invitation.objects.create(created_by=get_anonymous_user(), fixed_data=data, single_use=True) plan = FlowPlan(flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]) plan.context[PLAN_CONTEXT_PROMPT] = { INVITATION_TOKEN_KEY_CONTEXT: invite.pk.hex } session = self.client.session session[SESSION_KEY_PLAN] = plan session.save() with patch("authentik.flows.views.executor.FlowExecutorView.cancel", MagicMock()): base_url = reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}) response = self.client.get(base_url, follow=True) session = self.client.session plan: FlowPlan = session[SESSION_KEY_PLAN] self.assertEqual(plan.context[PLAN_CONTEXT_PROMPT], data | plan.context[PLAN_CONTEXT_PROMPT]) self.assertEqual(response.status_code, 200) self.assertStageRedirects(response, reverse("authentik_core:root-redirect")) self.assertFalse(Invitation.objects.filter(pk=invite.pk))
def test_valid_password(self): """Test with a valid pending user and valid password""" plan = FlowPlan(flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()]) plan.context[PLAN_CONTEXT_PENDING_USER] = self.user session = self.client.session session[SESSION_KEY_PLAN] = plan session.save() response = self.client.post( reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}), # Form data {"password": self.password}, ) self.assertEqual(response.status_code, 200) self.assertJSONEqual( force_str(response.content), { "component": "xak-flow-redirect", "to": reverse("authentik_core:root-redirect"), "type": ChallengeTypes.REDIRECT.value, }, )
def test_permission_denied(self): """Test with a valid pending user and valid password. Backend is patched to return PermissionError""" plan = FlowPlan(flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()]) plan.context[PLAN_CONTEXT_PENDING_USER] = self.user session = self.client.session session[SESSION_KEY_PLAN] = plan session.save() response = self.client.post( reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}), # Form data {"password": self.password + "test"}, ) self.assertEqual(response.status_code, 200) self.assertJSONEqual( force_str(response.content), { "component": "ak-stage-access-denied", "error_message": None, "title": "", "type": ChallengeTypes.NATIVE.value, }, )
def test_invalid_password_lockout(self): """Test with a valid pending user and invalid password (trigger logout counter)""" plan = FlowPlan(flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()]) plan.context[PLAN_CONTEXT_PENDING_USER] = self.user session = self.client.session session[SESSION_KEY_PLAN] = plan session.save() for _ in range(self.stage.failed_attempts_before_cancel): response = self.client.post( reverse( "authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}, ), # Form data {"password": self.password + "test"}, ) self.assertEqual(response.status_code, 200) response = self.client.post( reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}), # Form data {"password": self.password + "test"}, ) self.assertEqual(response.status_code, 200) # To ensure the plan has been cancelled, check SESSION_KEY_PLAN self.assertNotIn(SESSION_KEY_PLAN, self.client.session)
def test_expiry(self): """Test with expiry""" self.stage.session_duration = "seconds=2" self.stage.save() plan = FlowPlan(flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()]) plan.context[PLAN_CONTEXT_PENDING_USER] = self.user session = self.client.session session[SESSION_KEY_PLAN] = plan session.save() response = self.client.get( reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug})) self.assertEqual(response.status_code, 200) self.assertJSONEqual( force_str(response.content), { "to": reverse("authentik_core:root-redirect"), "type": "redirect" }, ) self.assertNotEqual(list(self.client.session.keys()), []) sleep(3) self.client.session.clear_expired() self.assertEqual(list(self.client.session.keys()), [])
def test_without_invitation_continue(self): """Test without any invitation, continue_flow_without_invitation is set.""" self.stage.continue_flow_without_invitation = True self.stage.save() plan = FlowPlan( flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()] ) plan.context[PLAN_CONTEXT_PENDING_USER] = self.user plan.context[ PLAN_CONTEXT_AUTHENTICATION_BACKEND ] = "django.contrib.auth.backends.ModelBackend" session = self.client.session session[SESSION_KEY_PLAN] = plan session.save() response = self.client.get( reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}) ) self.assertEqual(response.status_code, 200) self.assertJSONEqual( force_str(response.content), { "component": "xak-flow-redirect", "to": reverse("authentik_core:root-redirect"), "type": ChallengeTypes.REDIRECT.value, }, ) self.stage.continue_flow_without_invitation = False self.stage.save()
def test_without_invitation_fail(self): """Test without any invitation, continue_flow_without_invitation not set.""" plan = FlowPlan( flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()] ) plan.context[PLAN_CONTEXT_PENDING_USER] = self.user plan.context[ PLAN_CONTEXT_AUTHENTICATION_BACKEND ] = "django.contrib.auth.backends.ModelBackend" session = self.client.session session[SESSION_KEY_PLAN] = plan session.save() response = self.client.get( reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}) ) self.assertEqual(response.status_code, 200) self.assertJSONEqual( force_str(response.content), { "component": "ak-stage-access-denied", "error_message": None, "title": "", "type": ChallengeTypes.NATIVE.value, }, )
def test_with_invitation_prompt_data(self): """Test with invitation, check data in session""" data = {"foo": "bar"} invite = Invitation.objects.create( created_by=get_anonymous_user(), fixed_data=data, single_use=True ) plan = FlowPlan( flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()] ) plan.context[PLAN_CONTEXT_PROMPT] = {INVITATION_TOKEN_KEY: invite.pk.hex} session = self.client.session session[SESSION_KEY_PLAN] = plan session.save() with patch("authentik.flows.views.FlowExecutorView.cancel", MagicMock()): base_url = reverse( "authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug} ) response = self.client.get(base_url) session = self.client.session plan: FlowPlan = session[SESSION_KEY_PLAN] self.assertEqual(plan.context[PLAN_CONTEXT_PROMPT], data) self.assertEqual(response.status_code, 200) self.assertJSONEqual( force_str(response.content), { "component": "xak-flow-redirect", "to": reverse("authentik_core:root-redirect"), "type": ChallengeTypes.REDIRECT.value, }, ) self.assertFalse(Invitation.objects.filter(pk=invite.pk))
def test_valid(self): """Test valid captcha""" plan = FlowPlan(flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]) session = self.client.session session[SESSION_KEY_PLAN] = plan session.save() response = self.client.post( reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}), {"token": "PASSED"}, ) self.assertEqual(response.status_code, 200) self.assertStageRedirects(response, reverse("authentik_core:root-redirect"))
def test_without_user(self): """Test without pending user""" plan = FlowPlan(flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()]) session = self.client.session session[SESSION_KEY_PLAN] = plan session.save() url = reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}) response = self.client.get(url) self.assertEqual(response.status_code, 200)
def test_valid_post(self): """Test with a valid pending user and backend""" plan = FlowPlan(flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]) session = self.client.session session[SESSION_KEY_PLAN] = plan session.save() response = self.client.post( reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}) ) self.assertEqual(response.status_code, 200) self.assertStageResponse(response, self.flow, component="ak-stage-access-denied")
def test_rendering(self): """Test with pending user""" plan = FlowPlan(flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]) plan.context[PLAN_CONTEXT_PENDING_USER] = self.user session = self.client.session session[SESSION_KEY_PLAN] = plan session.save() url = reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}) response = self.client.get(url) self.assertEqual(response.status_code, 200)
def test_valid_challenge_with_policy(self) -> PromptResponseChallenge: """Test challenge_response validation""" plan = FlowPlan(flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()]) expr = "return request.context['password_prompt'] == request.context['password2_prompt']" expr_policy = ExpressionPolicy.objects.create(name="validate-form", expression=expr) self.stage.validation_policies.set([expr_policy]) self.stage.save() challenge_response = PromptResponseChallenge(None, stage=self.stage, plan=plan, data=self.prompt_data) self.assertEqual(challenge_response.is_valid(), True) return challenge_response
def test_invalid_challenge(self) -> PromptChallengeResponse: """Test challenge_response validation""" plan = FlowPlan(flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]) expr = "False" expr_policy = ExpressionPolicy.objects.create(name="validate-form", expression=expr) self.stage.validation_policies.set([expr_policy]) self.stage.save() challenge_response = PromptChallengeResponse(None, stage=self.stage, plan=plan, data=self.prompt_data) self.assertEqual(challenge_response.is_valid(), False) return challenge_response
def test_expire(self): """Test expiring consent from user""" self.client.force_login(self.user) flow = Flow.objects.create( name="test-consent", slug="test-consent", designation=FlowDesignation.AUTHENTICATION, ) stage = ConsentStage.objects.create( name="consent", mode=ConsentMode.EXPIRING, consent_expire_in="seconds=1" ) FlowStageBinding.objects.create(target=flow, stage=stage, order=2) plan = FlowPlan( flow_pk=flow.pk.hex, stages=[stage], markers=[StageMarker()], context={PLAN_CONTEXT_APPLICATION: self.application}, ) session = self.client.session session[SESSION_KEY_PLAN] = plan session.save() response = self.client.post( reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}), {}, ) self.assertEqual(response.status_code, 200) self.assertJSONEqual( force_str(response.content), { "component": "xak-flow-redirect", "to": reverse("authentik_core:root-redirect"), "type": ChallengeTypes.REDIRECT.value, }, ) self.assertTrue( UserConsent.objects.filter( user=self.user, application=self.application ).exists() ) sleep(1) clean_expired_models.delay().get() self.assertFalse( UserConsent.objects.filter( user=self.user, application=self.application ).exists() )
def test_token(self): """Test with token""" # Make sure token exists self.test_pending_user() plan = FlowPlan( flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()] ) session = self.client.session session[SESSION_KEY_PLAN] = plan session.save() token: Token = Token.objects.get(user=self.user) with patch("authentik.flows.views.FlowExecutorView.cancel", MagicMock()): # Call the executor shell to preseed the session url = reverse( "authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}, ) url_query = urlencode( { QS_KEY_TOKEN: token.key, } ) url += f"?query={url_query}" self.client.get(url) # Call the actual executor to get the JSON Response response = self.client.get( reverse( "authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}, ) ) self.assertEqual(response.status_code, 200) self.assertJSONEqual( force_str(response.content), { "component": "xak-flow-redirect", "to": reverse("authentik_core:root-redirect"), "type": ChallengeTypes.REDIRECT.value, }, ) session = self.client.session plan: FlowPlan = session[SESSION_KEY_PLAN] self.assertEqual(plan.context[PLAN_CONTEXT_PENDING_USER], self.user)
def test_recovery_flow_link(self): """Test link to the default recovery flow""" flow = Flow.objects.create(designation=FlowDesignation.RECOVERY, slug="qewrqerqr") plan = FlowPlan(flow_pk=self.flow.pk.hex, stages=[self.stage], markers=[StageMarker()]) session = self.client.session session[SESSION_KEY_PLAN] = plan session.save() response = self.client.get( reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}), ) self.assertEqual(response.status_code, 200) self.assertIn(flow.slug, force_str(response.content))