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

Get logo as MIME Image for emails

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

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

mixed_subtype = 'mixed'