mirror of
https://github.com/goauthentik/authentik.git
synced 2026-06-18 03:19:51 +03:00
flows: keep ?next url when using cancel (#18619)
keep ?next url when using cancel Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
"""authentik stage Base view"""
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
from urllib.parse import urlencode
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import AnonymousUser
|
||||
@@ -164,6 +165,16 @@ class ChallengeStageView(StageView):
|
||||
self.logger.warning("failed to template title", exc=exc)
|
||||
return self.executor.flow.title
|
||||
|
||||
@property
|
||||
def cancel_url(self) -> str:
|
||||
from authentik.flows.views.executor import NEXT_ARG_NAME, SESSION_KEY_GET
|
||||
|
||||
next_param = self.request.session.get(SESSION_KEY_GET, {}).get(NEXT_ARG_NAME)
|
||||
url = reverse("authentik_flows:cancel")
|
||||
if next_param:
|
||||
return f"{url}?{urlencode({NEXT_ARG_NAME: next_param})}"
|
||||
return url
|
||||
|
||||
def _get_challenge(self, *args, **kwargs) -> Challenge:
|
||||
with (
|
||||
start_span(
|
||||
@@ -186,7 +197,7 @@ class ChallengeStageView(StageView):
|
||||
data={
|
||||
"title": self.format_title(),
|
||||
"background": self.executor.flow.background_url(self.request),
|
||||
"cancel_url": reverse("authentik_flows:cancel"),
|
||||
"cancel_url": self.cancel_url,
|
||||
"layout": self.executor.flow.layout,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -32,8 +32,10 @@ class FlowTestCase(APITestCase):
|
||||
self.assertIsNotNone(raw_response["component"])
|
||||
if flow:
|
||||
self.assertIn("flow_info", raw_response)
|
||||
self.assertEqual(
|
||||
raw_response["flow_info"]["cancel_url"], reverse("authentik_flows:cancel")
|
||||
self.assertTrue(
|
||||
raw_response["flow_info"]["cancel_url"].startswith(
|
||||
reverse("authentik_flows:cancel")
|
||||
)
|
||||
)
|
||||
# We don't check the flow title since it will most likely go
|
||||
# through ChallengeStageView.format_title() so might not match 1:1
|
||||
|
||||
@@ -36,6 +36,7 @@ from authentik.policies.types import PolicyResult
|
||||
from authentik.stages.deny.models import DenyStage
|
||||
from authentik.stages.dummy.models import DummyStage
|
||||
from authentik.stages.identification.models import IdentificationStage, UserFields
|
||||
from authentik.stages.password.models import PasswordStage
|
||||
|
||||
POLICY_RETURN_FALSE = PropertyMock(return_value=PolicyResult(False, "foo"))
|
||||
POLICY_RETURN_TRUE = MagicMock(return_value=PolicyResult(True))
|
||||
@@ -692,3 +693,49 @@ class TestFlowExecutor(FlowTestCase):
|
||||
self.client.logout()
|
||||
response = self.client.post(url, data="{", content_type="application/json")
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_cancel_next(self):
|
||||
"""Test cancel URL with ?next param set"""
|
||||
flow = create_test_flow()
|
||||
|
||||
# Stage 0 is an identification stage
|
||||
ident_stage = IdentificationStage.objects.create(
|
||||
name=generate_id(),
|
||||
user_fields=[UserFields.USERNAME],
|
||||
)
|
||||
FlowStageBinding.objects.create(
|
||||
target=flow,
|
||||
stage=ident_stage,
|
||||
order=0,
|
||||
)
|
||||
|
||||
# Stage 1 is a password stage
|
||||
password_stage = PasswordStage.objects.create(name=generate_id(), backends=[])
|
||||
FlowStageBinding.objects.create(
|
||||
target=flow,
|
||||
stage=password_stage,
|
||||
order=1,
|
||||
)
|
||||
res = self.client.get(
|
||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug})
|
||||
+ f"?{urlencode({QS_QUERY: urlencode({NEXT_ARG_NAME: "/foo"})})}"
|
||||
)
|
||||
self.assertStageResponse(res, flow, component="ak-stage-identification")
|
||||
|
||||
res = self.client.post(
|
||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug})
|
||||
+ f"?{urlencode({QS_QUERY: urlencode({NEXT_ARG_NAME: "/foo"})})}",
|
||||
data={"component": "ak-stage-identification", "uid_field": generate_id()},
|
||||
follow=True,
|
||||
)
|
||||
self.assertEqual(res.status_code, 200)
|
||||
self.assertStageResponse(
|
||||
res,
|
||||
flow,
|
||||
flow_info={
|
||||
"background": "/static/dist/assets/images/flow_background.jpg",
|
||||
"cancel_url": "/flows/-/cancel/?next=%2Ffoo",
|
||||
"layout": "stacked",
|
||||
"title": flow.title,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -479,6 +479,9 @@ class CancelView(View):
|
||||
if SESSION_KEY_PLAN in request.session:
|
||||
del request.session[SESSION_KEY_PLAN]
|
||||
LOGGER.debug("Canceled current plan")
|
||||
next_url = self.request.GET.get(NEXT_ARG_NAME)
|
||||
if next_url and not is_url_absolute(next_url):
|
||||
return redirect(next_url)
|
||||
return redirect("authentik_flows:default-invalidation")
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user