authentik.stages.email.utils

email utils

 1"""email utils"""
 2
 3from email.mime.image import MIMEImage
 4from functools import lru_cache
 5from pathlib import Path
 6
 7from django.conf import settings
 8from django.core.mail import EmailMultiAlternatives
 9from django.core.mail.message import sanitize_address
10from django.template.exceptions import TemplateDoesNotExist
11from django.template.loader import render_to_string
12from django.utils import translation
13
14
15@lru_cache
16def logo_data() -> MIMEImage:
17    """Get logo as MIME Image for emails"""
18    path = Path("web/dist/assets/icons/icon_left_brand.png")
19    # When running tests, assets might not exist, so fallback to a different icon
20    if settings.TEST:
21        path = Path("web/authentik/sources/saml.png")
22    with open(path, "rb") as _logo_file:
23        logo = MIMEImage(_logo_file.read())
24    logo.add_header("Content-ID", "<logo>")
25    logo.add_header("Content-Disposition", "inline", filename="logo.png")
26    return logo
27
28
29def _sanitize_recipients(recipients: list[tuple[str, str]]) -> list[str]:
30    """Sanitize a list of (name, email) tuples into valid email addresses."""
31    sanitized = []
32    for recipient_name, recipient_email in recipients:
33        # Remove any newline characters from name and email before sanitizing
34        clean_name = recipient_name.replace("\n", " ").replace("\r", " ") if recipient_name else ""
35        clean_email = recipient_email.replace("\n", "").replace("\r", "") if recipient_email else ""
36        sanitized.append(sanitize_address((clean_name, clean_email), "utf-8"))
37    return sanitized
38
39
40class TemplateEmailMessage(EmailMultiAlternatives):
41    """Wrapper around EmailMultiAlternatives with integrated template rendering"""
42
43    def __init__(
44        self,
45        to: list[tuple[str, str]],
46        cc: list[tuple[str, str]] | None = None,
47        bcc: list[tuple[str, str]] | None = None,
48        template_name=None,
49        template_context=None,
50        language="",
51        **kwargs,
52    ):
53        sanitized_to = _sanitize_recipients(to)
54        sanitized_cc = _sanitize_recipients(cc) if cc else None
55        sanitized_bcc = _sanitize_recipients(bcc) if bcc else None
56        super().__init__(to=sanitized_to, cc=sanitized_cc, bcc=sanitized_bcc, **kwargs)
57        if not template_name:
58            return
59        with translation.override(language):
60            html_content = render_to_string(template_name, template_context)
61            try:
62                text_content = render_to_string(
63                    template_name.replace("html", "txt"), template_context
64                )
65                self.body = text_content
66            except TemplateDoesNotExist:
67                pass
68        self.mixed_subtype = "related"
69        self.attach_alternative(html_content, "text/html")
@lru_cache
def logo_data() -> email.mime.image.MIMEImage:
16@lru_cache
17def logo_data() -> MIMEImage:
18    """Get logo as MIME Image for emails"""
19    path = Path("web/dist/assets/icons/icon_left_brand.png")
20    # When running tests, assets might not exist, so fallback to a different icon
21    if settings.TEST:
22        path = Path("web/authentik/sources/saml.png")
23    with open(path, "rb") as _logo_file:
24        logo = MIMEImage(_logo_file.read())
25    logo.add_header("Content-ID", "<logo>")
26    logo.add_header("Content-Disposition", "inline", filename="logo.png")
27    return logo

Get logo as MIME Image for emails

class TemplateEmailMessage(django.core.mail.message.EmailMultiAlternatives):
41class TemplateEmailMessage(EmailMultiAlternatives):
42    """Wrapper around EmailMultiAlternatives with integrated template rendering"""
43
44    def __init__(
45        self,
46        to: list[tuple[str, str]],
47        cc: list[tuple[str, str]] | None = None,
48        bcc: list[tuple[str, str]] | None = None,
49        template_name=None,
50        template_context=None,
51        language="",
52        **kwargs,
53    ):
54        sanitized_to = _sanitize_recipients(to)
55        sanitized_cc = _sanitize_recipients(cc) if cc else None
56        sanitized_bcc = _sanitize_recipients(bcc) if bcc else None
57        super().__init__(to=sanitized_to, cc=sanitized_cc, bcc=sanitized_bcc, **kwargs)
58        if not template_name:
59            return
60        with translation.override(language):
61            html_content = render_to_string(template_name, template_context)
62            try:
63                text_content = render_to_string(
64                    template_name.replace("html", "txt"), template_context
65                )
66                self.body = text_content
67            except TemplateDoesNotExist:
68                pass
69        self.mixed_subtype = "related"
70        self.attach_alternative(html_content, "text/html")

Wrapper around EmailMultiAlternatives with integrated template rendering

TemplateEmailMessage( to: list[tuple[str, str]], cc: list[tuple[str, str]] | None = None, bcc: list[tuple[str, str]] | None = None, template_name=None, template_context=None, language='', **kwargs)
44    def __init__(
45        self,
46        to: list[tuple[str, str]],
47        cc: list[tuple[str, str]] | None = None,
48        bcc: list[tuple[str, str]] | None = None,
49        template_name=None,
50        template_context=None,
51        language="",
52        **kwargs,
53    ):
54        sanitized_to = _sanitize_recipients(to)
55        sanitized_cc = _sanitize_recipients(cc) if cc else None
56        sanitized_bcc = _sanitize_recipients(bcc) if bcc else None
57        super().__init__(to=sanitized_to, cc=sanitized_cc, bcc=sanitized_bcc, **kwargs)
58        if not template_name:
59            return
60        with translation.override(language):
61            html_content = render_to_string(template_name, template_context)
62            try:
63                text_content = render_to_string(
64                    template_name.replace("html", "txt"), template_context
65                )
66                self.body = text_content
67            except TemplateDoesNotExist:
68                pass
69        self.mixed_subtype = "related"
70        self.attach_alternative(html_content, "text/html")

Initialize a single email message (which can be sent to multiple recipients).

mixed_subtype = 'mixed'