From 17d33f4b191a9f4fdb164fb4e52b585e390ebf3c Mon Sep 17 00:00:00 2001
From: Jens L
Date: Sat, 2 Jul 2022 17:37:57 +0200
Subject: [PATCH] flows: denied action (#3194)
---
.vscode/tasks.json | 10 ++
authentik/core/tests/utils.py | 9 +-
authentik/flows/api/flows.py | 5 +-
.../migrations/0023_flow_denied_action.py | 26 +++
authentik/flows/models.py | 32 ++--
authentik/flows/tests/test_executor.py | 149 ++++++++++--------
authentik/flows/views/executor.py | 37 ++++-
authentik/lib/utils/errors.py | 14 +-
locale/en/LC_MESSAGES/django.po | 96 +++++------
schema.yml | 30 ++++
web/src/locales/de.po | 28 +++-
web/src/locales/en.po | 28 +++-
web/src/locales/es.po | 28 +++-
web/src/locales/fr_FR.po | 28 +++-
web/src/locales/pl.po | 28 +++-
web/src/locales/pseudo-LOCALE.po | 28 +++-
web/src/locales/tr.po | 28 +++-
web/src/locales/zh-Hans.po | 28 +++-
web/src/locales/zh-Hant.po | 28 +++-
web/src/locales/zh_TW.po | 28 +++-
web/src/pages/flows/FlowForm.ts | 34 ++++
web/src/pages/flows/StageBindingForm.ts | 2 +-
website/docs/flow/index.md | 8 +
23 files changed, 564 insertions(+), 168 deletions(-)
create mode 100644 authentik/flows/migrations/0023_flow_denied_action.py
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
index fb2b880fe..cd705afff 100644
--- a/.vscode/tasks.json
+++ b/.vscode/tasks.json
@@ -46,6 +46,16 @@
"args": ["install"],
"group": "build",
},
+ {
+ "label": "authentik: i18n-extract",
+ "command": "poetry",
+ "args": [
+ "run",
+ "make",
+ "i18n-extract"
+ ],
+ "group": "build",
+ },
{
"label": "authentik[website]: format",
"command": "make",
diff --git a/authentik/core/tests/utils.py b/authentik/core/tests/utils.py
index 84ef0845e..7ce92e8d4 100644
--- a/authentik/core/tests/utils.py
+++ b/authentik/core/tests/utils.py
@@ -11,14 +11,13 @@ from authentik.lib.generators import generate_id
from authentik.tenants.models import Tenant
-def create_test_flow(designation: FlowDesignation = FlowDesignation.STAGE_CONFIGURATION) -> Flow:
+def create_test_flow(
+ designation: FlowDesignation = FlowDesignation.STAGE_CONFIGURATION, **kwargs
+) -> Flow:
"""Generate a flow that can be used for testing"""
uid = generate_id(10)
return Flow.objects.create(
- name=uid,
- title=uid,
- slug=slugify(uid),
- designation=designation,
+ name=uid, title=uid, slug=slugify(uid), designation=designation, **kwargs
)
diff --git a/authentik/flows/api/flows.py b/authentik/flows/api/flows.py
index 5d84f493c..bbb0e49c0 100644
--- a/authentik/flows/api/flows.py
+++ b/authentik/flows/api/flows.py
@@ -73,6 +73,7 @@ class FlowSerializer(ModelSerializer):
"compatibility_mode",
"export_url",
"layout",
+ "denied_action",
]
extra_kwargs = {
"background": {"read_only": True},
@@ -110,8 +111,8 @@ class FlowViewSet(UsedByMixin, ModelViewSet):
serializer_class = FlowSerializer
lookup_field = "slug"
ordering = ["slug", "name"]
- search_fields = ["name", "slug", "designation", "title"]
- filterset_fields = ["flow_uuid", "name", "slug", "designation"]
+ search_fields = ["name", "slug", "designation", "title", "denied_action"]
+ filterset_fields = ["flow_uuid", "name", "slug", "designation", "denied_action"]
@permission_required(None, ["authentik_flows.view_flow_cache"])
@extend_schema(responses={200: CacheSerializer(many=False)})
diff --git a/authentik/flows/migrations/0023_flow_denied_action.py b/authentik/flows/migrations/0023_flow_denied_action.py
new file mode 100644
index 000000000..580d2f00e
--- /dev/null
+++ b/authentik/flows/migrations/0023_flow_denied_action.py
@@ -0,0 +1,26 @@
+# Generated by Django 4.0.5 on 2022-07-02 12:42
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ("authentik_flows", "0022_flow_layout"),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name="flow",
+ name="denied_action",
+ field=models.TextField(
+ choices=[
+ ("message_continue", "Message Continue"),
+ ("message", "Message"),
+ ("continue", "Continue"),
+ ],
+ default="message_continue",
+ help_text="Configure what should happen when a flow denies access to a user.",
+ ),
+ ),
+ ]
diff --git a/authentik/flows/models.py b/authentik/flows/models.py
index b1645ce37..d7e5ba39d 100644
--- a/authentik/flows/models.py
+++ b/authentik/flows/models.py
@@ -5,7 +5,6 @@ from typing import TYPE_CHECKING, Optional
from uuid import uuid4
from django.db import models
-from django.http import HttpRequest
from django.utils.translation import gettext_lazy as _
from model_utils.managers import InheritanceManager
from rest_framework.serializers import BaseSerializer
@@ -40,6 +39,14 @@ class InvalidResponseAction(models.TextChoices):
RESTART_WITH_CONTEXT = "restart_with_context"
+class FlowDeniedAction(models.TextChoices):
+ """Configure what response is given to denied flow executions"""
+
+ MESSAGE_CONTINUE = "message_continue"
+ MESSAGE = "message"
+ CONTINUE = "continue"
+
+
class FlowDesignation(models.TextChoices):
"""Designation of what a Flow should be used for. At a later point, this
should be replaced by a database entry."""
@@ -139,6 +146,12 @@ class Flow(SerializerModel, PolicyBindingModel):
),
)
+ denied_action = models.TextField(
+ choices=FlowDeniedAction.choices,
+ default=FlowDeniedAction.MESSAGE_CONTINUE,
+ help_text=_("Configure what should happen when a flow denies access to a user."),
+ )
+
@property
def background_url(self) -> str:
"""Get the URL to the background image. If the name is /static or starts with http
@@ -157,23 +170,6 @@ class Flow(SerializerModel, PolicyBindingModel):
return FlowSerializer
- @staticmethod
- def with_policy(request: HttpRequest, **flow_filter) -> Optional["Flow"]:
- """Get a Flow by `**flow_filter` and check if the request from `request` can access it."""
- from authentik.policies.engine import PolicyEngine
-
- flows = Flow.objects.filter(**flow_filter).order_by("slug")
- for flow in flows:
- engine = PolicyEngine(flow, request.user, request)
- engine.build()
- result = engine.result
- if result.passing:
- LOGGER.debug("with_policy: flow passing", flow=flow)
- return flow
- LOGGER.warning("with_policy: flow not passing", flow=flow, messages=result.messages)
- LOGGER.debug("with_policy: no flow found", filters=flow_filter)
- return None
-
def __str__(self) -> str:
return f"Flow {self.name} ({self.slug})"
diff --git a/authentik/flows/tests/test_executor.py b/authentik/flows/tests/test_executor.py
index 1a017812c..4387e9d28 100644
--- a/authentik/flows/tests/test_executor.py
+++ b/authentik/flows/tests/test_executor.py
@@ -6,14 +6,21 @@ from django.test.client import RequestFactory
from django.urls import reverse
from authentik.core.models import User
+from authentik.core.tests.utils import create_test_flow
from authentik.flows.exceptions import FlowNonApplicableException
from authentik.flows.markers import ReevaluateMarker, StageMarker
-from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding, InvalidResponseAction
+from authentik.flows.models import (
+ FlowDeniedAction,
+ FlowDesignation,
+ FlowStageBinding,
+ InvalidResponseAction,
+)
from authentik.flows.planner import FlowPlan, FlowPlanner
from authentik.flows.stage import PLAN_CONTEXT_PENDING_USER_IDENTIFIER, StageView
from authentik.flows.tests import FlowTestCase
from authentik.flows.views.executor import NEXT_ARG_NAME, SESSION_KEY_PLAN, FlowExecutorView
from authentik.lib.config import CONFIG
+from authentik.lib.generators import generate_id
from authentik.policies.dummy.models import DummyPolicy
from authentik.policies.models import PolicyBinding
from authentik.policies.reputation.models import ReputationPolicy
@@ -47,12 +54,10 @@ class TestFlowExecutor(FlowTestCase):
)
def test_existing_plan_diff_flow(self):
"""Check that a plan for a different flow cancels the current plan"""
- flow = Flow.objects.create(
- name="test-existing-plan-diff",
- slug="test-existing-plan-diff",
- designation=FlowDesignation.AUTHENTICATION,
+ flow = create_test_flow(
+ FlowDesignation.AUTHENTICATION,
)
- stage = DummyStage.objects.create(name="dummy")
+ stage = DummyStage.objects.create(name=generate_id())
binding = FlowStageBinding(target=flow, stage=stage, order=0)
plan = FlowPlan(flow_pk=flow.pk.hex + "a", bindings=[binding], markers=[StageMarker()])
session = self.client.session
@@ -77,10 +82,8 @@ class TestFlowExecutor(FlowTestCase):
)
def test_invalid_non_applicable_flow(self):
"""Tests that a non-applicable flow returns the correct error message"""
- flow = Flow.objects.create(
- name="test-non-applicable",
- slug="test-non-applicable",
- designation=FlowDesignation.AUTHENTICATION,
+ flow = create_test_flow(
+ FlowDesignation.AUTHENTICATION,
)
CONFIG.update_from_dict({"domain": "testserver"})
@@ -98,12 +101,15 @@ class TestFlowExecutor(FlowTestCase):
"authentik.flows.views.executor.to_stage_response",
TO_STAGE_RESPONSE_MOCK,
)
- def test_invalid_empty_flow(self):
- """Tests that an empty flow returns the correct error message"""
- flow = Flow.objects.create(
- name="test-empty",
- slug="test-empty",
- designation=FlowDesignation.AUTHENTICATION,
+ @patch(
+ "authentik.policies.engine.PolicyEngine.result",
+ POLICY_RETURN_FALSE,
+ )
+ def test_invalid_non_applicable_flow_continue(self):
+ """Tests that a non-applicable flow that should redirect"""
+ flow = create_test_flow(
+ FlowDesignation.AUTHENTICATION,
+ denied_action=FlowDeniedAction.CONTINUE,
)
CONFIG.update_from_dict({"domain": "testserver"})
@@ -119,10 +125,8 @@ class TestFlowExecutor(FlowTestCase):
)
def test_invalid_flow_redirect(self):
"""Tests that an invalid flow still redirects"""
- flow = Flow.objects.create(
- name="test-empty",
- slug="test-empty",
- designation=FlowDesignation.AUTHENTICATION,
+ flow = create_test_flow(
+ FlowDesignation.AUTHENTICATION,
)
CONFIG.update_from_dict({"domain": "testserver"})
@@ -132,18 +136,33 @@ class TestFlowExecutor(FlowTestCase):
self.assertEqual(response.status_code, 302)
self.assertEqual(response.url, reverse("authentik_core:root-redirect"))
+ @patch(
+ "authentik.flows.views.executor.to_stage_response",
+ TO_STAGE_RESPONSE_MOCK,
+ )
+ def test_invalid_empty_flow(self):
+ """Tests that an empty flow returns the correct error message"""
+ flow = create_test_flow(
+ FlowDesignation.AUTHENTICATION,
+ )
+
+ CONFIG.update_from_dict({"domain": "testserver"})
+ response = self.client.get(
+ reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
+ )
+ self.assertEqual(response.status_code, 302)
+ self.assertEqual(response.url, reverse("authentik_core:root-redirect"))
+
def test_multi_stage_flow(self):
"""Test a full flow with multiple stages"""
- flow = Flow.objects.create(
- name="test-full",
- slug="test-full",
- designation=FlowDesignation.AUTHENTICATION,
+ flow = create_test_flow(
+ FlowDesignation.AUTHENTICATION,
)
FlowStageBinding.objects.create(
- target=flow, stage=DummyStage.objects.create(name="dummy1"), order=0
+ target=flow, stage=DummyStage.objects.create(name=generate_id()), order=0
)
FlowStageBinding.objects.create(
- target=flow, stage=DummyStage.objects.create(name="dummy2"), order=1
+ target=flow, stage=DummyStage.objects.create(name=generate_id()), order=1
)
exec_url = reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug})
@@ -170,19 +189,19 @@ class TestFlowExecutor(FlowTestCase):
)
def test_reevaluate_remove_last(self):
"""Test planner with re-evaluate (last stage is removed)"""
- flow = Flow.objects.create(
- name="test-default-context",
- slug="test-default-context",
- designation=FlowDesignation.AUTHENTICATION,
+ flow = create_test_flow(
+ FlowDesignation.AUTHENTICATION,
+ )
+ false_policy = DummyPolicy.objects.create(
+ name=generate_id(), result=False, wait_min=1, wait_max=2
)
- false_policy = DummyPolicy.objects.create(result=False, wait_min=1, wait_max=2)
binding = FlowStageBinding.objects.create(
- target=flow, stage=DummyStage.objects.create(name="dummy1"), order=0
+ target=flow, stage=DummyStage.objects.create(name=generate_id()), order=0
)
binding2 = FlowStageBinding.objects.create(
target=flow,
- stage=DummyStage.objects.create(name="dummy2"),
+ stage=DummyStage.objects.create(name=generate_id()),
order=1,
re_evaluate_policies=True,
)
@@ -217,24 +236,24 @@ class TestFlowExecutor(FlowTestCase):
def test_reevaluate_remove_middle(self):
"""Test planner with re-evaluate (middle stage is removed)"""
- flow = Flow.objects.create(
- name="test-default-context",
- slug="test-default-context",
- designation=FlowDesignation.AUTHENTICATION,
+ flow = create_test_flow(
+ FlowDesignation.AUTHENTICATION,
+ )
+ false_policy = DummyPolicy.objects.create(
+ name=generate_id(), result=False, wait_min=1, wait_max=2
)
- false_policy = DummyPolicy.objects.create(result=False, wait_min=1, wait_max=2)
binding = FlowStageBinding.objects.create(
- target=flow, stage=DummyStage.objects.create(name="dummy1"), order=0
+ target=flow, stage=DummyStage.objects.create(name=generate_id()), order=0
)
binding2 = FlowStageBinding.objects.create(
target=flow,
- stage=DummyStage.objects.create(name="dummy2"),
+ stage=DummyStage.objects.create(name=generate_id()),
order=1,
re_evaluate_policies=True,
)
binding3 = FlowStageBinding.objects.create(
- target=flow, stage=DummyStage.objects.create(name="dummy3"), order=2
+ target=flow, stage=DummyStage.objects.create(name=generate_id()), order=2
)
PolicyBinding.objects.create(policy=false_policy, target=binding2, order=0)
@@ -277,24 +296,24 @@ class TestFlowExecutor(FlowTestCase):
def test_reevaluate_keep(self):
"""Test planner with re-evaluate (everything is kept)"""
- flow = Flow.objects.create(
- name="test-default-context",
- slug="test-default-context",
- designation=FlowDesignation.AUTHENTICATION,
+ flow = create_test_flow(
+ FlowDesignation.AUTHENTICATION,
+ )
+ true_policy = DummyPolicy.objects.create(
+ name=generate_id(), result=True, wait_min=1, wait_max=2
)
- true_policy = DummyPolicy.objects.create(result=True, wait_min=1, wait_max=2)
binding = FlowStageBinding.objects.create(
- target=flow, stage=DummyStage.objects.create(name="dummy1"), order=0
+ target=flow, stage=DummyStage.objects.create(name=generate_id()), order=0
)
binding2 = FlowStageBinding.objects.create(
target=flow,
- stage=DummyStage.objects.create(name="dummy2"),
+ stage=DummyStage.objects.create(name=generate_id()),
order=1,
re_evaluate_policies=True,
)
binding3 = FlowStageBinding.objects.create(
- target=flow, stage=DummyStage.objects.create(name="dummy3"), order=2
+ target=flow, stage=DummyStage.objects.create(name=generate_id()), order=2
)
PolicyBinding.objects.create(policy=true_policy, target=binding2, order=0)
@@ -347,30 +366,30 @@ class TestFlowExecutor(FlowTestCase):
def test_reevaluate_remove_consecutive(self):
"""Test planner with re-evaluate (consecutive stages are removed)"""
- flow = Flow.objects.create(
- name="test-default-context",
- slug="test-default-context",
- designation=FlowDesignation.AUTHENTICATION,
+ flow = create_test_flow(
+ FlowDesignation.AUTHENTICATION,
+ )
+ false_policy = DummyPolicy.objects.create(
+ name=generate_id(), result=False, wait_min=1, wait_max=2
)
- false_policy = DummyPolicy.objects.create(result=False, wait_min=1, wait_max=2)
binding = FlowStageBinding.objects.create(
- target=flow, stage=DummyStage.objects.create(name="dummy1"), order=0
+ target=flow, stage=DummyStage.objects.create(name=generate_id()), order=0
)
binding2 = FlowStageBinding.objects.create(
target=flow,
- stage=DummyStage.objects.create(name="dummy2"),
+ stage=DummyStage.objects.create(name=generate_id()),
order=1,
re_evaluate_policies=True,
)
binding3 = FlowStageBinding.objects.create(
target=flow,
- stage=DummyStage.objects.create(name="dummy3"),
+ stage=DummyStage.objects.create(name=generate_id()),
order=2,
re_evaluate_policies=True,
)
binding4 = FlowStageBinding.objects.create(
- target=flow, stage=DummyStage.objects.create(name="dummy4"), order=2
+ target=flow, stage=DummyStage.objects.create(name=generate_id()), order=2
)
PolicyBinding.objects.create(policy=false_policy, target=binding2, order=0)
@@ -415,13 +434,11 @@ class TestFlowExecutor(FlowTestCase):
def test_stageview_user_identifier(self):
"""Test PLAN_CONTEXT_PENDING_USER_IDENTIFIER"""
- flow = Flow.objects.create(
- name="test-default-context",
- slug="test-default-context",
- designation=FlowDesignation.AUTHENTICATION,
+ flow = create_test_flow(
+ FlowDesignation.AUTHENTICATION,
)
FlowStageBinding.objects.create(
- target=flow, stage=DummyStage.objects.create(name="dummy"), order=0
+ target=flow, stage=DummyStage.objects.create(name=generate_id()), order=0
)
ident = "test-identifier"
@@ -443,10 +460,8 @@ class TestFlowExecutor(FlowTestCase):
def test_invalid_restart(self):
"""Test flow that restarts on invalid entry"""
- flow = Flow.objects.create(
- name="restart-on-invalid",
- slug="restart-on-invalid",
- designation=FlowDesignation.AUTHENTICATION,
+ flow = create_test_flow(
+ FlowDesignation.AUTHENTICATION,
)
# Stage 0 is a deny stage that is added dynamically
# when the reputation policy says so
diff --git a/authentik/flows/views/executor.py b/authentik/flows/views/executor.py
index 677b7bf23..a75568bae 100644
--- a/authentik/flows/views/executor.py
+++ b/authentik/flows/views/executor.py
@@ -10,6 +10,7 @@ from django.http import Http404, HttpRequest, HttpResponse, HttpResponseRedirect
from django.http.request import QueryDict
from django.shortcuts import get_object_or_404, redirect
from django.template.response import TemplateResponse
+from django.urls import reverse
from django.utils.decorators import method_decorator
from django.views.decorators.clickjacking import xframe_options_sameorigin
from django.views.generic import View
@@ -37,6 +38,7 @@ from authentik.flows.exceptions import EmptyFlowException, FlowNonApplicableExce
from authentik.flows.models import (
ConfigurableStage,
Flow,
+ FlowDeniedAction,
FlowDesignation,
FlowStageBinding,
FlowToken,
@@ -54,6 +56,7 @@ from authentik.lib.sentry import SentryIgnoredException
from authentik.lib.utils.errors import exception_to_string
from authentik.lib.utils.reflection import all_subclasses, class_to_path
from authentik.lib.utils.urls import is_url_absolute, redirect_with_qs
+from authentik.policies.engine import PolicyEngine
from authentik.tenants.models import Tenant
LOGGER = get_logger()
@@ -131,12 +134,20 @@ class FlowExecutorView(APIView):
def handle_invalid_flow(self, exc: BaseException) -> HttpResponse:
"""When a flow is non-applicable check if user is on the correct domain"""
- if NEXT_ARG_NAME in self.request.GET:
- if not is_url_absolute(self.request.GET.get(NEXT_ARG_NAME)):
+ if self.flow.denied_action in [
+ FlowDeniedAction.CONTINUE,
+ FlowDeniedAction.MESSAGE_CONTINUE,
+ ]:
+ next_url = self.request.GET.get(NEXT_ARG_NAME)
+ if next_url and not is_url_absolute(next_url):
self._logger.debug("f(exec): Redirecting to next on fail")
- return redirect(self.request.GET.get(NEXT_ARG_NAME))
+ return to_stage_response(self.request, redirect(next_url))
+ if self.flow.denied_action == FlowDeniedAction.CONTINUE:
+ return to_stage_response(
+ self.request, redirect(reverse("authentik_core:root-redirect"))
+ )
message = exc.__doc__ if exc.__doc__ else str(exc)
- return self.stage_invalid(error_message=message)
+ return to_stage_response(self.request, self.stage_invalid(error_message=message))
def _check_flow_token(self, key: str) -> Optional[FlowPlan]:
"""Check if the user is using a flow token to restore a plan"""
@@ -187,7 +198,7 @@ class FlowExecutorView(APIView):
self.plan = self._initiate_plan()
except FlowNonApplicableException as exc:
self._logger.warning("f(exec): Flow not applicable to current user", exc=exc)
- return to_stage_response(self.request, self.handle_invalid_flow(exc))
+ return self.handle_invalid_flow(exc)
except EmptyFlowException as exc:
self._logger.warning("f(exec): Flow is empty", exc=exc)
# To match behaviour with loading an empty flow plan from cache,
@@ -470,6 +481,20 @@ class ToDefaultFlow(View):
designation: Optional[FlowDesignation] = None
+ def flow_by_policy(self, request: HttpRequest, **flow_filter) -> Optional[Flow]:
+ """Get a Flow by `**flow_filter` and check if the request from `request` can access it."""
+ flows = Flow.objects.filter(**flow_filter).order_by("slug")
+ for flow in flows:
+ engine = PolicyEngine(flow, request.user, request)
+ engine.build()
+ result = engine.result
+ if result.passing:
+ LOGGER.debug("flow_by_policy: flow passing", flow=flow)
+ return flow
+ LOGGER.warning("flow_by_policy: flow not passing", flow=flow, messages=result.messages)
+ LOGGER.debug("flow_by_policy: no flow found", filters=flow_filter)
+ return None
+
def dispatch(self, request: HttpRequest) -> HttpResponse:
tenant: Tenant = request.tenant
flow = None
@@ -480,7 +505,7 @@ class ToDefaultFlow(View):
flow = tenant.flow_invalidation
# If no flow was set, get the first based on slug and policy
if not flow:
- flow = Flow.with_policy(request, designation=self.designation)
+ flow = self.flow_by_policy(request, designation=self.designation)
# If we still don't have a flow, 404
if not flow:
raise Http404
diff --git a/authentik/lib/utils/errors.py b/authentik/lib/utils/errors.py
index e2b210571..f1207c3ce 100644
--- a/authentik/lib/utils/errors.py
+++ b/authentik/lib/utils/errors.py
@@ -1,10 +1,18 @@
"""error utils"""
-from traceback import format_tb
+from traceback import extract_tb
-TRACEBACK_HEADER = "Traceback (most recent call last):\n"
+from authentik.lib.utils.reflection import class_to_path
+
+TRACEBACK_HEADER = "Traceback (most recent call last):"
def exception_to_string(exc: Exception) -> str:
"""Convert exception to string stackrace"""
# Either use passed original exception or whatever we have
- return TRACEBACK_HEADER + "".join(format_tb(exc.__traceback__)) + str(exc)
+ return "\n".join(
+ [
+ TRACEBACK_HEADER,
+ *[x.rstrip() for x in extract_tb(exc.__traceback__).format()],
+ f"{class_to_path(exc.__class__)}: {str(exc)}",
+ ]
+ )
diff --git a/locale/en/LC_MESSAGES/django.po b/locale/en/LC_MESSAGES/django.po
index 1f75ef273..3eeb440d5 100644
--- a/locale/en/LC_MESSAGES/django.po
+++ b/locale/en/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2022-06-30 20:17+0000\n"
+"POT-Creation-Date: 2022-07-02 15:10+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME \n"
"Language-Team: LANGUAGE \n"
@@ -217,7 +217,7 @@ msgid "Powered by authentik"
msgstr ""
#: authentik/core/views/apps.py:48
-#: authentik/providers/oauth2/views/authorize.py:356
+#: authentik/providers/oauth2/views/authorize.py:364
#: authentik/providers/saml/views/sso.py:69
#, python-format
msgid "You're about to sign into %(application)s."
@@ -336,56 +336,60 @@ msgstr ""
msgid "Notification Webhook Mappings"
msgstr ""
-#: authentik/events/monitored_tasks.py:197
+#: authentik/events/monitored_tasks.py:191
msgid "Task has not been run yet."
msgstr ""
-#: authentik/flows/api/flows.py:227 authentik/flows/api/flows.py:249
+#: authentik/flows/api/flows.py:228 authentik/flows/api/flows.py:250
#, python-format
msgid "Policy (%(type)s)"
msgstr ""
-#: authentik/flows/api/flows.py:258
+#: authentik/flows/api/flows.py:259
#, python-format
msgid "Stage (%(type)s)"
msgstr ""
-#: authentik/flows/api/flows.py:373
+#: authentik/flows/api/flows.py:374
#, python-format
msgid "Flow not applicable to current user/request: %(messages)s"
msgstr ""
-#: authentik/flows/models.py:108
+#: authentik/flows/models.py:117
msgid "Visible in the URL."
msgstr ""
-#: authentik/flows/models.py:110
+#: authentik/flows/models.py:119
msgid "Shown as the Title in Flow pages."
msgstr ""
-#: authentik/flows/models.py:128
+#: authentik/flows/models.py:137
msgid "Background shown during execution"
msgstr ""
-#: authentik/flows/models.py:135
+#: authentik/flows/models.py:144
msgid ""
"Enable compatibility mode, increases compatibility with password managers on "
"mobile devices."
msgstr ""
-#: authentik/flows/models.py:180
+#: authentik/flows/models.py:152
+msgid "Configure what should happen when a flow denies access to a user."
+msgstr ""
+
+#: authentik/flows/models.py:178
msgid "Flow"
msgstr ""
-#: authentik/flows/models.py:181
+#: authentik/flows/models.py:179
msgid "Flows"
msgstr ""
-#: authentik/flows/models.py:211
+#: authentik/flows/models.py:209
msgid "Evaluate policies when the Stage is present to the user."
msgstr ""
-#: authentik/flows/models.py:218
+#: authentik/flows/models.py:216
msgid ""
"Configure how the flow executor should handle an invalid response to a "
"challenge. RETRY returns the error message and a similar challenge to the "
@@ -393,19 +397,19 @@ msgid ""
"RESTART_WITH_CONTEXT restarts the flow while keeping the current context."
msgstr ""
-#: authentik/flows/models.py:242
+#: authentik/flows/models.py:240
msgid "Flow Stage Binding"
msgstr ""
-#: authentik/flows/models.py:243
+#: authentik/flows/models.py:241
msgid "Flow Stage Bindings"
msgstr ""
-#: authentik/flows/models.py:293
+#: authentik/flows/models.py:291
msgid "Flow Token"
msgstr ""
-#: authentik/flows/models.py:294
+#: authentik/flows/models.py:292
msgid "Flow Tokens"
msgstr ""
@@ -833,7 +837,7 @@ msgstr ""
msgid "OAuth2 Tokens"
msgstr ""
-#: authentik/providers/oauth2/views/authorize.py:410
+#: authentik/providers/oauth2/views/authorize.py:418
#: authentik/providers/saml/views/flows.py:86
#, python-format
msgid "Redirecting to %(app)s..."
@@ -856,42 +860,42 @@ msgstr ""
msgid "authentik API Access on behalf of your user"
msgstr ""
-#: authentik/providers/proxy/models.py:52
+#: authentik/providers/proxy/models.py:47
msgid "Validate SSL Certificates of upstream servers"
msgstr ""
-#: authentik/providers/proxy/models.py:53
+#: authentik/providers/proxy/models.py:48
msgid "Internal host SSL Validation"
msgstr ""
-#: authentik/providers/proxy/models.py:59
+#: authentik/providers/proxy/models.py:54
msgid ""
"Enable support for forwardAuth in traefik and nginx auth_request. Exclusive "
"with internal_host."
msgstr ""
-#: authentik/providers/proxy/models.py:77
+#: authentik/providers/proxy/models.py:72
msgid "Set HTTP-Basic Authentication"
msgstr ""
-#: authentik/providers/proxy/models.py:79
+#: authentik/providers/proxy/models.py:74
msgid ""
"Set a custom HTTP-Basic Authentication header based on values from authentik."
msgstr ""
-#: authentik/providers/proxy/models.py:84
+#: authentik/providers/proxy/models.py:79
msgid "HTTP-Basic Username Key"
msgstr ""
-#: authentik/providers/proxy/models.py:94
+#: authentik/providers/proxy/models.py:89
msgid "HTTP-Basic Password Key"
msgstr ""
-#: authentik/providers/proxy/models.py:149
+#: authentik/providers/proxy/models.py:144
msgid "Proxy Provider"
msgstr ""
-#: authentik/providers/proxy/models.py:150
+#: authentik/providers/proxy/models.py:145
msgid "Proxy Providers"
msgstr ""
@@ -996,77 +1000,77 @@ msgstr ""
msgid "Used recovery-link to authenticate."
msgstr ""
-#: authentik/sources/ldap/models.py:32
+#: authentik/sources/ldap/models.py:33
msgid "Server URI"
msgstr ""
-#: authentik/sources/ldap/models.py:40
+#: authentik/sources/ldap/models.py:41
msgid ""
"Optionally verify the LDAP Server's Certificate against the CA Chain in this "
"keypair."
msgstr ""
-#: authentik/sources/ldap/models.py:45
+#: authentik/sources/ldap/models.py:46
msgid "Bind CN"
msgstr ""
-#: authentik/sources/ldap/models.py:47
+#: authentik/sources/ldap/models.py:48
msgid "Enable Start TLS"
msgstr ""
-#: authentik/sources/ldap/models.py:49
+#: authentik/sources/ldap/models.py:50
msgid "Base DN"
msgstr ""
-#: authentik/sources/ldap/models.py:51
+#: authentik/sources/ldap/models.py:52
msgid "Prepended to Base DN for User-queries."
msgstr ""
-#: authentik/sources/ldap/models.py:52
+#: authentik/sources/ldap/models.py:53
msgid "Addition User DN"
msgstr ""
-#: authentik/sources/ldap/models.py:56
+#: authentik/sources/ldap/models.py:57
msgid "Prepended to Base DN for Group-queries."
msgstr ""
-#: authentik/sources/ldap/models.py:57
+#: authentik/sources/ldap/models.py:58
msgid "Addition Group DN"
msgstr ""
-#: authentik/sources/ldap/models.py:63
+#: authentik/sources/ldap/models.py:64
msgid "Consider Objects matching this filter to be Users."
msgstr ""
-#: authentik/sources/ldap/models.py:66
+#: authentik/sources/ldap/models.py:67
msgid "Field which contains members of a group."
msgstr ""
-#: authentik/sources/ldap/models.py:70
+#: authentik/sources/ldap/models.py:71
msgid "Consider Objects matching this filter to be Groups."
msgstr ""
-#: authentik/sources/ldap/models.py:73
+#: authentik/sources/ldap/models.py:74
msgid "Field which contains a unique Identifier."
msgstr ""
-#: authentik/sources/ldap/models.py:80
+#: authentik/sources/ldap/models.py:81
msgid "Property mappings used for group creation/updating."
msgstr ""
-#: authentik/sources/ldap/models.py:145
+#: authentik/sources/ldap/models.py:149
msgid "LDAP Source"
msgstr ""
-#: authentik/sources/ldap/models.py:146
+#: authentik/sources/ldap/models.py:150
msgid "LDAP Sources"
msgstr ""
-#: authentik/sources/ldap/models.py:169
+#: authentik/sources/ldap/models.py:173
msgid "LDAP Property Mapping"
msgstr ""
-#: authentik/sources/ldap/models.py:170
+#: authentik/sources/ldap/models.py:174
msgid "LDAP Property Mappings"
msgstr ""
diff --git a/schema.yml b/schema.yml
index 73aee7f94..2b5c9ae9f 100644
--- a/schema.yml
+++ b/schema.yml
@@ -5140,6 +5140,15 @@ paths:
operationId: flows_instances_list
description: Flow Viewset
parameters:
+ - in: query
+ name: denied_action
+ schema:
+ type: string
+ enum:
+ - continue
+ - message
+ - message_continue
+ description: Configure what should happen when a flow denies access to a user.
- in: query
name: designation
schema:
@@ -20656,6 +20665,12 @@ components:
- branding_title
- matched_domain
- ui_footer_links
+ DeniedActionEnum:
+ enum:
+ - message_continue
+ - message
+ - continue
+ type: string
DenyStage:
type: object
description: DenyStage Serializer
@@ -21571,6 +21586,11 @@ components:
readOnly: true
layout:
$ref: '#/components/schemas/LayoutEnum'
+ denied_action:
+ allOf:
+ - $ref: '#/components/schemas/DeniedActionEnum'
+ description: Configure what should happen when a flow denies access to a
+ user.
required:
- background
- cache_count
@@ -21708,6 +21728,11 @@ components:
managers on mobile devices.
layout:
$ref: '#/components/schemas/LayoutEnum'
+ denied_action:
+ allOf:
+ - $ref: '#/components/schemas/DeniedActionEnum'
+ description: Configure what should happen when a flow denies access to a
+ user.
required:
- designation
- name
@@ -27269,6 +27294,11 @@ components:
managers on mobile devices.
layout:
$ref: '#/components/schemas/LayoutEnum'
+ denied_action:
+ allOf:
+ - $ref: '#/components/schemas/DeniedActionEnum'
+ description: Configure what should happen when a flow denies access to a
+ user.
PatchedFlowStageBindingRequest:
type: object
description: FlowStageBinding Serializer
diff --git a/web/src/locales/de.po b/web/src/locales/de.po
index 263eb8d7c..17d39c98a 100644
--- a/web/src/locales/de.po
+++ b/web/src/locales/de.po
@@ -789,6 +789,10 @@ msgstr "Standardmäßig werden für Quellen nur Symbole angezeigt. Aktiviere die
msgid "CA which the endpoint's Certificate is verified against. Can be left empty for no validation."
msgstr "CA, anhand derer das Zertifikat des Endpunkts überprüft wird. Kann leer gelassen werden, um keine Validierung durchzuführen."
+#: src/pages/flows/FlowForm.ts
+msgid "CONTINUE will either follow the ?next parameter or redirect to the default interface."
+msgstr ""
+
#: src/pages/providers/ldap/LDAPProviderForm.ts
msgid "Cached binding, flow is executed and session is cached in memory. Flow is executed when session expires."
msgstr ""
@@ -1530,6 +1534,10 @@ msgstr "Deaktivieren"
msgid "Debug"
msgstr "Debuggen"
+#: src/pages/flows/FlowForm.ts
+msgid "Decides the response when a policy denies access to this flow for a user."
+msgstr ""
+
#: src/pages/flows/FlowForm.ts
msgid "Decides what this Flow is used for. For example, the Authentication flow is redirect to when an un-authenticated user visits authentik."
msgstr "Entscheidet, wofür dieser Flow verwendet wird. Beispielsweise wird der Authentifizierungsablauf umgeleitet, wenn ein nicht authentifizierter Benutzer authentik besucht."
@@ -1627,6 +1635,10 @@ msgstr ""
msgid "Delete {0}"
msgstr "{0} löschen"
+#: src/pages/flows/FlowForm.ts
+msgid "Denied action"
+msgstr ""
+
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
msgid "Deny the user access"
msgstr "Dem Benutzer den Zugang verweigern"
@@ -3174,6 +3186,14 @@ msgstr "Logs"
msgid "Long-running operations which authentik executes in the background."
msgstr "Langlaufende Operationen, die Authentik im Hintergrund ausführt."
+#: src/pages/flows/FlowForm.ts
+msgid "MESSAGE will notify the user the flow isn't applicable."
+msgstr ""
+
+#: src/pages/flows/FlowForm.ts
+msgid "MESSAGE_CONTINUE will follow the ?next parameter if set, otherwise show a message."
+msgstr ""
+
#: src/user/user-settings/UserSettingsPage.ts
msgid "MFA Devices"
msgstr "Multifaktor-Authentifzierungs Geräte"
@@ -4262,13 +4282,17 @@ msgid "Quick actions"
msgstr "Schnellaktionen"
#: src/pages/flows/StageBindingForm.ts
-msgid "RESTART restarts the flow from the beginning, while keeping the flow context."
-msgstr "RESTART startet den Flow von Anfang an neu, während der Flow-Kontext beibehalten wird."
+#~ msgid "RESTART restarts the flow from the beginning, while keeping the flow context."
+#~ msgstr "RESTART startet den Flow von Anfang an neu, während der Flow-Kontext beibehalten wird."
#: src/pages/flows/StageBindingForm.ts
msgid "RESTART restarts the flow from the beginning."
msgstr "RESTART startet den Flow von Anfang an neu."
+#: src/pages/flows/StageBindingForm.ts
+msgid "RESTART_WITH_CONTEXT restarts the flow from the beginning, while keeping the flow context."
+msgstr ""
+
#: src/pages/flows/StageBindingForm.ts
msgid "RETRY returns the error message and a similar challenge to the executor."
msgstr "RETRY gibt die Fehlermeldung und eine ähnliche Aufforderung an den Executor zurück."
diff --git a/web/src/locales/en.po b/web/src/locales/en.po
index 438d1fca2..26a36f49b 100644
--- a/web/src/locales/en.po
+++ b/web/src/locales/en.po
@@ -786,6 +786,10 @@ msgstr "By default, only icons are shown for sources. Enable this to show their
msgid "CA which the endpoint's Certificate is verified against. Can be left empty for no validation."
msgstr "CA which the endpoint's Certificate is verified against. Can be left empty for no validation."
+#: src/pages/flows/FlowForm.ts
+msgid "CONTINUE will either follow the ?next parameter or redirect to the default interface."
+msgstr "CONTINUE will either follow the ?next parameter or redirect to the default interface."
+
#: src/pages/providers/ldap/LDAPProviderForm.ts
msgid "Cached binding, flow is executed and session is cached in memory. Flow is executed when session expires."
msgstr "Cached binding, flow is executed and session is cached in memory. Flow is executed when session expires."
@@ -1542,6 +1546,10 @@ msgstr "Deactivate"
msgid "Debug"
msgstr "Debug"
+#: src/pages/flows/FlowForm.ts
+msgid "Decides the response when a policy denies access to this flow for a user."
+msgstr "Decides the response when a policy denies access to this flow for a user."
+
#: src/pages/flows/FlowForm.ts
msgid "Decides what this Flow is used for. For example, the Authentication flow is redirect to when an un-authenticated user visits authentik."
msgstr "Decides what this Flow is used for. For example, the Authentication flow is redirect to when an un-authenticated user visits authentik."
@@ -1645,6 +1653,10 @@ msgstr ""
msgid "Delete {0}"
msgstr "Delete {0}"
+#: src/pages/flows/FlowForm.ts
+msgid "Denied action"
+msgstr "Denied action"
+
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
msgid "Deny the user access"
msgstr "Deny the user access"
@@ -3226,6 +3238,14 @@ msgstr "Logs"
msgid "Long-running operations which authentik executes in the background."
msgstr "Long-running operations which authentik executes in the background."
+#: src/pages/flows/FlowForm.ts
+msgid "MESSAGE will notify the user the flow isn't applicable."
+msgstr "MESSAGE will notify the user the flow isn't applicable."
+
+#: src/pages/flows/FlowForm.ts
+msgid "MESSAGE_CONTINUE will follow the ?next parameter if set, otherwise show a message."
+msgstr "MESSAGE_CONTINUE will follow the ?next parameter if set, otherwise show a message."
+
#: src/user/user-settings/UserSettingsPage.ts
msgid "MFA Devices"
msgstr "MFA Devices"
@@ -4332,13 +4352,17 @@ msgid "Quick actions"
msgstr "Quick actions"
#: src/pages/flows/StageBindingForm.ts
-msgid "RESTART restarts the flow from the beginning, while keeping the flow context."
-msgstr "RESTART restarts the flow from the beginning, while keeping the flow context."
+#~ msgid "RESTART restarts the flow from the beginning, while keeping the flow context."
+#~ msgstr "RESTART restarts the flow from the beginning, while keeping the flow context."
#: src/pages/flows/StageBindingForm.ts
msgid "RESTART restarts the flow from the beginning."
msgstr "RESTART restarts the flow from the beginning."
+#: src/pages/flows/StageBindingForm.ts
+msgid "RESTART_WITH_CONTEXT restarts the flow from the beginning, while keeping the flow context."
+msgstr "RESTART_WITH_CONTEXT restarts the flow from the beginning, while keeping the flow context."
+
#: src/pages/flows/StageBindingForm.ts
msgid "RETRY returns the error message and a similar challenge to the executor."
msgstr "RETRY returns the error message and a similar challenge to the executor."
diff --git a/web/src/locales/es.po b/web/src/locales/es.po
index 2edf801ca..937821520 100644
--- a/web/src/locales/es.po
+++ b/web/src/locales/es.po
@@ -779,6 +779,10 @@ msgstr "De forma predeterminada, solo se muestran los iconos de las fuentes. Act
msgid "CA which the endpoint's Certificate is verified against. Can be left empty for no validation."
msgstr "CA con la que se verifica el certificado del punto final. Se puede dejar vacío para que no se valide."
+#: src/pages/flows/FlowForm.ts
+msgid "CONTINUE will either follow the ?next parameter or redirect to the default interface."
+msgstr ""
+
#: src/pages/providers/ldap/LDAPProviderForm.ts
msgid "Cached binding, flow is executed and session is cached in memory. Flow is executed when session expires."
msgstr ""
@@ -1521,6 +1525,10 @@ msgstr "Desactivar"
msgid "Debug"
msgstr "Depurar"
+#: src/pages/flows/FlowForm.ts
+msgid "Decides the response when a policy denies access to this flow for a user."
+msgstr ""
+
#: src/pages/flows/FlowForm.ts
msgid "Decides what this Flow is used for. For example, the Authentication flow is redirect to when an un-authenticated user visits authentik."
msgstr "Decide para qué se utiliza este flujo. Por ejemplo, el flujo de autenticación se redirige a cuando un usuario no autenticado visita authentick."
@@ -1618,6 +1626,10 @@ msgstr ""
msgid "Delete {0}"
msgstr "Eliminar {0}"
+#: src/pages/flows/FlowForm.ts
+msgid "Denied action"
+msgstr ""
+
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
msgid "Deny the user access"
msgstr "Denegar el acceso al usuario"
@@ -3167,6 +3179,14 @@ msgstr "troncos"
msgid "Long-running operations which authentik executes in the background."
msgstr "Operaciones de larga ejecución que authentik se ejecuta en segundo plano."
+#: src/pages/flows/FlowForm.ts
+msgid "MESSAGE will notify the user the flow isn't applicable."
+msgstr ""
+
+#: src/pages/flows/FlowForm.ts
+msgid "MESSAGE_CONTINUE will follow the ?next parameter if set, otherwise show a message."
+msgstr ""
+
#: src/user/user-settings/UserSettingsPage.ts
msgid "MFA Devices"
msgstr "Dispositivos de MFA"
@@ -4255,13 +4275,17 @@ msgid "Quick actions"
msgstr "Acciones rápidas"
#: src/pages/flows/StageBindingForm.ts
-msgid "RESTART restarts the flow from the beginning, while keeping the flow context."
-msgstr "RESTART reinicia el flujo desde el principio, a la vez que mantiene el contexto del flujo."
+#~ msgid "RESTART restarts the flow from the beginning, while keeping the flow context."
+#~ msgstr "RESTART reinicia el flujo desde el principio, a la vez que mantiene el contexto del flujo."
#: src/pages/flows/StageBindingForm.ts
msgid "RESTART restarts the flow from the beginning."
msgstr "RESTART reinicia el flujo desde el principio."
+#: src/pages/flows/StageBindingForm.ts
+msgid "RESTART_WITH_CONTEXT restarts the flow from the beginning, while keeping the flow context."
+msgstr ""
+
#: src/pages/flows/StageBindingForm.ts
msgid "RETRY returns the error message and a similar challenge to the executor."
msgstr "RETRY devuelve el mensaje de error y un desafío similar para el ejecutor."
diff --git a/web/src/locales/fr_FR.po b/web/src/locales/fr_FR.po
index 6f5b2bf8d..ba7bfdcc6 100644
--- a/web/src/locales/fr_FR.po
+++ b/web/src/locales/fr_FR.po
@@ -785,6 +785,10 @@ msgstr ""
msgid "CA which the endpoint's Certificate is verified against. Can be left empty for no validation."
msgstr "AC auprès de laquelle le certificat du terminal est vérifié. Peut être laissé vide en l'absence de validation."
+#: src/pages/flows/FlowForm.ts
+msgid "CONTINUE will either follow the ?next parameter or redirect to the default interface."
+msgstr ""
+
#: src/pages/providers/ldap/LDAPProviderForm.ts
msgid "Cached binding, flow is executed and session is cached in memory. Flow is executed when session expires."
msgstr ""
@@ -1535,6 +1539,10 @@ msgstr "Désactiver"
msgid "Debug"
msgstr ""
+#: src/pages/flows/FlowForm.ts
+msgid "Decides the response when a policy denies access to this flow for a user."
+msgstr ""
+
#: src/pages/flows/FlowForm.ts
msgid "Decides what this Flow is used for. For example, the Authentication flow is redirect to when an un-authenticated user visits authentik."
msgstr "Détermine l'usage de ce flux. Par exemple, un flux d'authentification est la destination d'un visiteur d'authentik non authentifié."
@@ -1630,6 +1638,10 @@ msgstr "Supprimer l'utilisateur en attente. ATTENTION, cette étape ne demande a
msgid "Delete {0}"
msgstr "Supprimer {0}"
+#: src/pages/flows/FlowForm.ts
+msgid "Denied action"
+msgstr ""
+
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
msgid "Deny the user access"
msgstr "Refuser l'accès à l'utilisateu"
@@ -3198,6 +3210,14 @@ msgstr "Logs"
msgid "Long-running operations which authentik executes in the background."
msgstr "Opérations de longue durée qu'Authentik exécute en arrière-plan."
+#: src/pages/flows/FlowForm.ts
+msgid "MESSAGE will notify the user the flow isn't applicable."
+msgstr ""
+
+#: src/pages/flows/FlowForm.ts
+msgid "MESSAGE_CONTINUE will follow the ?next parameter if set, otherwise show a message."
+msgstr ""
+
#: src/user/user-settings/UserSettingsPage.ts
msgid "MFA Devices"
msgstr ""
@@ -4291,13 +4311,17 @@ msgid "Quick actions"
msgstr ""
#: src/pages/flows/StageBindingForm.ts
-msgid "RESTART restarts the flow from the beginning, while keeping the flow context."
-msgstr "REDÉMARRER redémarre le flux depuis le début, en gardant le contexte du flux."
+#~ msgid "RESTART restarts the flow from the beginning, while keeping the flow context."
+#~ msgstr "REDÉMARRER redémarre le flux depuis le début, en gardant le contexte du flux."
#: src/pages/flows/StageBindingForm.ts
msgid "RESTART restarts the flow from the beginning."
msgstr "REDÉMARRER redémarre le flux depuis le début."
+#: src/pages/flows/StageBindingForm.ts
+msgid "RESTART_WITH_CONTEXT restarts the flow from the beginning, while keeping the flow context."
+msgstr ""
+
#: src/pages/flows/StageBindingForm.ts
msgid "RETRY returns the error message and a similar challenge to the executor."
msgstr "RETRY indiquer le message d'erreur et présenter un challenge similaire à l'utilisateur"
diff --git a/web/src/locales/pl.po b/web/src/locales/pl.po
index 7bc4f513f..e8a81f41f 100644
--- a/web/src/locales/pl.po
+++ b/web/src/locales/pl.po
@@ -776,6 +776,10 @@ msgstr "Domyślnie dla źródeł wyświetlane są tylko ikony. Włącz tę opcj
msgid "CA which the endpoint's Certificate is verified against. Can be left empty for no validation."
msgstr "CA względem którego weryfikowany jest certyfikat. Można pozostawić puste, aby nie sprawdzać poprawności."
+#: src/pages/flows/FlowForm.ts
+msgid "CONTINUE will either follow the ?next parameter or redirect to the default interface."
+msgstr ""
+
#: src/pages/providers/ldap/LDAPProviderForm.ts
msgid "Cached binding, flow is executed and session is cached in memory. Flow is executed when session expires."
msgstr ""
@@ -1518,6 +1522,10 @@ msgstr "Dezaktywuj"
msgid "Debug"
msgstr "Debug"
+#: src/pages/flows/FlowForm.ts
+msgid "Decides the response when a policy denies access to this flow for a user."
+msgstr ""
+
#: src/pages/flows/FlowForm.ts
msgid "Decides what this Flow is used for. For example, the Authentication flow is redirect to when an un-authenticated user visits authentik."
msgstr "Decyduje, do czego służy ten przepływ. Na przykład przepływ uwierzytelniania służy do przekierowania nieuwierzytelnionego użytkownika który odwiedza authentik."
@@ -1615,6 +1623,10 @@ msgstr ""
msgid "Delete {0}"
msgstr "Usuń {0}"
+#: src/pages/flows/FlowForm.ts
+msgid "Denied action"
+msgstr ""
+
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
msgid "Deny the user access"
msgstr "Odmów użytkownikowi dostępu"
@@ -3164,6 +3176,14 @@ msgstr "Logi"
msgid "Long-running operations which authentik executes in the background."
msgstr "Długotrwałe operacje, które authentik wykonuje w tle."
+#: src/pages/flows/FlowForm.ts
+msgid "MESSAGE will notify the user the flow isn't applicable."
+msgstr ""
+
+#: src/pages/flows/FlowForm.ts
+msgid "MESSAGE_CONTINUE will follow the ?next parameter if set, otherwise show a message."
+msgstr ""
+
#: src/user/user-settings/UserSettingsPage.ts
msgid "MFA Devices"
msgstr "Urządzenia MFA"
@@ -4252,13 +4272,17 @@ msgid "Quick actions"
msgstr "Szybkie akcje"
#: src/pages/flows/StageBindingForm.ts
-msgid "RESTART restarts the flow from the beginning, while keeping the flow context."
-msgstr "RESTART ponownie uruchamia przepływ od początku, zachowując kontekst przepływu."
+#~ msgid "RESTART restarts the flow from the beginning, while keeping the flow context."
+#~ msgstr "RESTART ponownie uruchamia przepływ od początku, zachowując kontekst przepływu."
#: src/pages/flows/StageBindingForm.ts
msgid "RESTART restarts the flow from the beginning."
msgstr "RESTART ponownie uruchamia przepływ od początku."
+#: src/pages/flows/StageBindingForm.ts
+msgid "RESTART_WITH_CONTEXT restarts the flow from the beginning, while keeping the flow context."
+msgstr ""
+
#: src/pages/flows/StageBindingForm.ts
msgid "RETRY returns the error message and a similar challenge to the executor."
msgstr "RETRY zwraca komunikat o błędzie i podobne wyzwanie do executora."
diff --git a/web/src/locales/pseudo-LOCALE.po b/web/src/locales/pseudo-LOCALE.po
index e3cd1b45a..6b89e2abf 100644
--- a/web/src/locales/pseudo-LOCALE.po
+++ b/web/src/locales/pseudo-LOCALE.po
@@ -778,6 +778,10 @@ msgstr ""
msgid "CA which the endpoint's Certificate is verified against. Can be left empty for no validation."
msgstr ""
+#: src/pages/flows/FlowForm.ts
+msgid "CONTINUE will either follow the ?next parameter or redirect to the default interface."
+msgstr ""
+
#: src/pages/providers/ldap/LDAPProviderForm.ts
msgid "Cached binding, flow is executed and session is cached in memory. Flow is executed when session expires."
msgstr ""
@@ -1530,6 +1534,10 @@ msgstr ""
msgid "Debug"
msgstr ""
+#: src/pages/flows/FlowForm.ts
+msgid "Decides the response when a policy denies access to this flow for a user."
+msgstr ""
+
#: src/pages/flows/FlowForm.ts
msgid "Decides what this Flow is used for. For example, the Authentication flow is redirect to when an un-authenticated user visits authentik."
msgstr ""
@@ -1631,6 +1639,10 @@ msgstr ""
msgid "Delete {0}"
msgstr ""
+#: src/pages/flows/FlowForm.ts
+msgid "Denied action"
+msgstr ""
+
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
msgid "Deny the user access"
msgstr ""
@@ -3208,6 +3220,14 @@ msgstr ""
msgid "Long-running operations which authentik executes in the background."
msgstr ""
+#: src/pages/flows/FlowForm.ts
+msgid "MESSAGE will notify the user the flow isn't applicable."
+msgstr ""
+
+#: src/pages/flows/FlowForm.ts
+msgid "MESSAGE_CONTINUE will follow the ?next parameter if set, otherwise show a message."
+msgstr ""
+
#: src/user/user-settings/UserSettingsPage.ts
msgid "MFA Devices"
msgstr ""
@@ -4312,13 +4332,17 @@ msgid "Quick actions"
msgstr ""
#: src/pages/flows/StageBindingForm.ts
-msgid "RESTART restarts the flow from the beginning, while keeping the flow context."
-msgstr ""
+#~ msgid "RESTART restarts the flow from the beginning, while keeping the flow context."
+#~ msgstr ""
#: src/pages/flows/StageBindingForm.ts
msgid "RESTART restarts the flow from the beginning."
msgstr ""
+#: src/pages/flows/StageBindingForm.ts
+msgid "RESTART_WITH_CONTEXT restarts the flow from the beginning, while keeping the flow context."
+msgstr ""
+
#: src/pages/flows/StageBindingForm.ts
msgid "RETRY returns the error message and a similar challenge to the executor."
msgstr ""
diff --git a/web/src/locales/tr.po b/web/src/locales/tr.po
index 380cfe25d..02be4c4dc 100644
--- a/web/src/locales/tr.po
+++ b/web/src/locales/tr.po
@@ -779,6 +779,10 @@ msgstr "Varsayılan olarak, kaynaklar için yalnızca simgeler gösterilir. Tam
msgid "CA which the endpoint's Certificate is verified against. Can be left empty for no validation."
msgstr "Uç noktanın Sertifikası karşı doğrulanan CA. Doğrulama yapılmadan boş bırakılabilir."
+#: src/pages/flows/FlowForm.ts
+msgid "CONTINUE will either follow the ?next parameter or redirect to the default interface."
+msgstr ""
+
#: src/pages/providers/ldap/LDAPProviderForm.ts
msgid "Cached binding, flow is executed and session is cached in memory. Flow is executed when session expires."
msgstr ""
@@ -1521,6 +1525,10 @@ msgstr "Devre dışı bırak"
msgid "Debug"
msgstr "Hata Ayıklama"
+#: src/pages/flows/FlowForm.ts
+msgid "Decides the response when a policy denies access to this flow for a user."
+msgstr ""
+
#: src/pages/flows/FlowForm.ts
msgid "Decides what this Flow is used for. For example, the Authentication flow is redirect to when an un-authenticated user visits authentik."
msgstr "Bu Akış'ın ne için kullanıldığına karar verir. Örneğin, kimliği doğrulanmamış bir kullanıcı authentik ziyaret ettiğinde kimlik doğrulama akışı yönlendirir."
@@ -1618,6 +1626,10 @@ msgstr ""
msgid "Delete {0}"
msgstr "{0} Sil"
+#: src/pages/flows/FlowForm.ts
+msgid "Denied action"
+msgstr ""
+
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
msgid "Deny the user access"
msgstr "Kullanıcı erişimini engelle"
@@ -3168,6 +3180,14 @@ msgstr "Günlükler"
msgid "Long-running operations which authentik executes in the background."
msgstr "authentik'in arka planda yürüttüğü uzun süreli işlemler."
+#: src/pages/flows/FlowForm.ts
+msgid "MESSAGE will notify the user the flow isn't applicable."
+msgstr ""
+
+#: src/pages/flows/FlowForm.ts
+msgid "MESSAGE_CONTINUE will follow the ?next parameter if set, otherwise show a message."
+msgstr ""
+
#: src/user/user-settings/UserSettingsPage.ts
msgid "MFA Devices"
msgstr "MFA Cihazları"
@@ -4257,13 +4277,17 @@ msgid "Quick actions"
msgstr "Hızlı eylemler"
#: src/pages/flows/StageBindingForm.ts
-msgid "RESTART restarts the flow from the beginning, while keeping the flow context."
-msgstr "RESTART, akış bağlamını korurken akışı baştan yeniden başlatır."
+#~ msgid "RESTART restarts the flow from the beginning, while keeping the flow context."
+#~ msgstr "RESTART, akış bağlamını korurken akışı baştan yeniden başlatır."
#: src/pages/flows/StageBindingForm.ts
msgid "RESTART restarts the flow from the beginning."
msgstr "RESTART, akışı baştan yeniden başlatır."
+#: src/pages/flows/StageBindingForm.ts
+msgid "RESTART_WITH_CONTEXT restarts the flow from the beginning, while keeping the flow context."
+msgstr ""
+
#: src/pages/flows/StageBindingForm.ts
msgid "RETRY returns the error message and a similar challenge to the executor."
msgstr "RETRY hata iletisi ve yürütücünün benzer bir meydan okuma döndürür."
diff --git a/web/src/locales/zh-Hans.po b/web/src/locales/zh-Hans.po
index f8389b4b9..ec8de56c7 100644
--- a/web/src/locales/zh-Hans.po
+++ b/web/src/locales/zh-Hans.po
@@ -774,6 +774,10 @@ msgstr "默认情况下,只为源显示图标。启用此选项可显示它们
msgid "CA which the endpoint's Certificate is verified against. Can be left empty for no validation."
msgstr "验证端点证书所依据的 CA。可以留空,表示不进行验证。"
+#: src/pages/flows/FlowForm.ts
+msgid "CONTINUE will either follow the ?next parameter or redirect to the default interface."
+msgstr ""
+
#: src/pages/providers/ldap/LDAPProviderForm.ts
msgid "Cached binding, flow is executed and session is cached in memory. Flow is executed when session expires."
msgstr "缓存绑定,流程与会话会在内存中执行与缓存。会话过期时执行流程。"
@@ -1516,6 +1520,10 @@ msgstr "停用"
msgid "Debug"
msgstr "调试"
+#: src/pages/flows/FlowForm.ts
+msgid "Decides the response when a policy denies access to this flow for a user."
+msgstr ""
+
#: src/pages/flows/FlowForm.ts
msgid "Decides what this Flow is used for. For example, the Authentication flow is redirect to when an un-authenticated user visits authentik."
msgstr "决定此流程的用途。例如,当未经身份验证的用户访问 authentik 时,会重定向到身份验证流程。"
@@ -1613,6 +1621,10 @@ msgstr ""
msgid "Delete {0}"
msgstr "删除 {0}"
+#: src/pages/flows/FlowForm.ts
+msgid "Denied action"
+msgstr ""
+
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
msgid "Deny the user access"
msgstr "拒绝用户访问"
@@ -3150,6 +3162,14 @@ msgstr "日志"
msgid "Long-running operations which authentik executes in the background."
msgstr "authentik 在后台执行的长时间运行的操作。"
+#: src/pages/flows/FlowForm.ts
+msgid "MESSAGE will notify the user the flow isn't applicable."
+msgstr ""
+
+#: src/pages/flows/FlowForm.ts
+msgid "MESSAGE_CONTINUE will follow the ?next parameter if set, otherwise show a message."
+msgstr ""
+
#: src/user/user-settings/UserSettingsPage.ts
msgid "MFA Devices"
msgstr "MFA 设备"
@@ -4228,13 +4248,17 @@ msgid "Quick actions"
msgstr "快速操作"
#: src/pages/flows/StageBindingForm.ts
-msgid "RESTART restarts the flow from the beginning, while keeping the flow context."
-msgstr "RESTART 从头开始重新启动流程,同时保留流程上下文。"
+#~ msgid "RESTART restarts the flow from the beginning, while keeping the flow context."
+#~ msgstr "RESTART 从头开始重新启动流程,同时保留流程上下文。"
#: src/pages/flows/StageBindingForm.ts
msgid "RESTART restarts the flow from the beginning."
msgstr "RESTART 从头开始重新启动流程。"
+#: src/pages/flows/StageBindingForm.ts
+msgid "RESTART_WITH_CONTEXT restarts the flow from the beginning, while keeping the flow context."
+msgstr ""
+
#: src/pages/flows/StageBindingForm.ts
msgid "RETRY returns the error message and a similar challenge to the executor."
msgstr "RETRY 向执行器返回错误消息和类似的质询。"
diff --git a/web/src/locales/zh-Hant.po b/web/src/locales/zh-Hant.po
index c4e484c1c..7e983f56f 100644
--- a/web/src/locales/zh-Hant.po
+++ b/web/src/locales/zh-Hant.po
@@ -776,6 +776,10 @@ msgstr "默认情况下,只为源显示图标。启用此选项可显示他们
msgid "CA which the endpoint's Certificate is verified against. Can be left empty for no validation."
msgstr "验证终端节点证书所依据的 CA。可以留空以表示不进行验证。"
+#: src/pages/flows/FlowForm.ts
+msgid "CONTINUE will either follow the ?next parameter or redirect to the default interface."
+msgstr ""
+
#: src/pages/providers/ldap/LDAPProviderForm.ts
msgid "Cached binding, flow is executed and session is cached in memory. Flow is executed when session expires."
msgstr ""
@@ -1518,6 +1522,10 @@ msgstr "停用"
msgid "Debug"
msgstr "调试"
+#: src/pages/flows/FlowForm.ts
+msgid "Decides the response when a policy denies access to this flow for a user."
+msgstr ""
+
#: src/pages/flows/FlowForm.ts
msgid "Decides what this Flow is used for. For example, the Authentication flow is redirect to when an un-authenticated user visits authentik."
msgstr "决定此 Flow 的用途。例如,当未经身份验证的用户访问 authentik 时,身份验证流程将重定向到。"
@@ -1615,6 +1623,10 @@ msgstr ""
msgid "Delete {0}"
msgstr "删除 {0}"
+#: src/pages/flows/FlowForm.ts
+msgid "Denied action"
+msgstr ""
+
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
msgid "Deny the user access"
msgstr "拒绝用户访问"
@@ -3154,6 +3166,14 @@ msgstr "日志"
msgid "Long-running operations which authentik executes in the background."
msgstr "authentik 在后台执行的长时间运行的操作。"
+#: src/pages/flows/FlowForm.ts
+msgid "MESSAGE will notify the user the flow isn't applicable."
+msgstr ""
+
+#: src/pages/flows/FlowForm.ts
+msgid "MESSAGE_CONTINUE will follow the ?next parameter if set, otherwise show a message."
+msgstr ""
+
#: src/user/user-settings/UserSettingsPage.ts
msgid "MFA Devices"
msgstr "MFA 设备"
@@ -4233,13 +4253,17 @@ msgid "Quick actions"
msgstr "快速行动"
#: src/pages/flows/StageBindingForm.ts
-msgid "RESTART restarts the flow from the beginning, while keeping the flow context."
-msgstr "RESTART 从头开始重新启动流程,同时保留流程上下文。"
+#~ msgid "RESTART restarts the flow from the beginning, while keeping the flow context."
+#~ msgstr "RESTART 从头开始重新启动流程,同时保留流程上下文。"
#: src/pages/flows/StageBindingForm.ts
msgid "RESTART restarts the flow from the beginning."
msgstr "RESTART 从头开始重新启动流程。"
+#: src/pages/flows/StageBindingForm.ts
+msgid "RESTART_WITH_CONTEXT restarts the flow from the beginning, while keeping the flow context."
+msgstr ""
+
#: src/pages/flows/StageBindingForm.ts
msgid "RETRY returns the error message and a similar challenge to the executor."
msgstr "RETRY 向执行器返回错误消息和类似的质询。"
diff --git a/web/src/locales/zh_TW.po b/web/src/locales/zh_TW.po
index c0baf5697..4e70ce85b 100644
--- a/web/src/locales/zh_TW.po
+++ b/web/src/locales/zh_TW.po
@@ -776,6 +776,10 @@ msgstr "默认情况下,只为源显示图标。启用此选项可显示他们
msgid "CA which the endpoint's Certificate is verified against. Can be left empty for no validation."
msgstr "验证终端节点证书所依据的 CA。可以留空以表示不进行验证。"
+#: src/pages/flows/FlowForm.ts
+msgid "CONTINUE will either follow the ?next parameter or redirect to the default interface."
+msgstr ""
+
#: src/pages/providers/ldap/LDAPProviderForm.ts
msgid "Cached binding, flow is executed and session is cached in memory. Flow is executed when session expires."
msgstr ""
@@ -1518,6 +1522,10 @@ msgstr "停用"
msgid "Debug"
msgstr "调试"
+#: src/pages/flows/FlowForm.ts
+msgid "Decides the response when a policy denies access to this flow for a user."
+msgstr ""
+
#: src/pages/flows/FlowForm.ts
msgid "Decides what this Flow is used for. For example, the Authentication flow is redirect to when an un-authenticated user visits authentik."
msgstr "决定此 Flow 的用途。例如,当未经身份验证的用户访问 authentik 时,身份验证流程将重定向到。"
@@ -1615,6 +1623,10 @@ msgstr ""
msgid "Delete {0}"
msgstr "删除 {0}"
+#: src/pages/flows/FlowForm.ts
+msgid "Denied action"
+msgstr ""
+
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
msgid "Deny the user access"
msgstr "拒绝用户访问"
@@ -3154,6 +3166,14 @@ msgstr "日志"
msgid "Long-running operations which authentik executes in the background."
msgstr "authentik 在后台执行的长时间运行的操作。"
+#: src/pages/flows/FlowForm.ts
+msgid "MESSAGE will notify the user the flow isn't applicable."
+msgstr ""
+
+#: src/pages/flows/FlowForm.ts
+msgid "MESSAGE_CONTINUE will follow the ?next parameter if set, otherwise show a message."
+msgstr ""
+
#: src/user/user-settings/UserSettingsPage.ts
msgid "MFA Devices"
msgstr "MFA 设备"
@@ -4233,13 +4253,17 @@ msgid "Quick actions"
msgstr "快速行动"
#: src/pages/flows/StageBindingForm.ts
-msgid "RESTART restarts the flow from the beginning, while keeping the flow context."
-msgstr "RESTART 从头开始重新启动流程,同时保留流程上下文。"
+#~ msgid "RESTART restarts the flow from the beginning, while keeping the flow context."
+#~ msgstr "RESTART 从头开始重新启动流程,同时保留流程上下文。"
#: src/pages/flows/StageBindingForm.ts
msgid "RESTART restarts the flow from the beginning."
msgstr "RESTART 从头开始重新启动流程。"
+#: src/pages/flows/StageBindingForm.ts
+msgid "RESTART_WITH_CONTEXT restarts the flow from the beginning, while keeping the flow context."
+msgstr ""
+
#: src/pages/flows/StageBindingForm.ts
msgid "RETRY returns the error message and a similar challenge to the executor."
msgstr "RETRY 向执行器返回错误消息和类似的质询。"
diff --git a/web/src/pages/flows/FlowForm.ts b/web/src/pages/flows/FlowForm.ts
index ea0c18e35..9a3b952fc 100644
--- a/web/src/pages/flows/FlowForm.ts
+++ b/web/src/pages/flows/FlowForm.ts
@@ -1,3 +1,4 @@
+import { DeniedActionEnum } from "@goauthentik/api/dist/models/DeniedActionEnum.js";
import { DEFAULT_CONFIG, config } from "@goauthentik/web/api/Config";
import "@goauthentik/web/elements/forms/HorizontalFormElement";
import { ModelForm } from "@goauthentik/web/elements/forms/ModelForm";
@@ -120,6 +121,27 @@ export class FlowForm extends ModelForm {
`;
}
+ renderDeniedAction(): TemplateResult {
+ return html`
+
+ `;
+ }
+
renderLayout(): TemplateResult {
return html`
+
+
+
+ ${t`Decides the response when a policy denies access to this flow for a user.`}
+
+
diff --git a/website/docs/flow/index.md b/website/docs/flow/index.md
index 5b21a708f..c085cc55e 100644
--- a/website/docs/flow/index.md
+++ b/website/docs/flow/index.md
@@ -20,6 +20,14 @@ Flows can have policies assigned to them. These policies determine if the curren
Keep in mind that in certain circumstances, policies cannot match against users and groups as there is no authenticated user yet.
+### Denied action
+
+Configure what happens when access to a flow is denied by a policy. By default, authentik will redirect to a `?next` parameter if set, and otherwise show an error message.
+
+- `MESSAGE_CONTINUE`: Show a message if no `?next` parameter is set, otherwise redirect.
+- `MESSAGE`: Always show error message.
+- `CONTINUE`: Always redirect, either to `?next` if set, otherwise to the default interface.
+
## Designation
Flows are designated for a single purpose. This designation changes when a flow is used. The following designations are available: