From d1cde64214214eae68fa0a9992407e6d3e326186 Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Sun, 4 Apr 2021 13:06:17 +0200 Subject: [PATCH] stages/email: add support for custom template to API Signed-off-by: Jens Langhammer --- authentik/stages/email/api.py | 33 ++++++++++++++++++- .../migrations/0003_auto_20210404_1054.py | 18 ++++++++++ authentik/stages/email/models.py | 4 +-- swagger.yaml | 23 +++++++++++-- web/src/pages/stages/email/EmailStageForm.ts | 19 ++++++----- 5 files changed, 82 insertions(+), 15 deletions(-) create mode 100644 authentik/stages/email/migrations/0003_auto_20210404_1054.py diff --git a/authentik/stages/email/api.py b/authentik/stages/email/api.py index 116538b80..30a715229 100644 --- a/authentik/stages/email/api.py +++ b/authentik/stages/email/api.py @@ -1,8 +1,18 @@ """EmailStage API Views""" +from drf_yasg.utils import swagger_auto_schema +from rest_framework.decorators import action +from rest_framework.request import Request +from rest_framework.response import Response +from rest_framework.serializers import ValidationError from rest_framework.viewsets import ModelViewSet +from authentik.core.api.utils import TypeCreateSerializer from authentik.flows.api.stages import StageSerializer -from authentik.stages.email.models import EmailStage, get_template_choices +from authentik.stages.email.models import ( + EmailStage, + EmailTemplates, + get_template_choices, +) class EmailStageSerializer(StageSerializer): @@ -12,6 +22,13 @@ class EmailStageSerializer(StageSerializer): super().__init__(*args, **kwargs) self.fields["template"].choices = get_template_choices() + def validate_template(self, value: str) -> str: + choices = get_template_choices() + for path, _ in choices: + if path == value: + return value + raise ValidationError(f"Invalid template '{value}' specified.") + class Meta: model = EmailStage @@ -39,3 +56,17 @@ class EmailStageViewSet(ModelViewSet): serializer_class = EmailStageSerializer # TODO: Validate connection settings when use_global_settings is unchecked + @swagger_auto_schema(responses={200: TypeCreateSerializer(many=True)}) + @action(detail=False, pagination_class=None, filter_backends=[]) + def templates(self, request: Request) -> Response: + """Get all available templates, including custom templates""" + choices = [] + for value, label in get_template_choices(): + choices.append( + { + "name": value, + "description": label, + "component": "", + } + ) + return Response(TypeCreateSerializer(choices, many=True).data) diff --git a/authentik/stages/email/migrations/0003_auto_20210404_1054.py b/authentik/stages/email/migrations/0003_auto_20210404_1054.py new file mode 100644 index 000000000..f8a916f46 --- /dev/null +++ b/authentik/stages/email/migrations/0003_auto_20210404_1054.py @@ -0,0 +1,18 @@ +# Generated by Django 3.1.7 on 2021-04-04 10:54 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("authentik_stages_email", "0002_emailstage_use_global_settings"), + ] + + operations = [ + migrations.AlterField( + model_name="emailstage", + name="template", + field=models.TextField(default="email/password_reset.html"), + ), + ] diff --git a/authentik/stages/email/models.py b/authentik/stages/email/models.py index 65c155f4b..c9a89f377 100644 --- a/authentik/stages/email/models.py +++ b/authentik/stages/email/models.py @@ -78,9 +78,7 @@ class EmailStage(Stage): default=30, help_text=_("Time in minutes the token sent is valid.") ) subject = models.TextField(default="authentik") - template = models.TextField( - choices=get_template_choices(), default=EmailTemplates.PASSWORD_RESET - ) + template = models.TextField(default=EmailTemplates.PASSWORD_RESET) @property def serializer(self) -> BaseSerializer: diff --git a/swagger.yaml b/swagger.yaml index c364f0457..f5ef4f0cf 100755 --- a/swagger.yaml +++ b/swagger.yaml @@ -12081,6 +12081,25 @@ paths: tags: - stages parameters: [] + /stages/email/templates/: + get: + operationId: stages_email_templates + description: Get all available templates, including custom templates + parameters: [] + responses: + '200': + description: '' + schema: + type: array + items: + $ref: '#/definitions/TypeCreate' + '403': + description: Authentication credentials were invalid, absent or insufficient. + schema: + $ref: '#/definitions/GenericError' + tags: + - stages + parameters: [] /stages/email/{stage_uuid}/: get: operationId: stages_email_read @@ -17693,9 +17712,7 @@ definitions: template: title: Template type: string - enum: - - email/password_reset.html - - email/account_confirmation.html + minLength: 1 IdentificationStage: required: - name diff --git a/web/src/pages/stages/email/EmailStageForm.ts b/web/src/pages/stages/email/EmailStageForm.ts index 8449dee9e..e360591e8 100644 --- a/web/src/pages/stages/email/EmailStageForm.ts +++ b/web/src/pages/stages/email/EmailStageForm.ts @@ -1,4 +1,4 @@ -import { EmailStage, EmailStageTemplateEnum, StagesApi } from "authentik-api"; +import { EmailStage, StagesApi } from "authentik-api"; import { t } from "@lingui/macro"; import { customElement, property } from "lit-element"; import { html, TemplateResult } from "lit-html"; @@ -8,6 +8,7 @@ import { ifDefined } from "lit-html/directives/if-defined"; import "../../../elements/forms/HorizontalFormElement"; import "../../../elements/forms/FormGroup"; import { first } from "../../../utils"; +import { until } from "lit-html/directives/until"; @customElement("ak-stage-email-form") export class EmailStageForm extends Form { @@ -153,14 +154,16 @@ export class EmailStageForm extends Form { + name="template">