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""" """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 rest_framework.viewsets import ModelViewSet
from authentik.core.api.utils import TypeCreateSerializer
from authentik.flows.api.stages import StageSerializer 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): class EmailStageSerializer(StageSerializer):
@ -12,6 +22,13 @@ class EmailStageSerializer(StageSerializer):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.fields["template"].choices = get_template_choices() 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: class Meta:
model = EmailStage model = EmailStage
@ -39,3 +56,17 @@ class EmailStageViewSet(ModelViewSet):
serializer_class = EmailStageSerializer serializer_class = EmailStageSerializer
# TODO: Validate connection settings when use_global_settings is unchecked # 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.") default=30, help_text=_("Time in minutes the token sent is valid.")
) )
subject = models.TextField(default="authentik") subject = models.TextField(default="authentik")
template = models.TextField( template = models.TextField(default=EmailTemplates.PASSWORD_RESET)
choices=get_template_choices(), default=EmailTemplates.PASSWORD_RESET
)
@property @property
def serializer(self) -> BaseSerializer: def serializer(self) -> BaseSerializer:

View File

@ -12081,6 +12081,25 @@ paths:
tags: tags:
- stages - stages
parameters: [] 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}/: /stages/email/{stage_uuid}/:
get: get:
operationId: stages_email_read operationId: stages_email_read
@ -17693,9 +17712,7 @@ definitions:
template: template:
title: Template title: Template
type: string type: string
enum: minLength: 1
- email/password_reset.html
- email/account_confirmation.html
IdentificationStage: IdentificationStage:
required: required:
- name - 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 { t } from "@lingui/macro";
import { customElement, property } from "lit-element"; import { customElement, property } from "lit-element";
import { html, TemplateResult } from "lit-html"; 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/HorizontalFormElement";
import "../../../elements/forms/FormGroup"; import "../../../elements/forms/FormGroup";
import { first } from "../../../utils"; import { first } from "../../../utils";
import { until } from "lit-html/directives/until";
@customElement("ak-stage-email-form") @customElement("ak-stage-email-form")
export class EmailStageForm extends Form<EmailStage> { export class EmailStageForm extends Form<EmailStage> {
@ -153,14 +154,16 @@ export class EmailStageForm extends Form<EmailStage> {
<ak-form-element-horizontal <ak-form-element-horizontal
label=${t`Template`} label=${t`Template`}
?required=${true} ?required=${true}
name="subject"> name="template">
<select name="users" class="pf-c-form-control"> <select name="users" class="pf-c-form-control">
<option value=${EmailStageTemplateEnum.AccountConfirmationHtml} ?selected=${this.stage?.template === EmailStageTemplateEnum.AccountConfirmationHtml}> ${until(new StagesApi(DEFAULT_CONFIG).stagesEmailTemplates().then(templates => {
${t`Account confirmation`} return templates.map(template => {
</option> const selected = this.stage?.template === template.name;
<option value=${EmailStageTemplateEnum.PasswordResetHtml} ?selected=${this.stage?.template === EmailStageTemplateEnum.PasswordResetHtml}> return html`<option value=${ifDefined(template.name)} ?selected=${selected}>
${t`Password reset`} ${template.description}
</option> </option>`;
});
}), html`<option>${t`Loading...`}</option>`)}
</select> </select>
</ak-form-element-horizontal> </ak-form-element-horizontal>
</div> </div>