Fix issue #8806: add SSL contexts support to ftplib

This commit is contained in:
Giampaolo Rodolà 2010-05-26 18:06:04 +00:00
parent 60853211da
commit a67299e757
4 changed files with 49 additions and 6 deletions

View file

@ -65,7 +65,7 @@ The module defines the following items:
Support for the :keyword:`with` statement was added. Support for the :keyword:`with` statement was added.
.. class:: FTP_TLS(host='', user='', passwd='', acct='', [keyfile[, certfile[, timeout]]]) .. class:: FTP_TLS(host='', user='', passwd='', acct='', [keyfile[, certfile[, context[, timeout]]]])
A :class:`FTP` subclass which adds TLS support to FTP as described in A :class:`FTP` subclass which adds TLS support to FTP as described in
:rfc:`4217`. :rfc:`4217`.
@ -74,6 +74,9 @@ The module defines the following items:
explicitly ask for it by calling the :meth:`prot_p` method. explicitly ask for it by calling the :meth:`prot_p` method.
*keyfile* and *certfile* are optional -- they can contain a PEM formatted *keyfile* and *certfile* are optional -- they can contain a PEM formatted
private key and certificate chain file name for the SSL connection. private key and certificate chain file name for the SSL connection.
*context* parameter is a :class:`ssl.SSLContext` object which allows
bundling SSL configuration options, certificates and private keys into a
single (potentially long-lived) structure.
.. versionadded:: 3.2 .. versionadded:: 3.2

View file

@ -638,9 +638,17 @@ else:
ssl_version = ssl.PROTOCOL_TLSv1 ssl_version = ssl.PROTOCOL_TLSv1
def __init__(self, host='', user='', passwd='', acct='', keyfile=None, def __init__(self, host='', user='', passwd='', acct='', keyfile=None,
certfile=None, timeout=_GLOBAL_DEFAULT_TIMEOUT): certfile=None, context=None,
timeout=_GLOBAL_DEFAULT_TIMEOUT):
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")
self.keyfile = keyfile self.keyfile = keyfile
self.certfile = certfile self.certfile = certfile
self.context = context
self._prot_p = False self._prot_p = False
FTP.__init__(self, host, user, passwd, acct, timeout) FTP.__init__(self, host, user, passwd, acct, timeout)
@ -657,7 +665,11 @@ else:
resp = self.voidcmd('AUTH TLS') resp = self.voidcmd('AUTH TLS')
else: else:
resp = self.voidcmd('AUTH SSL') resp = self.voidcmd('AUTH SSL')
self.sock = ssl.wrap_socket(self.sock, self.keyfile, self.certfile, if self.context is not None:
self.sock = self.context.wrap_socket(self.sock)
else:
self.sock = ssl.wrap_socket(self.sock, self.keyfile,
self.certfile,
ssl_version=self.ssl_version) ssl_version=self.ssl_version)
self.file = self.sock.makefile(mode='r', encoding=self.encoding) self.file = self.sock.makefile(mode='r', encoding=self.encoding)
return resp return resp
@ -689,6 +701,9 @@ else:
def ntransfercmd(self, cmd, rest=None): def ntransfercmd(self, cmd, rest=None):
conn, size = FTP.ntransfercmd(self, cmd, rest) conn, size = FTP.ntransfercmd(self, cmd, rest)
if self._prot_p: if self._prot_p:
if self.context is not None:
conn = self.context.wrap_socket(conn)
else:
conn = ssl.wrap_socket(conn, self.keyfile, self.certfile, conn = ssl.wrap_socket(conn, self.keyfile, self.certfile,
ssl_version=self.ssl_version) ssl_version=self.ssl_version)
return conn, size return conn, size

View file

@ -719,6 +719,29 @@ class TestTLS_FTPClass(TestCase):
finally: finally:
self.client.ssl_version = ssl.PROTOCOL_TLSv1 self.client.ssl_version = ssl.PROTOCOL_TLSv1
def test_context(self):
self.client.quit()
ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
self.assertRaises(ValueError, ftplib.FTP_TLS, keyfile=CERTFILE,
context=ctx)
self.assertRaises(ValueError, ftplib.FTP_TLS, certfile=CERTFILE,
context=ctx)
self.assertRaises(ValueError, ftplib.FTP_TLS, certfile=CERTFILE,
keyfile=CERTFILE, context=ctx)
self.client = ftplib.FTP_TLS(context=ctx, timeout=2)
self.client.connect(self.server.host, self.server.port)
self.assertNotIsInstance(self.client.sock, ssl.SSLSocket)
self.client.auth()
self.assertIs(self.client.sock.context, ctx)
self.assertIsInstance(self.client.sock, ssl.SSLSocket)
self.client.prot_p()
sock = self.client.transfercmd('list')
self.assertIs(self.client.sock.context, ctx)
self.assertIsInstance(sock, ssl.SSLSocket)
sock.close()
class TestTimeouts(TestCase): class TestTimeouts(TestCase):

View file

@ -392,6 +392,8 @@ C-API
Library Library
------- -------
- Issue #8806: add SSL contexts support to ftplib.
- Issue #4769: Fix main() function of the base64 module, use sys.stdin.buffer - Issue #4769: Fix main() function of the base64 module, use sys.stdin.buffer
and sys.stdout.buffer (instead of sys.stdin and sys.stdout) to use the bytes and sys.stdout.buffer (instead of sys.stdin and sys.stdout) to use the bytes
API API