From ec8f2d4bf98f35cc8dc03931c85878228df0f992 Mon Sep 17 00:00:00 2001 From: Jens L Date: Thu, 21 Dec 2023 14:32:05 +0100 Subject: [PATCH] stages/email: prevent authentik emails from being marked as spam (also add text template support) (#7949) * use <> style email address with name Signed-off-by: Jens Langhammer * add support for text templates Signed-off-by: Jens Langhammer * fix icon display in event log Signed-off-by: Jens Langhammer * add text email templates Signed-off-by: Jens Langhammer * update docs, update email screenshot Signed-off-by: Jens Langhammer * prevent prettier from breaking example template Signed-off-by: Jens Langhammer * Optimised images with calibre/image-actions * Apply suggestions from code review Co-authored-by: Marc 'risson' Schmitt Signed-off-by: Jens L. * reword docs Signed-off-by: Jens Langhammer --------- Signed-off-by: Jens Langhammer Signed-off-by: Jens L. Co-authored-by: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com> Co-authored-by: Marc 'risson' Schmitt --- authentik/events/models.py | 2 +- authentik/stages/email/stage.py | 2 +- .../templates/email/account_confirmation.txt | 8 ++++ .../templates/email/event_notification.html | 2 +- .../templates/email/event_notification.txt | 18 +++++++++ .../email/templates/email/password_reset.txt | 12 ++++++ .../stages/email/templates/email/setup.txt | 7 ++++ .../templatetags/authentik_stages_email.py | 6 +++ authentik/stages/email/tests/test_sending.py | 6 ++- authentik/stages/email/tests/test_stage.py | 4 +- authentik/stages/email/utils.py | 11 +++++- web/src/components/ak-event-info.ts | 4 +- .../docs/flow/stages/email/email_recovery.png | Bin 23911 -> 25528 bytes website/docs/flow/stages/email/index.mdx | 37 +++++++++++------- 14 files changed, 95 insertions(+), 24 deletions(-) create mode 100644 authentik/stages/email/templates/email/account_confirmation.txt create mode 100644 authentik/stages/email/templates/email/event_notification.txt create mode 100644 authentik/stages/email/templates/email/password_reset.txt create mode 100644 authentik/stages/email/templates/email/setup.txt diff --git a/authentik/events/models.py b/authentik/events/models.py index 965e38cc2..240ba11de 100644 --- a/authentik/events/models.py +++ b/authentik/events/models.py @@ -461,7 +461,7 @@ class NotificationTransport(SerializerModel): } mail = TemplateEmailMessage( subject=subject_prefix + context["title"], - to=[notification.user.email], + to=[f"{notification.user.name} <{notification.user.email}>"], language=notification.user.locale(), template_name="email/event_notification.html", template_context=context, diff --git a/authentik/stages/email/stage.py b/authentik/stages/email/stage.py index 0fa36bfbe..160a68e92 100644 --- a/authentik/stages/email/stage.py +++ b/authentik/stages/email/stage.py @@ -110,7 +110,7 @@ class EmailStageView(ChallengeStageView): try: message = TemplateEmailMessage( subject=_(current_stage.subject), - to=[email], + to=[f"{pending_user.name} <{email}>"], language=pending_user.locale(self.request), template_name=current_stage.template, template_context={ diff --git a/authentik/stages/email/templates/email/account_confirmation.txt b/authentik/stages/email/templates/email/account_confirmation.txt new file mode 100644 index 000000000..0a1fc70d1 --- /dev/null +++ b/authentik/stages/email/templates/email/account_confirmation.txt @@ -0,0 +1,8 @@ +{% load i18n %}{% translate "Welcome!" %} + +{% translate "We're excited to have you get started. First, you need to confirm your account. Just open the link below." %} + +{{ url }} + +-- +Powered by goauthentik.io. diff --git a/authentik/stages/email/templates/email/event_notification.html b/authentik/stages/email/templates/email/event_notification.html index e34563404..7ca78fc64 100644 --- a/authentik/stages/email/templates/email/event_notification.html +++ b/authentik/stages/email/templates/email/event_notification.html @@ -44,7 +44,7 @@ {% blocktranslate with name=source.from %} - This email was sent from the notification transport {{name}}. + This email was sent from the notification transport {{ name }}. {% endblocktranslate %} diff --git a/authentik/stages/email/templates/email/event_notification.txt b/authentik/stages/email/templates/email/event_notification.txt new file mode 100644 index 000000000..bd7d92896 --- /dev/null +++ b/authentik/stages/email/templates/email/event_notification.txt @@ -0,0 +1,18 @@ +{% load authentik_stages_email %}{% load i18n %}{% translate "Dear authentik user," %} + +{% translate "The following notification was created:" %} + + {{ body|indent }} + +{% if key_value %} +{% translate "Additional attributes:" %} +{% for key, value in key_value.items %} + {{ key }}: {{ value|indent }}{% endfor %} +{% endif %} + +{% if source %}{% blocktranslate with name=source.from %} +This email was sent from the notification transport {{ name }}. +{% endblocktranslate %}{% endif %} + +-- +Powered by goauthentik.io. diff --git a/authentik/stages/email/templates/email/password_reset.txt b/authentik/stages/email/templates/email/password_reset.txt new file mode 100644 index 000000000..0c13ad2f8 --- /dev/null +++ b/authentik/stages/email/templates/email/password_reset.txt @@ -0,0 +1,12 @@ +{% load i18n %}{% load humanize %}{% blocktrans with username=user.username %}Hi {{ username }},{% endblocktrans %} + +{% blocktrans %} +You recently requested to change your password for your authentik account. Use the link below to set a new password. +{% endblocktrans %} +{{ url }} +{% blocktrans with expires=expires|naturaltime %} +If you did not request a password change, please ignore this Email. The link above is valid for {{ expires }}. +{% endblocktrans %} + +-- +Powered by goauthentik.io. diff --git a/authentik/stages/email/templates/email/setup.txt b/authentik/stages/email/templates/email/setup.txt new file mode 100644 index 000000000..6d0eb0ce0 --- /dev/null +++ b/authentik/stages/email/templates/email/setup.txt @@ -0,0 +1,7 @@ +{% load i18n %}authentik Test-Email +{% blocktrans %} +This is a test email to inform you, that you've successfully configured authentik emails. +{% endblocktrans %} + +-- +Powered by goauthentik.io. diff --git a/authentik/stages/email/templatetags/authentik_stages_email.py b/authentik/stages/email/templatetags/authentik_stages_email.py index 7623c6c71..9b3dfb194 100644 --- a/authentik/stages/email/templatetags/authentik_stages_email.py +++ b/authentik/stages/email/templatetags/authentik_stages_email.py @@ -29,3 +29,9 @@ def inline_static_binary(path: str) -> str: b64content = b64encode(_file.read().encode()) return f"data:image/{result.suffix};base64,{b64content.decode('utf-8')}" return path + + +@register.filter(name="indent") +def indent_string(val, num_spaces=4): + """Intent text by a given amount of spaces""" + return val.replace("\n", "\n" + " " * num_spaces) diff --git a/authentik/stages/email/tests/test_sending.py b/authentik/stages/email/tests/test_sending.py index 424d474ce..5c67c8842 100644 --- a/authentik/stages/email/tests/test_sending.py +++ b/authentik/stages/email/tests/test_sending.py @@ -58,9 +58,11 @@ class TestEmailStageSending(FlowTestCase): events = Event.objects.filter(action=EventAction.EMAIL_SENT) self.assertEqual(len(events), 1) event = events.first() - self.assertEqual(event.context["message"], f"Email to {self.user.email} sent") + self.assertEqual( + event.context["message"], f"Email to {self.user.name} <{self.user.email}> sent" + ) self.assertEqual(event.context["subject"], "authentik") - self.assertEqual(event.context["to_email"], [self.user.email]) + self.assertEqual(event.context["to_email"], [f"{self.user.name} <{self.user.email}>"]) self.assertEqual(event.context["from_email"], "system@authentik.local") def test_pending_fake_user(self): diff --git a/authentik/stages/email/tests/test_stage.py b/authentik/stages/email/tests/test_stage.py index 853bc2a77..277549937 100644 --- a/authentik/stages/email/tests/test_stage.py +++ b/authentik/stages/email/tests/test_stage.py @@ -94,7 +94,7 @@ class TestEmailStage(FlowTestCase): self.assertEqual(response.status_code, 200) self.assertEqual(len(mail.outbox), 1) self.assertEqual(mail.outbox[0].subject, "authentik") - self.assertEqual(mail.outbox[0].to, [self.user.email]) + self.assertEqual(mail.outbox[0].to, [f"{self.user.name} <{self.user.email}>"]) @patch( "authentik.stages.email.models.EmailStage.backend_class", @@ -114,7 +114,7 @@ class TestEmailStage(FlowTestCase): self.assertEqual(response.status_code, 200) self.assertEqual(len(mail.outbox), 1) self.assertEqual(mail.outbox[0].subject, "authentik") - self.assertEqual(mail.outbox[0].to, ["foo@bar.baz"]) + self.assertEqual(mail.outbox[0].to, [f"{self.user.name} "]) @patch( "authentik.stages.email.models.EmailStage.backend_class", diff --git a/authentik/stages/email/utils.py b/authentik/stages/email/utils.py index a6edd4609..8f7f702ce 100644 --- a/authentik/stages/email/utils.py +++ b/authentik/stages/email/utils.py @@ -4,6 +4,7 @@ from functools import lru_cache from pathlib import Path from django.core.mail import EmailMultiAlternatives +from django.template.exceptions import TemplateDoesNotExist from django.template.loader import render_to_string from django.utils import translation @@ -24,9 +25,15 @@ class TemplateEmailMessage(EmailMultiAlternatives): """Wrapper around EmailMultiAlternatives with integrated template rendering""" def __init__(self, template_name=None, template_context=None, language="", **kwargs): + super().__init__(**kwargs) with translation.override(language): html_content = render_to_string(template_name, template_context) - super().__init__(**kwargs) - self.content_subtype = "html" + try: + text_content = render_to_string( + template_name.replace("html", "txt"), template_context + ) + self.body = text_content + except TemplateDoesNotExist: + pass self.mixed_subtype = "related" self.attach_alternative(html_content, "text/html") diff --git a/web/src/components/ak-event-info.ts b/web/src/components/ak-event-info.ts index 6901f31a8..e728958c2 100644 --- a/web/src/components/ak-event-info.ts +++ b/web/src/components/ak-event-info.ts @@ -285,10 +285,12 @@ export class EventInfo extends AKElement { } renderEmailSent() { + let body = this.event.context.body as string; + body = body.replace("cid:logo.png", "/static/dist/assets/icons/icon_left_brand.png"); return html`
${msg("Email info:")}
${this.getEmailInfo(this.event.context)}
- + `; } diff --git a/website/docs/flow/stages/email/email_recovery.png b/website/docs/flow/stages/email/email_recovery.png index 1dc5dbbc4684cf4dc6448808c5c74f67ca82bb12..0bc14bafd1b7718e9f268158b081854fbc69cd0e 100644 GIT binary patch literal 25528 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 c$HsQMsMC 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 diff --git a/website/docs/flow/stages/email/index.mdx b/website/docs/flow/stages/email/index.mdx index 1064d8f13..a7751e295 100644 --- a/website/docs/flow/stages/email/index.mdx +++ b/website/docs/flow/stages/email/index.mdx @@ -25,6 +25,10 @@ return True You can also use custom email templates, to use your own design or layout. +:::info +Starting with authentik 2024.1, it is possible to create `.txt` files with the same name as the `.html` template. If a matching `.txt` file exists, the email sent will be a multipart email with both the text and HTML template. +::: + import Tabs from "@theme/Tabs"; import TabItem from "@theme/TabItem"; @@ -81,13 +85,17 @@ Templates are rendered using Django's templating engine. The following variables - `user`: The pending user object. - `expires`: The timestamp when the token expires. + + ```html -{# This is how you can write comments which aren't rendered. #} {# Extend this -template from the base email template, which includes base layout and CSS. #} {% -extends "email/base.html" %} {# Load the internationalization module to -translate strings, and humanize to show date-time #} {% load i18n %} {% load -humanize %} {# The email/base.html template uses a single "content" block #} {% -block content %} +{# This is how you can write comments which aren't rendered. #} +{# Extend this template from the base email template, which includes base layout and CSS. #} +{% extends "email/base.html" %} +{# Load the internationalization module to translate strings, and humanize to show date-time #} +{% load i18n %} +{% load humanize %} +{# The email/base.html template uses a single "content" block #} +{% block content %} {% blocktrans with username=user.username %} Hi {{ username }}, {% @@ -99,9 +107,9 @@ block content %} @@ -130,8 +138,7 @@ block content %} href="{{ url }}" rel="noopener noreferrer" target="_blank" - >{% trans 'Reset - Password' %}{% trans 'Reset Password' %} @@ -145,9 +152,9 @@ block content %}
- {% blocktrans %} You recently requested to change your - password for you authentik account. Use the button below to - set a new password. {% endblocktrans %} + {% blocktrans %} + You recently requested to change your password for you authentik account. Use the button below to set a new password. + {% endblocktrans %}
- {% blocktrans with expires=expires|naturaltime %} If you did - not request a password change, please ignore this Email. The - link above is valid for {{ expires }}. {% endblocktrans %} + {% blocktrans with expires=expires|naturaltime %} + If you did not request a password change, please ignore this Email. The link above is valid for {{ expires }}. + {% endblocktrans %}
@@ -155,3 +162,5 @@ block content %} {% endblock %} ``` + +