Exemple #1
0
    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))
Exemple #2
0
    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")
Exemple #3
0
    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"
            },
        )
Exemple #4
0
    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,
            },
        )
Exemple #5
0
    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())
Exemple #6
0
    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,
            },
        )
Exemple #7
0
    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])
Exemple #8
0
    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
Exemple #9
0
    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]})
Exemple #10
0
    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",
        )
Exemple #11
0
    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",
        )
Exemple #12
0
    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)
Exemple #13
0
    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()
        )
Exemple #14
0
    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))
Exemple #15
0
    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,
            },
        )
Exemple #16
0
    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,
            },
        )
Exemple #17
0
    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)
Exemple #18
0
    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()), [])
Exemple #19
0
    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()
Exemple #20
0
    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,
            },
        )
Exemple #21
0
    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))
Exemple #22
0
 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"))
Exemple #23
0
    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)
Exemple #24
0
    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")
Exemple #25
0
    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)
Exemple #26
0
 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
Exemple #27
0
 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
Exemple #28
0
    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()
        )
Exemple #29
0
    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)
Exemple #30
0
    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))