mirror of https://github.com/python/cpython
gh-94172: Remove keyfile, certfile and check_hostname parameters (#94173)
Remove the keyfile, certfile and check_hostname parameters, deprecated since Python 3.6, in modules: ftplib, http.client, imaplib, poplib and smtplib. Use the context parameter (ssl_context in imaplib) instead. Parameters following the removed parameters become keyword-only parameters. ftplib: Remove the FTP_TLS.ssl_version class attribute: use the context parameter instead.
This commit is contained in:
parent
9c4ae037b9
commit
ef0e72b31d
|
@ -540,6 +540,16 @@ Removed
|
|||
<https://github.com/sphinx-contrib/sphinx-lint>`_.
|
||||
(Contributed by Julien Palard in :gh:`98179`.)
|
||||
|
||||
* Remove the *keyfile*, *certfile* and *check_hostname* parameters, deprecated
|
||||
since Python 3.6, in modules: :mod:`ftplib`, :mod:`http.client`,
|
||||
:mod:`imaplib`, :mod:`poplib` and :mod:`smtplib`. Use the *context* parameter
|
||||
(*ssl_context* in :mod:`imaplib`) instead.
|
||||
(Contributed by Victor Stinner in :gh:`94172`.)
|
||||
|
||||
* :mod:`ftplib`: Remove the ``FTP_TLS.ssl_version`` class attribute: use the
|
||||
*context* parameter instead.
|
||||
(Contributed by Victor Stinner in :gh:`94172`.)
|
||||
|
||||
|
||||
Porting to Python 3.12
|
||||
======================
|
||||
|
|
|
@ -713,28 +713,12 @@ else:
|
|||
'221 Goodbye.'
|
||||
>>>
|
||||
'''
|
||||
ssl_version = ssl.PROTOCOL_TLS_CLIENT
|
||||
|
||||
def __init__(self, host='', user='', passwd='', acct='',
|
||||
keyfile=None, certfile=None, context=None,
|
||||
timeout=_GLOBAL_DEFAULT_TIMEOUT, source_address=None, *,
|
||||
encoding='utf-8'):
|
||||
if context is not None and keyfile is not None:
|
||||
raise ValueError("context and keyfile arguments are mutually "
|
||||
"exclusive")
|
||||
if context is not None and certfile is not None:
|
||||
raise ValueError("context and certfile arguments are mutually "
|
||||
"exclusive")
|
||||
if keyfile is not None or certfile is not None:
|
||||
import warnings
|
||||
warnings.warn("keyfile and certfile are deprecated, use a "
|
||||
"custom context instead", DeprecationWarning, 2)
|
||||
self.keyfile = keyfile
|
||||
self.certfile = certfile
|
||||
*, context=None, timeout=_GLOBAL_DEFAULT_TIMEOUT,
|
||||
source_address=None, encoding='utf-8'):
|
||||
if context is None:
|
||||
context = ssl._create_stdlib_context(self.ssl_version,
|
||||
certfile=certfile,
|
||||
keyfile=keyfile)
|
||||
context = ssl._create_stdlib_context()
|
||||
self.context = context
|
||||
self._prot_p = False
|
||||
super().__init__(host, user, passwd, acct,
|
||||
|
@ -749,7 +733,7 @@ else:
|
|||
'''Set up secure control connection by using TLS/SSL.'''
|
||||
if isinstance(self.sock, ssl.SSLSocket):
|
||||
raise ValueError("Already using TLS")
|
||||
if self.ssl_version >= ssl.PROTOCOL_TLS:
|
||||
if self.context.protocol >= ssl.PROTOCOL_TLS:
|
||||
resp = self.voidcmd('AUTH TLS')
|
||||
else:
|
||||
resp = self.voidcmd('AUTH SSL')
|
||||
|
|
|
@ -1414,33 +1414,14 @@ else:
|
|||
|
||||
default_port = HTTPS_PORT
|
||||
|
||||
# XXX Should key_file and cert_file be deprecated in favour of context?
|
||||
|
||||
def __init__(self, host, port=None, key_file=None, cert_file=None,
|
||||
timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
|
||||
source_address=None, *, context=None,
|
||||
check_hostname=None, blocksize=8192):
|
||||
def __init__(self, host, port=None,
|
||||
*, timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
|
||||
source_address=None, context=None, blocksize=8192):
|
||||
super(HTTPSConnection, self).__init__(host, port, timeout,
|
||||
source_address,
|
||||
blocksize=blocksize)
|
||||
if (key_file is not None or cert_file is not None or
|
||||
check_hostname is not None):
|
||||
import warnings
|
||||
warnings.warn("key_file, cert_file and check_hostname are "
|
||||
"deprecated, use a custom context instead.",
|
||||
DeprecationWarning, 2)
|
||||
self.key_file = key_file
|
||||
self.cert_file = cert_file
|
||||
if context is None:
|
||||
context = _create_https_context(self._http_vsn)
|
||||
if check_hostname is not None:
|
||||
context.check_hostname = check_hostname
|
||||
if key_file or cert_file:
|
||||
context.load_cert_chain(cert_file, key_file)
|
||||
# cert and key file means the user wants to authenticate.
|
||||
# enable TLS 1.3 PHA implicitly even for custom contexts.
|
||||
if context.post_handshake_auth is not None:
|
||||
context.post_handshake_auth = True
|
||||
self._context = context
|
||||
|
||||
def connect(self):
|
||||
|
|
|
@ -1285,16 +1285,12 @@ if HAVE_SSL:
|
|||
|
||||
"""IMAP4 client class over SSL connection
|
||||
|
||||
Instantiate with: IMAP4_SSL([host[, port[, keyfile[, certfile[, ssl_context[, timeout=None]]]]]])
|
||||
Instantiate with: IMAP4_SSL([host[, port[, ssl_context[, timeout=None]]]])
|
||||
|
||||
host - host's name (default: localhost);
|
||||
port - port number (default: standard IMAP4 SSL port);
|
||||
keyfile - PEM formatted file that contains your private key (default: None);
|
||||
certfile - PEM formatted certificate chain file (default: None);
|
||||
ssl_context - a SSLContext object that contains your certificate chain
|
||||
and private key (default: None)
|
||||
Note: if ssl_context is provided, then parameters keyfile or
|
||||
certfile should not be set otherwise ValueError is raised.
|
||||
timeout - socket timeout (default: None) If timeout is not given or is None,
|
||||
the global default socket timeout is used
|
||||
|
||||
|
@ -1302,23 +1298,10 @@ if HAVE_SSL:
|
|||
"""
|
||||
|
||||
|
||||
def __init__(self, host='', port=IMAP4_SSL_PORT, keyfile=None,
|
||||
certfile=None, ssl_context=None, timeout=None):
|
||||
if ssl_context is not None and keyfile is not None:
|
||||
raise ValueError("ssl_context and keyfile arguments are mutually "
|
||||
"exclusive")
|
||||
if ssl_context is not None and certfile is not None:
|
||||
raise ValueError("ssl_context and certfile arguments are mutually "
|
||||
"exclusive")
|
||||
if keyfile is not None or certfile is not None:
|
||||
import warnings
|
||||
warnings.warn("keyfile and certfile are deprecated, use a "
|
||||
"custom ssl_context instead", DeprecationWarning, 2)
|
||||
self.keyfile = keyfile
|
||||
self.certfile = certfile
|
||||
def __init__(self, host='', port=IMAP4_SSL_PORT,
|
||||
*, ssl_context=None, timeout=None):
|
||||
if ssl_context is None:
|
||||
ssl_context = ssl._create_stdlib_context(certfile=certfile,
|
||||
keyfile=keyfile)
|
||||
ssl_context = ssl._create_stdlib_context()
|
||||
self.ssl_context = ssl_context
|
||||
IMAP4.__init__(self, host, port, timeout)
|
||||
|
||||
|
|
|
@ -419,35 +419,19 @@ if HAVE_SSL:
|
|||
class POP3_SSL(POP3):
|
||||
"""POP3 client class over SSL connection
|
||||
|
||||
Instantiate with: POP3_SSL(hostname, port=995, keyfile=None, certfile=None,
|
||||
context=None)
|
||||
Instantiate with: POP3_SSL(hostname, port=995, context=None)
|
||||
|
||||
hostname - the hostname of the pop3 over ssl server
|
||||
port - port number
|
||||
keyfile - PEM formatted file that contains your private key
|
||||
certfile - PEM formatted certificate chain file
|
||||
context - a ssl.SSLContext
|
||||
|
||||
See the methods of the parent class POP3 for more documentation.
|
||||
"""
|
||||
|
||||
def __init__(self, host, port=POP3_SSL_PORT, keyfile=None, certfile=None,
|
||||
timeout=socket._GLOBAL_DEFAULT_TIMEOUT, context=None):
|
||||
if context is not None and keyfile is not None:
|
||||
raise ValueError("context and keyfile arguments are mutually "
|
||||
"exclusive")
|
||||
if context is not None and certfile is not None:
|
||||
raise ValueError("context and certfile arguments are mutually "
|
||||
"exclusive")
|
||||
if keyfile is not None or certfile is not None:
|
||||
import warnings
|
||||
warnings.warn("keyfile and certfile are deprecated, use a "
|
||||
"custom context instead", DeprecationWarning, 2)
|
||||
self.keyfile = keyfile
|
||||
self.certfile = certfile
|
||||
def __init__(self, host, port=POP3_SSL_PORT,
|
||||
*, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, context=None):
|
||||
if context is None:
|
||||
context = ssl._create_stdlib_context(certfile=certfile,
|
||||
keyfile=keyfile)
|
||||
context = ssl._create_stdlib_context()
|
||||
self.context = context
|
||||
POP3.__init__(self, host, port, timeout)
|
||||
|
||||
|
@ -457,7 +441,7 @@ if HAVE_SSL:
|
|||
server_hostname=self.host)
|
||||
return sock
|
||||
|
||||
def stls(self, keyfile=None, certfile=None, context=None):
|
||||
def stls(self, context=None):
|
||||
"""The method unconditionally raises an exception since the
|
||||
STLS command doesn't make any sense on an already established
|
||||
SSL/TLS session.
|
||||
|
|
|
@ -749,14 +749,14 @@ class SMTP:
|
|||
# We could not login successfully. Return result of last attempt.
|
||||
raise last_exception
|
||||
|
||||
def starttls(self, keyfile=None, certfile=None, context=None):
|
||||
def starttls(self, *, context=None):
|
||||
"""Puts the connection to the SMTP server into TLS mode.
|
||||
|
||||
If there has been no previous EHLO or HELO command this session, this
|
||||
method tries ESMTP EHLO first.
|
||||
|
||||
If the server supports TLS, this will encrypt the rest of the SMTP
|
||||
session. If you provide the keyfile and certfile parameters,
|
||||
session. If you provide the context parameter,
|
||||
the identity of the SMTP server and client can be checked. This,
|
||||
however, depends on whether the socket module really checks the
|
||||
certificates.
|
||||
|
@ -774,19 +774,8 @@ class SMTP:
|
|||
if resp == 220:
|
||||
if not _have_ssl:
|
||||
raise RuntimeError("No SSL support included in this Python")
|
||||
if context is not None and keyfile is not None:
|
||||
raise ValueError("context and keyfile arguments are mutually "
|
||||
"exclusive")
|
||||
if context is not None and certfile is not None:
|
||||
raise ValueError("context and certfile arguments are mutually "
|
||||
"exclusive")
|
||||
if keyfile is not None or certfile is not None:
|
||||
import warnings
|
||||
warnings.warn("keyfile and certfile are deprecated, use a "
|
||||
"custom context instead", DeprecationWarning, 2)
|
||||
if context is None:
|
||||
context = ssl._create_stdlib_context(certfile=certfile,
|
||||
keyfile=keyfile)
|
||||
context = ssl._create_stdlib_context()
|
||||
self.sock = context.wrap_socket(self.sock,
|
||||
server_hostname=self._host)
|
||||
self.file = None
|
||||
|
@ -1017,35 +1006,18 @@ if _have_ssl:
|
|||
compiled with SSL support). If host is not specified, '' (the local
|
||||
host) is used. If port is omitted, the standard SMTP-over-SSL port
|
||||
(465) is used. local_hostname and source_address have the same meaning
|
||||
as they do in the SMTP class. keyfile and certfile are also optional -
|
||||
they can contain a PEM formatted private key and certificate chain file
|
||||
for the SSL connection. context also optional, can contain a
|
||||
SSLContext, and is an alternative to keyfile and certfile; If it is
|
||||
specified both keyfile and certfile must be None.
|
||||
as they do in the SMTP class. context also optional, can contain a
|
||||
SSLContext.
|
||||
|
||||
"""
|
||||
|
||||
default_port = SMTP_SSL_PORT
|
||||
|
||||
def __init__(self, host='', port=0, local_hostname=None,
|
||||
keyfile=None, certfile=None,
|
||||
timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
|
||||
*, timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
|
||||
source_address=None, context=None):
|
||||
if context is not None and keyfile is not None:
|
||||
raise ValueError("context and keyfile arguments are mutually "
|
||||
"exclusive")
|
||||
if context is not None and certfile is not None:
|
||||
raise ValueError("context and certfile arguments are mutually "
|
||||
"exclusive")
|
||||
if keyfile is not None or certfile is not None:
|
||||
import warnings
|
||||
warnings.warn("keyfile and certfile are deprecated, use a "
|
||||
"custom context instead", DeprecationWarning, 2)
|
||||
self.keyfile = keyfile
|
||||
self.certfile = certfile
|
||||
if context is None:
|
||||
context = ssl._create_stdlib_context(certfile=certfile,
|
||||
keyfile=keyfile)
|
||||
context = ssl._create_stdlib_context()
|
||||
self.context = context
|
||||
SMTP.__init__(self, host, port, local_hostname, timeout,
|
||||
source_address)
|
||||
|
|
|
@ -984,11 +984,11 @@ class TestTLS_FTPClass(TestCase):
|
|||
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
|
||||
ctx.check_hostname = False
|
||||
ctx.verify_mode = ssl.CERT_NONE
|
||||
self.assertRaises(ValueError, ftplib.FTP_TLS, keyfile=CERTFILE,
|
||||
self.assertRaises(TypeError, ftplib.FTP_TLS, keyfile=CERTFILE,
|
||||
context=ctx)
|
||||
self.assertRaises(ValueError, ftplib.FTP_TLS, certfile=CERTFILE,
|
||||
self.assertRaises(TypeError, ftplib.FTP_TLS, certfile=CERTFILE,
|
||||
context=ctx)
|
||||
self.assertRaises(ValueError, ftplib.FTP_TLS, certfile=CERTFILE,
|
||||
self.assertRaises(TypeError, ftplib.FTP_TLS, certfile=CERTFILE,
|
||||
keyfile=CERTFILE, context=ctx)
|
||||
|
||||
self.client = ftplib.FTP_TLS(context=ctx, timeout=TIMEOUT)
|
||||
|
|
|
@ -1978,7 +1978,7 @@ class HTTPSTest(TestCase):
|
|||
self.assertEqual(exc_info.exception.reason, 'CERTIFICATE_VERIFY_FAILED')
|
||||
|
||||
def test_local_good_hostname(self):
|
||||
# The (valid) cert validates the HTTP hostname
|
||||
# The (valid) cert validates the HTTPS hostname
|
||||
import ssl
|
||||
server = self.make_server(CERT_localhost)
|
||||
context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
|
||||
|
@ -1991,7 +1991,7 @@ class HTTPSTest(TestCase):
|
|||
self.assertEqual(resp.status, 404)
|
||||
|
||||
def test_local_bad_hostname(self):
|
||||
# The (valid) cert doesn't validate the HTTP hostname
|
||||
# The (valid) cert doesn't validate the HTTPS hostname
|
||||
import ssl
|
||||
server = self.make_server(CERT_fakehostname)
|
||||
context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
|
||||
|
@ -1999,38 +1999,21 @@ class HTTPSTest(TestCase):
|
|||
h = client.HTTPSConnection('localhost', server.port, context=context)
|
||||
with self.assertRaises(ssl.CertificateError):
|
||||
h.request('GET', '/')
|
||||
# Same with explicit check_hostname=True
|
||||
with warnings_helper.check_warnings(('', DeprecationWarning)):
|
||||
h = client.HTTPSConnection('localhost', server.port,
|
||||
context=context, check_hostname=True)
|
||||
|
||||
# Same with explicit context.check_hostname=True
|
||||
context.check_hostname = True
|
||||
h = client.HTTPSConnection('localhost', server.port, context=context)
|
||||
with self.assertRaises(ssl.CertificateError):
|
||||
h.request('GET', '/')
|
||||
# With check_hostname=False, the mismatching is ignored
|
||||
context.check_hostname = False
|
||||
with warnings_helper.check_warnings(('', DeprecationWarning)):
|
||||
h = client.HTTPSConnection('localhost', server.port,
|
||||
context=context, check_hostname=False)
|
||||
h.request('GET', '/nonexistent')
|
||||
resp = h.getresponse()
|
||||
resp.close()
|
||||
h.close()
|
||||
self.assertEqual(resp.status, 404)
|
||||
# The context's check_hostname setting is used if one isn't passed to
|
||||
# HTTPSConnection.
|
||||
|
||||
# With context.check_hostname=False, the mismatching is ignored
|
||||
context.check_hostname = False
|
||||
h = client.HTTPSConnection('localhost', server.port, context=context)
|
||||
h.request('GET', '/nonexistent')
|
||||
resp = h.getresponse()
|
||||
self.assertEqual(resp.status, 404)
|
||||
resp.close()
|
||||
h.close()
|
||||
# Passing check_hostname to HTTPSConnection should override the
|
||||
# context's setting.
|
||||
with warnings_helper.check_warnings(('', DeprecationWarning)):
|
||||
h = client.HTTPSConnection('localhost', server.port,
|
||||
context=context, check_hostname=True)
|
||||
with self.assertRaises(ssl.CertificateError):
|
||||
h.request('GET', '/')
|
||||
self.assertEqual(resp.status, 404)
|
||||
|
||||
@unittest.skipIf(not hasattr(client, 'HTTPSConnection'),
|
||||
'http.client.HTTPSConnection not available')
|
||||
|
@ -2066,11 +2049,9 @@ class HTTPSTest(TestCase):
|
|||
self.assertIs(h._context, context)
|
||||
self.assertFalse(h._context.post_handshake_auth)
|
||||
|
||||
with warnings.catch_warnings():
|
||||
warnings.filterwarnings('ignore', 'key_file, cert_file and check_hostname are deprecated',
|
||||
DeprecationWarning)
|
||||
h = client.HTTPSConnection('localhost', 443, context=context,
|
||||
cert_file=CERT_localhost)
|
||||
context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT, cert_file=CERT_localhost)
|
||||
context.post_handshake_auth = True
|
||||
h = client.HTTPSConnection('localhost', 443, context=context)
|
||||
self.assertTrue(h._context.post_handshake_auth)
|
||||
|
||||
|
||||
|
|
|
@ -573,15 +573,6 @@ class NewIMAPSSLTests(NewIMAPTestsMixin, unittest.TestCase):
|
|||
ssl_context=ssl_context)
|
||||
client.shutdown()
|
||||
|
||||
# Mock the private method _connect(), so mark the test as specific
|
||||
# to CPython stdlib
|
||||
@cpython_only
|
||||
def test_certfile_arg_warn(self):
|
||||
with warnings_helper.check_warnings(('', DeprecationWarning)):
|
||||
with mock.patch.object(self.imap_class, 'open'):
|
||||
with mock.patch.object(self.imap_class, '_connect'):
|
||||
self.imap_class('localhost', 143, certfile=CERTFILE)
|
||||
|
||||
class ThreadedNetworkedTests(unittest.TestCase):
|
||||
server_class = socketserver.TCPServer
|
||||
imap_class = imaplib.IMAP4
|
||||
|
@ -1070,18 +1061,6 @@ class RemoteIMAP_SSLTest(RemoteIMAPTest):
|
|||
rs = _server.logout()
|
||||
self.assertEqual(rs[0], 'BYE', rs)
|
||||
|
||||
def test_ssl_context_certfile_exclusive(self):
|
||||
with socket_helper.transient_internet(self.host):
|
||||
self.assertRaises(
|
||||
ValueError, self.imap_class, self.host, self.port,
|
||||
certfile=CERTFILE, ssl_context=self.create_ssl_context())
|
||||
|
||||
def test_ssl_context_keyfile_exclusive(self):
|
||||
with socket_helper.transient_internet(self.host):
|
||||
self.assertRaises(
|
||||
ValueError, self.imap_class, self.host, self.port,
|
||||
keyfile=CERTFILE, ssl_context=self.create_ssl_context())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
|
|
@ -425,13 +425,6 @@ class TestPOP3_SSLClass(TestPOP3Class):
|
|||
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
|
||||
ctx.check_hostname = False
|
||||
ctx.verify_mode = ssl.CERT_NONE
|
||||
self.assertRaises(ValueError, poplib.POP3_SSL, self.server.host,
|
||||
self.server.port, keyfile=CERTFILE, context=ctx)
|
||||
self.assertRaises(ValueError, poplib.POP3_SSL, self.server.host,
|
||||
self.server.port, certfile=CERTFILE, context=ctx)
|
||||
self.assertRaises(ValueError, poplib.POP3_SSL, self.server.host,
|
||||
self.server.port, keyfile=CERTFILE,
|
||||
certfile=CERTFILE, context=ctx)
|
||||
|
||||
self.client.quit()
|
||||
self.client = poplib.POP3_SSL(self.server.host, self.server.port,
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
Remove the *keyfile*, *certfile* and *check_hostname* parameters, deprecated
|
||||
since Python 3.6, in modules: :mod:`ftplib`, :mod:`http.client`,
|
||||
:mod:`imaplib`, :mod:`poplib` and :mod:`smtplib`. Use the *context*
|
||||
parameter (*ssl_context* in :mod:`imaplib`) instead. Patch by Victor
|
||||
Stinner.
|
|
@ -0,0 +1,2 @@
|
|||
:mod:`ftplib`: Remove the ``FTP_TLS.ssl_version`` class attribute: use the
|
||||
*context* parameter instead. Patch by Victor Stinner
|
Loading…
Reference in New Issue