stages/email: add support for custom template to API

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
Jens Langhammer 2021-04-04 13:06:17 +02:00
parent d061868fdc
commit d1cde64214
5 changed files with 82 additions and 15 deletions

View file

@ -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)

View file

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

View file

@ -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:

View file

@ -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

View file

@ -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<EmailStage> {
@ -153,14 +154,16 @@ export class EmailStageForm extends Form<EmailStage> {
<ak-form-element-horizontal
label=${t`Template`}
?required=${true}
name="subject">
name="template">
<select name="users" class="pf-c-form-control">
<option value=${EmailStageTemplateEnum.AccountConfirmationHtml} ?selected=${this.stage?.template === EmailStageTemplateEnum.AccountConfirmationHtml}>
${t`Account confirmation`}
</option>
<option value=${EmailStageTemplateEnum.PasswordResetHtml} ?selected=${this.stage?.template === EmailStageTemplateEnum.PasswordResetHtml}>
${t`Password reset`}
</option>
${until(new StagesApi(DEFAULT_CONFIG).stagesEmailTemplates().then(templates => {
return templates.map(template => {
const selected = this.stage?.template === template.name;
return html`<option value=${ifDefined(template.name)} ?selected=${selected}>
${template.description}
</option>`;
});
}), html`<option>${t`Loading...`}</option>`)}
</select>
</ak-form-element-horizontal>
</div>