1
0
mirror of https://github.com/django/django.git synced 2025-06-05 11:39:13 +00:00

Fixed 35653 -- Added ssl_cafile to smtp email backend.

This commit is contained in:
Igor Scheller 2024-08-07 13:49:15 +02:00
parent 58e548db8b
commit 495420eec3
6 changed files with 45 additions and 5 deletions

View File

@ -206,6 +206,7 @@ EMAIL_HOST_USER = ""
EMAIL_HOST_PASSWORD = "" EMAIL_HOST_PASSWORD = ""
EMAIL_USE_TLS = False EMAIL_USE_TLS = False
EMAIL_USE_SSL = False EMAIL_USE_SSL = False
EMAIL_SSL_CAFILE = None
EMAIL_SSL_CERTFILE = None EMAIL_SSL_CERTFILE = None
EMAIL_SSL_KEYFILE = None EMAIL_SSL_KEYFILE = None
EMAIL_TIMEOUT = None EMAIL_TIMEOUT = None

View File

@ -28,6 +28,7 @@ class EmailBackend(BaseEmailBackend):
timeout=None, timeout=None,
ssl_keyfile=None, ssl_keyfile=None,
ssl_certfile=None, ssl_certfile=None,
ssl_cafile=None,
**kwargs, **kwargs,
): ):
super().__init__(fail_silently=fail_silently) super().__init__(fail_silently=fail_silently)
@ -44,6 +45,9 @@ class EmailBackend(BaseEmailBackend):
self.ssl_certfile = ( self.ssl_certfile = (
settings.EMAIL_SSL_CERTFILE if ssl_certfile is None else ssl_certfile settings.EMAIL_SSL_CERTFILE if ssl_certfile is None else ssl_certfile
) )
self.ssl_cafile = (
settings.EMAIL_SSL_CAFILE if ssl_cafile is None else ssl_cafile
)
if self.use_ssl and self.use_tls: if self.use_ssl and self.use_tls:
raise ValueError( raise ValueError(
"EMAIL_USE_TLS/EMAIL_USE_SSL are mutually exclusive, so only set " "EMAIL_USE_TLS/EMAIL_USE_SSL are mutually exclusive, so only set "
@ -61,9 +65,13 @@ class EmailBackend(BaseEmailBackend):
if self.ssl_certfile or self.ssl_keyfile: if self.ssl_certfile or self.ssl_keyfile:
ssl_context = ssl.SSLContext(protocol=ssl.PROTOCOL_TLS_CLIENT) ssl_context = ssl.SSLContext(protocol=ssl.PROTOCOL_TLS_CLIENT)
ssl_context.load_cert_chain(self.ssl_certfile, self.ssl_keyfile) ssl_context.load_cert_chain(self.ssl_certfile, self.ssl_keyfile)
return ssl_context
else: else:
return ssl.create_default_context() ssl_context = ssl.create_default_context()
if self.ssl_cafile:
ssl_context.load_verify_locations(cafile=self.ssl_cafile)
return ssl_context
def open(self): def open(self):
""" """

View File

@ -1495,6 +1495,17 @@ see the explicit TLS setting :setting:`EMAIL_USE_TLS`.
Note that :setting:`EMAIL_USE_TLS`/:setting:`EMAIL_USE_SSL` are mutually Note that :setting:`EMAIL_USE_TLS`/:setting:`EMAIL_USE_SSL` are mutually
exclusive, so only set one of those settings to ``True``. exclusive, so only set one of those settings to ``True``.
.. setting:: EMAIL_SSL_CAFILE
``EMAIL_SSL_CAFILE``
----------------------
Default: ``None``
If :setting:`EMAIL_USE_SSL` or :setting:`EMAIL_USE_TLS` is ``True``, you can
optionally specify the path to a PEM-formatted certificate authority
root certificate to validate the SSL connection.
.. setting:: EMAIL_SSL_CERTFILE .. setting:: EMAIL_SSL_CERTFILE
``EMAIL_SSL_CERTFILE`` ``EMAIL_SSL_CERTFILE``
@ -1517,8 +1528,9 @@ If :setting:`EMAIL_USE_SSL` or :setting:`EMAIL_USE_TLS` is ``True``, you can
optionally specify the path to a PEM-formatted private key file to use for the optionally specify the path to a PEM-formatted private key file to use for the
SSL connection. SSL connection.
Note that setting :setting:`EMAIL_SSL_CERTFILE` and :setting:`EMAIL_SSL_KEYFILE` Note that setting :setting:`EMAIL_SSL_CERTFILE`, :setting:`EMAIL_SSL_KEYFILE`
doesn't result in any certificate checking. They're passed to the underlying SSL or :setting:`EMAIL_SSL_CAFILE` doesn't result in any certificate checking.
They're passed to the underlying SSL
connection. Please refer to the documentation of Python's connection. Please refer to the documentation of Python's
:meth:`python:ssl.SSLContext.wrap_socket` function for details on how the :meth:`python:ssl.SSLContext.wrap_socket` function for details on how the
certificate chain file and private key file are handled. certificate chain file and private key file are handled.
@ -3631,6 +3643,7 @@ Email
* :setting:`EMAIL_HOST_PASSWORD` * :setting:`EMAIL_HOST_PASSWORD`
* :setting:`EMAIL_HOST_USER` * :setting:`EMAIL_HOST_USER`
* :setting:`EMAIL_PORT` * :setting:`EMAIL_PORT`
* :setting:`EMAIL_SSL_CAFILE`
* :setting:`EMAIL_SSL_CERTFILE` * :setting:`EMAIL_SSL_CERTFILE`
* :setting:`EMAIL_SSL_KEYFILE` * :setting:`EMAIL_SSL_KEYFILE`
* :setting:`EMAIL_SUBJECT_PREFIX` * :setting:`EMAIL_SUBJECT_PREFIX`

View File

@ -220,6 +220,9 @@ Email
returns a boolean indicating whether a provided text is contained in the returns a boolean indicating whether a provided text is contained in the
email ``body`` and in all attached MIME type ``text/*`` alternatives. email ``body`` and in all attached MIME type ``text/*`` alternatives.
* The SMTP email backend now supports certificate validation using a ``cafile``
with the :setting:`EMAIL_SSL_CAFILE` setting.
Error Reporting Error Reporting
~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~

View File

@ -605,7 +605,7 @@ can :ref:`write your own email backend <topic-custom-email-backend>`.
SMTP backend SMTP backend
~~~~~~~~~~~~ ~~~~~~~~~~~~
.. class:: backends.smtp.EmailBackend(host=None, port=None, username=None, password=None, use_tls=None, fail_silently=False, use_ssl=None, timeout=None, ssl_keyfile=None, ssl_certfile=None, **kwargs) .. class:: backends.smtp.EmailBackend(host=None, port=None, username=None, password=None, use_tls=None, fail_silently=False, use_ssl=None, timeout=None, ssl_keyfile=None, ssl_certfile=None, ssl_cafile=None, **kwargs)
This is the default backend. Email will be sent through a SMTP server. This is the default backend. Email will be sent through a SMTP server.
@ -621,6 +621,7 @@ SMTP backend
* ``timeout``: :setting:`EMAIL_TIMEOUT` * ``timeout``: :setting:`EMAIL_TIMEOUT`
* ``ssl_keyfile``: :setting:`EMAIL_SSL_KEYFILE` * ``ssl_keyfile``: :setting:`EMAIL_SSL_KEYFILE`
* ``ssl_certfile``: :setting:`EMAIL_SSL_CERTFILE` * ``ssl_certfile``: :setting:`EMAIL_SSL_CERTFILE`
* ``ssl_cafile``: :setting:`EMAIL_SSL_CAFILE`
The SMTP backend is the default configuration inherited by Django. If you The SMTP backend is the default configuration inherited by Django. If you
want to specify it explicitly, put the following in your settings:: want to specify it explicitly, put the following in your settings::

View File

@ -2269,16 +2269,30 @@ class SMTPBackendTests(BaseEmailBackendTests, SMTPBackendTestsBase):
backend = smtp.EmailBackend() backend = smtp.EmailBackend()
self.assertFalse(backend.use_ssl) self.assertFalse(backend.use_ssl)
@override_settings(EMAIL_SSL_CAFILE="foo")
def test_email_ssl_cafile_use_settings(self):
backend = smtp.EmailBackend()
self.assertEqual(backend.ssl_cafile, "foo")
@override_settings(EMAIL_SSL_CERTFILE="foo") @override_settings(EMAIL_SSL_CERTFILE="foo")
def test_email_ssl_certfile_use_settings(self): def test_email_ssl_certfile_use_settings(self):
backend = smtp.EmailBackend() backend = smtp.EmailBackend()
self.assertEqual(backend.ssl_certfile, "foo") self.assertEqual(backend.ssl_certfile, "foo")
@override_settings(EMAIL_SSL_CAFILE="foo")
def test_email_ssl_cafile_override_settings(self):
backend = smtp.EmailBackend(ssl_cafile="bar")
self.assertEqual(backend.ssl_cafile, "bar")
@override_settings(EMAIL_SSL_CERTFILE="foo") @override_settings(EMAIL_SSL_CERTFILE="foo")
def test_email_ssl_certfile_override_settings(self): def test_email_ssl_certfile_override_settings(self):
backend = smtp.EmailBackend(ssl_certfile="bar") backend = smtp.EmailBackend(ssl_certfile="bar")
self.assertEqual(backend.ssl_certfile, "bar") self.assertEqual(backend.ssl_certfile, "bar")
def test_email_ssl_cafile_default_disabled(self):
backend = smtp.EmailBackend()
self.assertIsNone(backend.ssl_cafile)
def test_email_ssl_certfile_default_disabled(self): def test_email_ssl_certfile_default_disabled(self):
backend = smtp.EmailBackend() backend = smtp.EmailBackend()
self.assertIsNone(backend.ssl_certfile) self.assertIsNone(backend.ssl_certfile)