فهرست منبع

Fixed #20743 -- Added support for keyfile/certfile in SMTP connections.

Thanks jwmayfield, serg.partizan, and Wojciech Banaś for work on the patch.
Andi Albrecht 11 سال پیش
والد
کامیت
00535e8e6b
7فایلهای تغییر یافته به همراه87 افزوده شده و 4 حذف شده
  1. 2 0
      django/conf/global_settings.py
  2. 9 1
      django/core/mail/backends/smtp.py
  3. 34 0
      docs/ref/settings.txt
  4. 4 0
      docs/releases/1.8.txt
  5. 1 0
      docs/spelling_wordlist
  6. 9 3
      docs/topics/email.txt
  7. 28 0
      tests/mail/tests.py

+ 2 - 0
django/conf/global_settings.py

@@ -194,6 +194,8 @@ EMAIL_HOST_USER = ''
 EMAIL_HOST_PASSWORD = ''
 EMAIL_USE_TLS = False
 EMAIL_USE_SSL = False
+EMAIL_SSL_CERTFILE = None
+EMAIL_SSL_KEYFILE = None
 
 # List of strings representing installed apps.
 INSTALLED_APPS = ()

+ 9 - 1
django/core/mail/backends/smtp.py

@@ -15,6 +15,7 @@ class EmailBackend(BaseEmailBackend):
     """
     def __init__(self, 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):
         super(EmailBackend, self).__init__(fail_silently=fail_silently)
         self.host = host or settings.EMAIL_HOST
@@ -24,6 +25,8 @@ class EmailBackend(BaseEmailBackend):
         self.use_tls = settings.EMAIL_USE_TLS if use_tls is None else use_tls
         self.use_ssl = settings.EMAIL_USE_SSL if use_ssl is None else use_ssl
         self.timeout = timeout
+        self.ssl_keyfile = settings.EMAIL_SSL_KEYFILE if ssl_keyfile is None else ssl_keyfile
+        self.ssl_certfile = settings.EMAIL_SSL_CERTFILE if ssl_certfile is None else ssl_certfile
         if self.use_ssl and self.use_tls:
             raise ValueError(
                 "EMAIL_USE_TLS/EMAIL_USE_SSL are mutually exclusive, so only set "
@@ -46,6 +49,11 @@ class EmailBackend(BaseEmailBackend):
         connection_params = {'local_hostname': DNS_NAME.get_fqdn()}
         if self.timeout is not None:
             connection_params['timeout'] = self.timeout
+        if self.use_ssl:
+            connection_params.update({
+                'keyfile': self.ssl_keyfile,
+                'certfile': self.ssl_certfile,
+            })
         try:
             self.connection = connection_class(self.host, self.port, **connection_params)
 
@@ -53,7 +61,7 @@ class EmailBackend(BaseEmailBackend):
             # non-secure connections.
             if not self.use_ssl and self.use_tls:
                 self.connection.ehlo()
-                self.connection.starttls()
+                self.connection.starttls(keyfile=self.ssl_keyfile, certfile=self.ssl_certfile)
                 self.connection.ehlo()
             if self.username and self.password:
                 self.connection.login(self.username, self.password)

+ 34 - 0
docs/ref/settings.txt

@@ -1228,6 +1228,38 @@ see the explicit TLS setting :setting:`EMAIL_USE_TLS`.
 Note that :setting:`EMAIL_USE_TLS`/:setting:`EMAIL_USE_SSL` are mutually
 exclusive, so only set one of those settings to ``True``.
 
+.. setting:: EMAIL_SSL_CERTFILE
+
+EMAIL_SSL_CERTFILE
+------------------
+
+.. versionadded:: 1.8
+
+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 chain file to use
+for the SSL connection.
+
+.. setting:: EMAIL_SSL_KEYFILE
+
+EMAIL_SSL_KEYFILE
+-----------------
+
+.. versionadded:: 1.8
+
+Default: ``None``
+
+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
+SSL connection.
+
+Note that setting :setting:`EMAIL_SSL_CERTFILE` and :setting:`EMAIL_SSL_KEYFILE`
+doesn't result in any certificate checking. They're passed to the underlying SSL
+connection. Please refer to the documentation of Python's
+:func:`python:ssl.wrap_socket` function for details on how the certificate chain
+file and private key file are handled.
+
 .. setting:: FILE_CHARSET
 
 FILE_CHARSET
@@ -2926,6 +2958,8 @@ Email
 * :setting:`EMAIL_HOST_PASSWORD`
 * :setting:`EMAIL_HOST_USER`
 * :setting:`EMAIL_PORT`
+* :setting:`EMAIL_SSL_CERTFILE`
+* :setting:`EMAIL_SSL_KEYFILE`
 * :setting:`EMAIL_SUBJECT_PREFIX`
 * :setting:`EMAIL_USE_TLS`
 * :setting:`MANAGERS`

+ 4 - 0
docs/releases/1.8.txt

@@ -140,6 +140,10 @@ Email
 * :ref:`Email backends <topic-email-backends>` now support the context manager
   protocol for opening and closing connections.
 
+* The SMTP email backend now supports ``keyfile`` and ``certfile``
+  authentication with the :setting:`EMAIL_SSL_CERTFILE` and
+  :setting:`EMAIL_SSL_KEYFILE` settings.
+
 File Storage
 ^^^^^^^^^^^^
 

+ 1 - 0
docs/spelling_wordlist

@@ -426,6 +426,7 @@ Palau
 params
 parens
 pdf
+PEM
 perl
 permalink
 pessimization

+ 9 - 3
docs/topics/email.txt

@@ -445,13 +445,14 @@ can :ref:`write your own email backend <topic-custom-email-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, **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, **kwargs])
 
     This is the default backend. Email will be sent through a SMTP server.
     The server address and authentication credentials are set in the
     :setting:`EMAIL_HOST`, :setting:`EMAIL_PORT`, :setting:`EMAIL_HOST_USER`,
-    :setting:`EMAIL_HOST_PASSWORD`, :setting:`EMAIL_USE_TLS` and
-    :setting:`EMAIL_USE_SSL` settings in your settings file.
+    :setting:`EMAIL_HOST_PASSWORD`, :setting:`EMAIL_USE_TLS`,
+    :setting:`EMAIL_USE_SSL`, :setting:`EMAIL_SSL_CERTFILE` and
+    :setting:`EMAIL_SSL_KEYFILE` settings in your settings file.
 
     The SMTP backend is the default configuration inherited by Django. If you
     want to specify it explicitly, put the following in your settings::
@@ -481,6 +482,11 @@ SMTP backend
         If unspecified, the default ``timeout`` will be the one provided by
         :func:`socket.getdefaulttimeout()`, which defaults to ``None`` (no timeout).
 
+    .. versionchanged:: 1.8
+
+        The ``ssl_keyfile`` and ``ssl_certfile`` parameters and
+        corresponding settings were added.
+
 .. _topic-email-console-backend:
 
 Console backend

+ 28 - 0
tests/mail/tests.py

@@ -969,6 +969,34 @@ class SMTPBackendTests(BaseEmailBackendTests, SimpleTestCase):
         backend = smtp.EmailBackend()
         self.assertFalse(backend.use_ssl)
 
+    @override_settings(EMAIL_SSL_CERTFILE='foo')
+    def test_email_ssl_certfile_use_settings(self):
+        backend = smtp.EmailBackend()
+        self.assertEqual(backend.ssl_certfile, 'foo')
+
+    @override_settings(EMAIL_SSL_CERTFILE='foo')
+    def test_email_ssl_certfile_override_settings(self):
+        backend = smtp.EmailBackend(ssl_certfile='bar')
+        self.assertEqual(backend.ssl_certfile, 'bar')
+
+    def test_email_ssl_certfile_default_disabled(self):
+        backend = smtp.EmailBackend()
+        self.assertEqual(backend.ssl_certfile, None)
+
+    @override_settings(EMAIL_SSL_KEYFILE='foo')
+    def test_email_ssl_keyfile_use_settings(self):
+        backend = smtp.EmailBackend()
+        self.assertEqual(backend.ssl_keyfile, 'foo')
+
+    @override_settings(EMAIL_SSL_KEYFILE='foo')
+    def test_email_ssl_keyfile_override_settings(self):
+        backend = smtp.EmailBackend(ssl_keyfile='bar')
+        self.assertEqual(backend.ssl_keyfile, 'bar')
+
+    def test_email_ssl_keyfile_default_disabled(self):
+        backend = smtp.EmailBackend()
+        self.assertEqual(backend.ssl_keyfile, None)
+
     @override_settings(EMAIL_USE_TLS=True)
     def test_email_tls_attempts_starttls(self):
         backend = smtp.EmailBackend()