stage/deny: add custom message (#7144)

* stage/deny: add message

* add migration, tests and schema update

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* add form

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
Samir Musali 2023-10-18 18:13:33 +03:00 committed by GitHub
parent 4262bd6ace
commit a60f3b4b81
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 104 additions and 5 deletions

View File

@ -11,7 +11,7 @@ class DenyStageSerializer(StageSerializer):
class Meta: class Meta:
model = DenyStage model = DenyStage
fields = StageSerializer.Meta.fields fields = StageSerializer.Meta.fields + ["deny_message"]
class DenyStageViewSet(UsedByMixin, ModelViewSet): class DenyStageViewSet(UsedByMixin, ModelViewSet):

View File

@ -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=""),
),
]

View File

@ -1,5 +1,5 @@
"""deny stage models""" """deny stage models"""
from django.db import models
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.views import View from django.views import View
from rest_framework.serializers import BaseSerializer from rest_framework.serializers import BaseSerializer
@ -10,6 +10,8 @@ from authentik.flows.models import Stage
class DenyStage(Stage): class DenyStage(Stage):
"""Cancels the current flow.""" """Cancels the current flow."""
deny_message = models.TextField(blank=True, default="")
@property @property
def serializer(self) -> type[BaseSerializer]: def serializer(self) -> type[BaseSerializer]:
from authentik.stages.deny.api import DenyStageSerializer from authentik.stages.deny.api import DenyStageSerializer

View File

@ -2,6 +2,7 @@
from django.http import HttpRequest, HttpResponse from django.http import HttpRequest, HttpResponse
from authentik.flows.stage import StageView from authentik.flows.stage import StageView
from authentik.stages.deny.models import DenyStage
class DenyStageView(StageView): class DenyStageView(StageView):
@ -9,4 +10,6 @@ class DenyStageView(StageView):
def dispatch(self, request: HttpRequest) -> HttpResponse: def dispatch(self, request: HttpRequest) -> HttpResponse:
"""Cancels the current flow""" """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)

View File

@ -45,3 +45,38 @@ class TestUserDenyStage(FlowTestCase):
) )
self.assertStageResponse(response, self.flow, component="ak-stage-access-denied") 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"
)

View File

@ -7009,6 +7009,10 @@
] ]
}, },
"title": "Flow set" "title": "Flow set"
},
"deny_message": {
"type": "string",
"title": "Deny message"
} }
}, },
"required": [] "required": []

View File

@ -23909,6 +23909,10 @@ paths:
operationId: stages_deny_list operationId: stages_deny_list
description: DenyStage Viewset description: DenyStage Viewset
parameters: parameters:
- in: query
name: deny_message
schema:
type: string
- in: query - in: query
name: name name: name
schema: schema:
@ -29786,6 +29790,8 @@ components:
type: array type: array
items: items:
$ref: '#/components/schemas/FlowSet' $ref: '#/components/schemas/FlowSet'
deny_message:
type: string
required: required:
- component - component
- meta_model_name - meta_model_name
@ -29804,6 +29810,8 @@ components:
type: array type: array
items: items:
$ref: '#/components/schemas/FlowSetRequest' $ref: '#/components/schemas/FlowSetRequest'
deny_message:
type: string
required: required:
- name - name
Device: Device:
@ -35997,6 +36005,8 @@ components:
type: array type: array
items: items:
$ref: '#/components/schemas/FlowSetRequest' $ref: '#/components/schemas/FlowSetRequest'
deny_message:
type: string
PatchedDockerServiceConnectionRequest: PatchedDockerServiceConnectionRequest:
type: object type: object
description: DockerServiceConnection Serializer description: DockerServiceConnection Serializer

View File

@ -39,7 +39,8 @@ export class DenyStageForm extends ModelForm<DenyStage, string> {
} }
renderForm(): TemplateResult { renderForm(): TemplateResult {
return html` <span> return html`
<span>
${msg( ${msg(
"Statically deny the flow. To use this stage effectively, disable *Evaluate when flow is planned* on the respective binding.", "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<DenyStage, string> {
class="pf-c-form-control" class="pf-c-form-control"
required required
/> />
</ak-form-element-horizontal>`; </ak-form-element-horizontal>
<ak-form-group .expanded=${true}>
<span slot="header"> ${msg("Stage-specific settings")} </span>
<div slot="body" class="pf-c-form">
<ak-form-element-horizontal label=${msg("Deny message")} name="denyMessage">
<input
type="text"
value="${ifDefined(this.instance?.denyMessage || "")}"
class="pf-c-form-control"
required
/>
<p class="pf-c-form__helper-text">
${msg("Message shown when this stage is run.")}
</p>
</ak-form-element-horizontal>
</div>
</ak-form-group>
`;
} }
} }

View File

@ -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`. 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 #### User write stage
##### `groups` (List of [Group objects](../../user-group/group.md)) ##### `groups` (List of [Group objects](../../user-group/group.md))