From c50cf74c4e139224dc15d121f8a016167063686e Mon Sep 17 00:00:00 2001 From: David Miller Date: Mon, 2 Jul 2018 11:43:27 +0100 Subject: [PATCH] Add support for authentication using client TLS certificates --- docs/index.rst | 14 ++++++++++++++ flask_mail.py | 19 +++++++++++++++---- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index 9621272..f45149b 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -46,6 +46,10 @@ options (each is explained later in the documentation): * **MAIL_USE_SSL** : default **False** +* **MAIL_CLIENT_CERT** : default **None** + +* **MAIL_CLIENT_KEY** : default **None** + * **MAIL_DEBUG** : default **app.debug** * **MAIL_USERNAME** : default **None** @@ -173,6 +177,16 @@ should be fine for any unicode character that can be decomposed by NFKD into one or more ASCII characters. If you need romanization/transliteration (i.e `ß` → `ss`) then your application should do it and pass a proper ASCII string. +Client certificate authentication +--------------------------------- + +In some situations it may be desirable for the mail sender to present a client TLS +certificate to the mail relay, for an extra layer of authentication beyond that provided +by a username and password. In this case, you should set the configuration variables +``MAIL_CLIENT_CERT`` and ``MAIL_CLIENT_KEY`` to the paths to your certificate and private +key, respectively, which will then be loaded when establishing a TLS connection (i.e. when +the ``MAIL_USE_TLS`` or ``MAIL_USE_SSL`` variables are set to **True**) with the mail +relay. Unit tests and suppressing emails --------------------------------- diff --git a/flask_mail.py b/flask_mail.py index d0756e5..fb00a79 100644 --- a/flask_mail.py +++ b/flask_mail.py @@ -152,15 +152,22 @@ def __exit__(self, exc_type, exc_value, tb): self.host.quit() def configure_host(self): + client_cert = None + client_key = None + + if self.mail.client_cert and self.mail.client_key: + client_cert = self.mail.client_cert + client_key = self.mail.client_key + if self.mail.use_ssl: - host = smtplib.SMTP_SSL(self.mail.server, self.mail.port) + host = smtplib.SMTP_SSL(self.mail.server, self.mail.port, certfile=client_cert, keyfile=client_key) else: host = smtplib.SMTP(self.mail.server, self.mail.port) host.set_debuglevel(int(self.mail.debug)) if self.mail.use_tls: - host.starttls() + host.starttls(certfile=client_cert, keyfile=client_key) if self.mail.username and self.mail.password: host.login(self.mail.username, self.mail.password) @@ -527,14 +534,16 @@ def connect(self): class _Mail(_MailMixin): def __init__(self, server, username, password, port, use_tls, use_ssl, - default_sender, debug, max_emails, suppress, - ascii_attachments=False): + client_cert, client_key, default_sender, debug, max_emails, + suppress, ascii_attachments=False): self.server = server self.username = username self.password = password self.port = port self.use_tls = use_tls self.use_ssl = use_ssl + self.client_cert = client_cert + self.client_key = client_key self.default_sender = default_sender self.debug = debug self.max_emails = max_emails @@ -563,6 +572,8 @@ def init_mail(self, config, debug=False, testing=False): config.get('MAIL_PORT', 25), config.get('MAIL_USE_TLS', False), config.get('MAIL_USE_SSL', False), + config.get('MAIL_CLIENT_CERT'), + config.get('MAIL_CLIENT_KEY'), config.get('MAIL_DEFAULT_SENDER'), int(config.get('MAIL_DEBUG', debug)), config.get('MAIL_MAX_EMAILS'),