diff --git a/authentik/stages/deny/api.py b/authentik/stages/deny/api.py index 0cda5c214..bdadff749 100644 --- a/authentik/stages/deny/api.py +++ b/authentik/stages/deny/api.py @@ -11,7 +11,7 @@ class DenyStageSerializer(StageSerializer): class Meta: model = DenyStage - fields = StageSerializer.Meta.fields + fields = StageSerializer.Meta.fields + ["deny_message"] class DenyStageViewSet(UsedByMixin, ModelViewSet): diff --git a/authentik/stages/deny/migrations/0002_denystage_deny_message.py b/authentik/stages/deny/migrations/0002_denystage_deny_message.py new file mode 100644 index 000000000..0709c1878 --- /dev/null +++ b/authentik/stages/deny/migrations/0002_denystage_deny_message.py @@ -0,0 +1,17 @@ +# Generated by Django 4.2.6 on 2023-10-18 09:11 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("authentik_stages_deny", "0001_initial"), + ] + + operations = [ + migrations.AddField( + model_name="denystage", + name="deny_message", + field=models.TextField(blank=True, default=""), + ), + ] diff --git a/authentik/stages/deny/models.py b/authentik/stages/deny/models.py index 4aa2e692f..c5ab2c251 100644 --- a/authentik/stages/deny/models.py +++ b/authentik/stages/deny/models.py @@ -1,5 +1,5 @@ """deny stage models""" - +from django.db import models from django.utils.translation import gettext_lazy as _ from django.views import View from rest_framework.serializers import BaseSerializer @@ -10,6 +10,8 @@ from authentik.flows.models import Stage class DenyStage(Stage): """Cancels the current flow.""" + deny_message = models.TextField(blank=True, default="") + @property def serializer(self) -> type[BaseSerializer]: from authentik.stages.deny.api import DenyStageSerializer diff --git a/authentik/stages/deny/stage.py b/authentik/stages/deny/stage.py index 2b38a299e..606f504a1 100644 --- a/authentik/stages/deny/stage.py +++ b/authentik/stages/deny/stage.py @@ -2,6 +2,7 @@ from django.http import HttpRequest, HttpResponse from authentik.flows.stage import StageView +from authentik.stages.deny.models import DenyStage class DenyStageView(StageView): @@ -9,4 +10,6 @@ class DenyStageView(StageView): def dispatch(self, request: HttpRequest) -> HttpResponse: """Cancels the current flow""" - return self.executor.stage_invalid() + stage: DenyStage = self.executor.current_stage + message = self.executor.plan.context.get("deny_message", stage.deny_message) + return self.executor.stage_invalid(message) diff --git a/authentik/stages/deny/tests.py b/authentik/stages/deny/tests.py index f72b097de..17e9d49bb 100644 --- a/authentik/stages/deny/tests.py +++ b/authentik/stages/deny/tests.py @@ -45,3 +45,38 @@ class TestUserDenyStage(FlowTestCase): ) self.assertStageResponse(response, self.flow, component="ak-stage-access-denied") + + def test_message_static(self): + """Test with a static error message""" + self.stage.deny_message = "foo" + self.stage.save() + 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.get( + reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}) + ) + + self.assertStageResponse( + response, self.flow, component="ak-stage-access-denied", error_message="foo" + ) + + def test_message_overwrite(self): + """Test with an overwritten error message""" + self.stage.deny_message = "foo" + self.stage.save() + plan = FlowPlan(flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]) + plan.context["deny_message"] = "bar" + 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.assertStageResponse( + response, self.flow, component="ak-stage-access-denied", error_message="bar" + ) diff --git a/blueprints/schema.json b/blueprints/schema.json index 57f624087..ca96a5e16 100644 --- a/blueprints/schema.json +++ b/blueprints/schema.json @@ -7009,6 +7009,10 @@ ] }, "title": "Flow set" + }, + "deny_message": { + "type": "string", + "title": "Deny message" } }, "required": [] diff --git a/schema.yml b/schema.yml index 219633aa2..a9edeb564 100644 --- a/schema.yml +++ b/schema.yml @@ -23909,6 +23909,10 @@ paths: operationId: stages_deny_list description: DenyStage Viewset parameters: + - in: query + name: deny_message + schema: + type: string - in: query name: name schema: @@ -29786,6 +29790,8 @@ components: type: array items: $ref: '#/components/schemas/FlowSet' + deny_message: + type: string required: - component - meta_model_name @@ -29804,6 +29810,8 @@ components: type: array items: $ref: '#/components/schemas/FlowSetRequest' + deny_message: + type: string required: - name Device: @@ -35997,6 +36005,8 @@ components: type: array items: $ref: '#/components/schemas/FlowSetRequest' + deny_message: + type: string PatchedDockerServiceConnectionRequest: type: object description: DockerServiceConnection Serializer diff --git a/web/src/admin/stages/deny/DenyStageForm.ts b/web/src/admin/stages/deny/DenyStageForm.ts index 929496a78..340e911d2 100644 --- a/web/src/admin/stages/deny/DenyStageForm.ts +++ b/web/src/admin/stages/deny/DenyStageForm.ts @@ -39,7 +39,8 @@ export class DenyStageForm extends ModelForm { } renderForm(): TemplateResult { - return html` + return html` + ${msg( "Statically deny the flow. To use this stage effectively, disable *Evaluate when flow is planned* on the respective binding.", )} @@ -51,6 +52,23 @@ export class DenyStageForm extends ModelForm { class="pf-c-form-control" required /> - `; + + + ${msg("Stage-specific settings")} +
+ + +

+ ${msg("Message shown when this stage is run.")} +

+
+
+
+ `; } } diff --git a/website/docs/flow/context/index.md b/website/docs/flow/context/index.md index 07b584092..e98e4d30d 100644 --- a/website/docs/flow/context/index.md +++ b/website/docs/flow/context/index.md @@ -98,6 +98,16 @@ URL that the form will be submitted to. Key-value pairs of the data that is included in the form and will be submitted to `url`. +#### Deny stage + +##### `deny_message` (string) + +:::info +Requires authentik 2023.10 +::: + +Optionally overwrite the deny message shown, has a higher priority than the message configured in the stage. + #### User write stage ##### `groups` (List of [Group objects](../../user-group/group.md))