Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-amqp for openSUSE:Factory checked in at 2021-02-04 20:23:00 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-amqp (Old) and /work/SRC/openSUSE:Factory/.python-amqp.new.28504 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-amqp" Thu Feb 4 20:23:00 2021 rev:36 rq:868434 version:5.0.5 Changes: -------- --- /work/SRC/openSUSE:Factory/python-amqp/python-amqp.changes 2020-12-23 14:19:17.385604088 +0100 +++ /work/SRC/openSUSE:Factory/.python-amqp.new.28504/python-amqp.changes 2021-02-04 20:23:49.666803752 +0100 @@ -1,0 +2,23 @@ +Mon Feb 1 17:40:55 UTC 2021 - Dirk M??ller <dmuel...@suse.com> + +- update to 5.0.5: + - Removed mistakenly introduced code which was causing import errors + - Add missing load_default_certs() call to fix a regression in v5.0.3 release. (#350) + - Change the default value of ssl_version to None. When not set, the + proper value between ssl.PROTOCOL_TLS_CLIENT and ssl.PROTOCOL_TLS_SERVER + will be selected based on the param server_side in order to create + a TLS Context object with better defaults that fit the desired + connection side. + - Change the default value of cert_reqs to None. The default value + of ctx.verify_mode is ssl.CERT_NONE, but when ssl.PROTOCOL_TLS_CLIENT + is used, ctx.verify_mode defaults to ssl.CERT_REQUIRED. + - Fix context.check_hostname logic. Checking the hostname depends on + having support of the SNI TLS extension and being provided with a + server_hostname value. Another important thing to mention is that + enabling hostname checking automatically sets verify_mode from + ssl.CERT_NONE to ssl.CERT_REQUIRED in the stdlib ssl and it cannot + be set back to ssl.CERT_NONE as long as hostname checking is enabled. + - Refactor the SNI tests to test one thing at a time and removing some + tests that were being repeated over an + +------------------------------------------------------------------- Old: ---- amqp-5.0.2.tar.gz New: ---- amqp-5.0.5.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-amqp.spec ++++++ --- /var/tmp/diff_new_pack.W4L7x9/_old 2021-02-04 20:23:50.458804958 +0100 +++ /var/tmp/diff_new_pack.W4L7x9/_new 2021-02-04 20:23:50.458804958 +0100 @@ -1,7 +1,7 @@ # # spec file for package python-amqp # -# Copyright (c) 2020 SUSE LLC +# Copyright (c) 2021 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -19,7 +19,7 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} %define skip_python2 1 Name: python-amqp -Version: 5.0.2 +Version: 5.0.5 Release: 0 Summary: Low-level AMQP client for Python (fork of amqplib) License: LGPL-2.1-or-later ++++++ amqp-5.0.2.tar.gz -> amqp-5.0.5.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/amqp-5.0.2/Changelog new/amqp-5.0.5/Changelog --- old/amqp-5.0.2/Changelog 2020-11-08 17:49:29.000000000 +0100 +++ new/amqp-5.0.5/Changelog 2021-01-28 11:08:06.000000000 +0100 @@ -5,6 +5,56 @@ The previous amqplib changelog is here: http://code.google.com/p/py-amqplib/source/browse/CHANGES +.. _version-5.0.5: + +5.0.5 +===== +:release-date: 2021-01-28 4:30 P.M UTC+6:00 +:release-by: Asif Saif Uddin + +- Removed mistakenly introduced code which was causing import errors + + + +.. _version-5.0.4: + +5.0.4 +===== +:release-date: 2021-01-28 2:30 P.M UTC+6:00 +:release-by: Asif Saif Uddin + +- Add missing load_default_certs() call to fix a regression in v5.0.3 release. (#350) + + +.. _version-5.0.3: + +5.0.3 +===== +:release-date: 2021-01-19 9:00 P.M UTC+6:00 +:release-by: Asif Saif Uddin + +- Change the default value of ssl_version to None. When not set, the + proper value between ssl.PROTOCOL_TLS_CLIENT and ssl.PROTOCOL_TLS_SERVER + will be selected based on the param server_side in order to create + a TLS Context object with better defaults that fit the desired + connection side. + +- Change the default value of cert_reqs to None. The default value + of ctx.verify_mode is ssl.CERT_NONE, but when ssl.PROTOCOL_TLS_CLIENT + is used, ctx.verify_mode defaults to ssl.CERT_REQUIRED. + +- Fix context.check_hostname logic. Checking the hostname depends on + having support of the SNI TLS extension and being provided with a + server_hostname value. Another important thing to mention is that + enabling hostname checking automatically sets verify_mode from + ssl.CERT_NONE to ssl.CERT_REQUIRED in the stdlib ssl and it cannot + be set back to ssl.CERT_NONE as long as hostname checking is enabled. + +- Refactor the SNI tests to test one thing at a time and removing some + tests that were being repeated over and over. + + + .. _version-5.0.2: 5.0.2 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/amqp-5.0.2/PKG-INFO new/amqp-5.0.5/PKG-INFO --- old/amqp-5.0.2/PKG-INFO 2020-11-08 17:50:10.838995200 +0100 +++ new/amqp-5.0.5/PKG-INFO 2021-01-28 11:15:26.662340900 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 1.2 Name: amqp -Version: 5.0.2 +Version: 5.0.5 Summary: Low-level AMQP client for Python (fork of amqplib). Home-page: http://github.com/celery/py-amqp Author: Barry Pederson diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/amqp-5.0.2/README.rst new/amqp-5.0.5/README.rst --- old/amqp-5.0.2/README.rst 2020-11-08 17:49:45.000000000 +0100 +++ new/amqp-5.0.5/README.rst 2021-01-28 11:09:02.000000000 +0100 @@ -4,7 +4,7 @@ |build-status| |coverage| |license| |wheel| |pyversion| |pyimp| -:Version: 5.0.2 +:Version: 5.0.5 :Web: https://amqp.readthedocs.io/ :Download: https://pypi.org/project/amqp/ :Source: http://github.com/celery/py-amqp/ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/amqp-5.0.2/amqp/__init__.py new/amqp-5.0.5/amqp/__init__.py --- old/amqp-5.0.2/amqp/__init__.py 2020-11-08 17:49:45.000000000 +0100 +++ new/amqp-5.0.5/amqp/__init__.py 2021-01-28 11:08:38.000000000 +0100 @@ -4,7 +4,7 @@ import re from collections import namedtuple -__version__ = '5.0.2' +__version__ = '5.0.5' __author__ = 'Barry Pederson' __maintainer__ = 'Asif Saif Uddin, Matus Valo' __contact__ = 'pya...@celeryproject.org' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/amqp-5.0.2/amqp/connection.py new/amqp-5.0.5/amqp/connection.py --- old/amqp-5.0.2/amqp/connection.py 2020-09-21 10:47:19.000000000 +0200 +++ new/amqp-5.0.5/amqp/connection.py 2020-12-13 06:35:15.000000000 +0100 @@ -94,9 +94,10 @@ client name. For EXTERNAL authentication both userid and password are ignored. - The 'ssl' parameter may be simply True/False, or for Python >= 3.6 - a dictionary of options to pass to ssl.SSLContext such as - requiring certain certificates. + The 'ssl' parameter may be simply True/False, or + a dictionary of options to pass to :class:`ssl.SSLContext` such as + requiring certain certificates. For details, refer ``ssl`` parameter of + :class:`~amqp.transport.SSLTransport`. The "socket_settings" parameter is a dictionary defining tcp settings which will be applied as socket options. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/amqp-5.0.2/amqp/transport.py new/amqp-5.0.5/amqp/transport.py --- old/amqp-5.0.2/amqp/transport.py 2020-11-08 15:38:51.000000000 +0100 +++ new/amqp-5.0.5/amqp/transport.py 2021-01-28 09:19:05.000000000 +0100 @@ -53,7 +53,36 @@ class _AbstractTransport: - """Common superclass for TCP and SSL transports.""" + """Common superclass for TCP and SSL transports. + + PARAMETERS: + host: str + + Broker address in format ``HOSTNAME:PORT``. + + connect_timeout: int + + Timeout of creating new connection. + + read_timeout: int + + sets ``SO_RCVTIMEO`` parameter of socket. + + write_timeout: int + + sets ``SO_SNDTIMEO`` parameter of socket. + + socket_settings: dict + + dictionary containing `optname` and ``optval`` passed to + ``setsockopt(2)``. + + raise_on_initial_eintr: bool + + when True, ``socket.timeout`` is raised + when exception is received during first read. See ``_read()`` for + details. + """ def __init__(self, host, connect_timeout=None, read_timeout=None, write_timeout=None, @@ -255,12 +284,14 @@ def read_frame(self, unpack=unpack): """Parse AMQP frame. - Frame has following format: - 0 1 3 7 size+7 size+8 - +------+---------+---------+ +-------------+ +-----------+ - | type | channel | size | | payload | | frame-end | - +------+---------+---------+ +-------------+ +-----------+ - octet short long 'size' octets octet + Frame has following format:: + + 0 1 3 7 size+7 size+8 + +------+---------+---------+ +-------------+ +-----------+ + | type | channel | size | | payload | | frame-end | + +------+---------+---------+ +-------------+ +-----------+ + octet short long 'size' octets octet + """ read = self._read read_frame_buffer = EMPTY_BUFFER @@ -328,7 +359,39 @@ class SSLTransport(_AbstractTransport): - """Transport that works over SSL.""" + """Transport that works over SSL. + + PARAMETERS: + host: str + + Broker address in format ``HOSTNAME:PORT``. + + connect_timeout: int + + Timeout of creating new connection. + + ssl: bool|dict + + parameters of TLS subsystem. + - when ``ssl`` is not dictionary, defaults of TLS are used + - otherwise: + - if ``ssl`` dictionary contains ``context`` key, + :attr:`~SSLTransport._wrap_context` is used for wrapping + socket. ``context`` is a dictionary passed to + :attr:`~SSLTransport._wrap_context` as context parameter. + All others items from ``ssl`` argument are passed as + ``sslopts``. + - if ``ssl`` dictionary does not contain ``context`` key, + :attr:`~SSLTransport._wrap_socket_sni` is used for + wrapping socket. All items in ``ssl`` argument are + passed to :attr:`~SSLTransport._wrap_socket_sni` as + parameters. + + kwargs: + + additional arguments of + :class:`~amqp.transport._AbstractTransport` class + """ def __init__(self, host, connect_timeout=None, ssl=None, **kwargs): self.sslopts = ssl if isinstance(ssl, dict) else {} @@ -348,19 +411,96 @@ return self._wrap_socket_sni(sock, **sslopts) def _wrap_context(self, sock, sslopts, check_hostname=None, **ctx_options): + """Wrap socket without SNI headers. + + PARAMETERS: + sock: socket.socket + + Socket to be wrapped. + + sslopts: dict + + Parameters of :attr:`ssl.SSLContext.wrap_socket`. + + check_hostname + + Whether to match the peer cert???s hostname. See + :attr:`ssl.SSLContext.check_hostname` for details. + + ctx_options + + Parameters of :attr:`ssl.create_default_context`. + """ ctx = ssl.create_default_context(**ctx_options) ctx.check_hostname = check_hostname return ctx.wrap_socket(sock, **sslopts) def _wrap_socket_sni(self, sock, keyfile=None, certfile=None, - server_side=False, cert_reqs=ssl.CERT_NONE, + server_side=False, cert_reqs=None, ca_certs=None, do_handshake_on_connect=False, suppress_ragged_eofs=True, server_hostname=None, - ciphers=None, ssl_version=ssl.PROTOCOL_TLS): + ciphers=None, ssl_version=None): """Socket wrap with SNI headers. - stdlib `ssl.SSLContext.wrap_socket` method augmented with support for - setting the server_hostname field required for SNI hostname header + stdlib :attr:`ssl.SSLContext.wrap_socket` method augmented with support + for setting the server_hostname field required for SNI hostname header. + + PARAMETERS: + sock: socket.socket + + Socket to be wrapped. + + keyfile: str + + Path to the private key + + certfile: str + + Path to the certificate + + server_side: bool + + Identifies whether server-side or client-side + behavior is desired from this socket. See + :attr:`~ssl.SSLContext.wrap_socket` for details. + + cert_reqs: ssl.VerifyMode + + When set to other than :attr:`ssl.CERT_NONE`, peers certificate + is checked. Possible values are :attr:`ssl.CERT_NONE`, + :attr:`ssl.CERT_OPTIONAL` and :attr:`ssl.CERT_REQUIRED`. + + ca_certs: str + + Path to ???certification authority??? (CA) certificates + used to validate other peers??? certificates when ``cert_reqs`` + is other than :attr:`ssl.CERT_NONE`. + + do_handshake_on_connect: bool + + Specifies whether to do the SSL + handshake automatically. See + :attr:`~ssl.SSLContext.wrap_socket` for details. + + suppress_ragged_eofs (bool): + + See :attr:`~ssl.SSLContext.wrap_socket` for details. + + server_hostname: str + + Specifies the hostname of the service which + we are connecting to. See :attr:`~ssl.SSLContext.wrap_socket` + for details. + + ciphers: str + + Available ciphers for sockets created with this + context. See :attr:`ssl.SSLContext.set_ciphers` + + ssl_version: + + Protocol of the SSL Context. The value is one of + ``ssl.PROTOCOL_*`` constants. """ opts = { 'sock': sock, @@ -370,20 +510,39 @@ 'server_hostname': server_hostname, } + if ssl_version is None: + ssl_version = ( + ssl.PROTOCOL_TLS_SERVER + if server_side + else ssl.PROTOCOL_TLS_CLIENT + ) + context = ssl.SSLContext(ssl_version) + if certfile is not None: context.load_cert_chain(certfile, keyfile) if ca_certs is not None: context.load_verify_locations(ca_certs) - if ciphers: + if ciphers is not None: context.set_ciphers(ciphers) - if cert_reqs != ssl.CERT_NONE: - context.check_hostname = True - # Set SNI headers if supported - if (server_hostname is not None) and ( - hasattr(ssl, 'HAS_SNI') and ssl.HAS_SNI) and ( - hasattr(ssl, 'SSLContext')): + if cert_reqs is not None: context.verify_mode = cert_reqs + # Set SNI headers if supported + try: + context.check_hostname = ( + ssl.HAS_SNI and server_hostname is not None + ) + except AttributeError: + pass # ask forgiveness not permission + + if ca_certs is None and context.verify_mode != ssl.CERT_NONE: + purpose = ( + ssl.Purpose.CLIENT_AUTH + if server_side + else ssl.Purpose.SERVER_AUTH + ) + context.load_default_certs(purpose) + sock = context.wrap_socket(**opts) return sock @@ -438,7 +597,10 @@ class TCPTransport(_AbstractTransport): - """Transport that deals directly with TCP socket.""" + """Transport that deals directly with TCP socket. + + All parameters are :class:`~amqp.transport._AbstractTransport` class. + """ def _setup_transport(self): # Setup to _write() directly to the socket, and @@ -476,7 +638,29 @@ """Create transport. Given a few parameters from the Connection constructor, - select and create a subclass of _AbstractTransport. + select and create a subclass of + :class:`~amqp.transport._AbstractTransport`. + + PARAMETERS: + + host: str + + Broker address in format ``HOSTNAME:PORT``. + + connect_timeout: int + + Timeout of creating new connection. + + ssl: bool|dict + + If set, :class:`~amqp.transport.SSLTransport` is used + and ``ssl`` parameter is passed to it. Otherwise + :class:`~amqp.transport.TCPTransport` is used. + + kwargs: + + additional arguments of :class:`~amqp.transport._AbstractTransport` + class """ transport = SSLTransport if ssl else TCPTransport return transport(host, connect_timeout=connect_timeout, ssl=ssl, **kwargs) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/amqp-5.0.2/amqp.egg-info/PKG-INFO new/amqp-5.0.5/amqp.egg-info/PKG-INFO --- old/amqp-5.0.2/amqp.egg-info/PKG-INFO 2020-11-08 17:50:10.000000000 +0100 +++ new/amqp-5.0.5/amqp.egg-info/PKG-INFO 2021-01-28 11:15:26.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 1.2 Name: amqp -Version: 5.0.2 +Version: 5.0.5 Summary: Low-level AMQP client for Python (fork of amqplib). Home-page: http://github.com/celery/py-amqp Author: Barry Pederson diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/amqp-5.0.2/docs/includes/introduction.txt new/amqp-5.0.5/docs/includes/introduction.txt --- old/amqp-5.0.2/docs/includes/introduction.txt 2020-11-08 17:49:45.000000000 +0100 +++ new/amqp-5.0.5/docs/includes/introduction.txt 2021-01-28 11:08:49.000000000 +0100 @@ -1,4 +1,4 @@ -:Version: 5.0.2 +:Version: 5.0.5 :Web: https://amqp.readthedocs.io/ :Download: https://pypi.org/project/amqp/ :Source: http://github.com/celery/py-amqp/ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/amqp-5.0.2/docs/reference/amqp.transport.rst new/amqp-5.0.5/docs/reference/amqp.transport.rst --- old/amqp-5.0.2/docs/reference/amqp.transport.rst 2020-01-22 16:47:01.000000000 +0100 +++ new/amqp-5.0.5/docs/reference/amqp.transport.rst 2020-12-13 06:35:15.000000000 +0100 @@ -7,5 +7,20 @@ .. currentmodule:: amqp.transport .. automodule:: amqp.transport + +.. autoclass:: _AbstractTransport + :members: + :undoc-members: + +.. autoclass:: SSLTransport + :members: + :private-members: _wrap_context, _wrap_socket_sni + :undoc-members: + +.. autoclass:: TCPTransport + :members: + :undoc-members: + +.. autoclass:: Transport :members: :undoc-members: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/amqp-5.0.2/docs/reference/index.rst new/amqp-5.0.5/docs/reference/index.rst --- old/amqp-5.0.2/docs/reference/index.rst 2020-01-22 16:47:01.000000000 +0100 +++ new/amqp-5.0.5/docs/reference/index.rst 2020-12-13 06:35:15.000000000 +0100 @@ -23,4 +23,3 @@ amqp.serialization amqp.spec amqp.utils - amqp.five diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/amqp-5.0.2/t/integration/test_rmq.py new/amqp-5.0.5/t/integration/test_rmq.py --- old/amqp-5.0.2/t/integration/test_rmq.py 2020-09-21 10:47:19.000000000 +0200 +++ new/amqp-5.0.5/t/integration/test_rmq.py 2021-01-28 09:19:05.000000000 +0100 @@ -5,15 +5,19 @@ import pytest import amqp +from amqp import transport def get_connection( - hostname, port, vhost, use_tls=False, keyfile=None, certfile=None): + hostname, port, vhost, use_tls=False, + keyfile=None, certfile=None, ca_certs=None +): host = f'{hostname}:{port}' if use_tls: return amqp.Connection(host=host, vhost=vhost, ssl={ 'keyfile': keyfile, - 'certfile': certfile + 'certfile': certfile, + 'ca_certs': ca_certs, } ) else: @@ -40,7 +44,8 @@ ).get("slaveid", None), use_tls=True, keyfile='t/certs/client_key.pem', - certfile='t/certs/client_certificate.pem' + certfile='t/certs/client_certificate.pem', + ca_certs='t/certs/ca_certificate.pem', ) @@ -67,6 +72,32 @@ ) with pytest.raises(ssl.SSLError): connection.connect() + + +@pytest.mark.env('rabbitmq') +@pytest.mark.flaky(reruns=5, reruns_delay=2) +def test_tls_default_certs(): + # testing TLS connection against badssl.com with default certs + connection = transport.Transport( + host="tls-v1-2.badssl.com:1012", + ssl=True, + ) + assert type(connection) == transport.SSLTransport + connection.connect() + + +@pytest.mark.env('rabbitmq') +@pytest.mark.flaky(reruns=5, reruns_delay=2) +def test_tls_no_default_certs_fails(): + # testing TLS connection fails against badssl.com without default certs + connection = transport.Transport( + host="tls-v1-2.badssl.com:1012", + ssl={ + "ca_certs": 't/certs/ca_certificate.pem', + }, + ) + with pytest.raises(ssl.SSLError): + connection.connect() @pytest.mark.env('rabbitmq') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/amqp-5.0.2/t/unit/test_transport.py new/amqp-5.0.5/t/unit/test_transport.py --- old/amqp-5.0.2/t/unit/test_transport.py 2020-11-08 17:42:04.000000000 +0100 +++ new/amqp-5.0.5/t/unit/test_transport.py 2021-01-28 09:19:05.000000000 +0100 @@ -1,6 +1,7 @@ import errno import os import re +import ssl import socket import struct from struct import pack @@ -639,135 +640,144 @@ def test_wrap_socket_sni(self): # testing default values of _wrap_socket_sni() - sock = Mock() - with patch( - 'ssl.SSLContext.wrap_socket', - return_value=sentinel.WRAPPED_SOCKET) as mock_ssl_wrap: + with patch('ssl.SSLContext') as mock_ssl_context_class: + sock = Mock() + context = mock_ssl_context_class() + context.wrap_socket.return_value = sentinel.WRAPPED_SOCKET ret = self.t._wrap_socket_sni(sock) - mock_ssl_wrap.assert_called_with(sock=sock, - server_side=False, - do_handshake_on_connect=False, - suppress_ragged_eofs=True, - server_hostname=None) + context.load_cert_chain.assert_not_called() + context.load_verify_locations.assert_not_called() + context.set_ciphers.assert_not_called() + context.verify_mode.assert_not_called() - assert ret == sentinel.WRAPPED_SOCKET + context.load_default_certs.assert_called_with( + ssl.Purpose.SERVER_AUTH + ) + context.wrap_socket.assert_called_with( + sock=sock, + server_side=False, + do_handshake_on_connect=False, + suppress_ragged_eofs=True, + server_hostname=None + ) + assert ret == sentinel.WRAPPED_SOCKET def test_wrap_socket_sni_certfile(self): # testing _wrap_socket_sni() with parameters certfile and keyfile - sock = Mock() - with patch( - 'ssl.SSLContext.wrap_socket', - return_value=sentinel.WRAPPED_SOCKET) as mock_ssl_wrap, \ - patch('ssl.SSLContext.load_cert_chain') as mock_load_cert_chain: - ret = self.t._wrap_socket_sni( - sock, keyfile=sentinel.KEYFILE, certfile=sentinel.CERTFILE) - - mock_load_cert_chain.assert_called_with( - sentinel.CERTFILE, sentinel.KEYFILE) - mock_ssl_wrap.assert_called_with(sock=sock, - server_side=False, - do_handshake_on_connect=False, - suppress_ragged_eofs=True, - server_hostname=None) + with patch('ssl.SSLContext') as mock_ssl_context_class: + sock = Mock() + context = mock_ssl_context_class() + self.t._wrap_socket_sni( + sock, keyfile=sentinel.KEYFILE, certfile=sentinel.CERTFILE + ) - assert ret == sentinel.WRAPPED_SOCKET + context.load_default_certs.assert_called_with( + ssl.Purpose.SERVER_AUTH + ) + context.load_cert_chain.assert_called_with( + sentinel.CERTFILE, sentinel.KEYFILE + ) def test_wrap_socket_ca_certs(self): # testing _wrap_socket_sni() with parameter ca_certs - sock = Mock() - with patch( - 'ssl.SSLContext.wrap_socket', - return_value=sentinel.WRAPPED_SOCKET - ) as mock_ssl_wrap, patch( - 'ssl.SSLContext.load_verify_locations' - ) as mock_load_verify_locations: - ret = self.t._wrap_socket_sni(sock, ca_certs=sentinel.CA_CERTS) - - mock_load_verify_locations.assert_called_with(sentinel.CA_CERTS) - mock_ssl_wrap.assert_called_with(sock=sock, - server_side=False, - do_handshake_on_connect=False, - suppress_ragged_eofs=True, - server_hostname=None) + with patch('ssl.SSLContext') as mock_ssl_context_class: + sock = Mock() + context = mock_ssl_context_class() + self.t._wrap_socket_sni(sock, ca_certs=sentinel.CA_CERTS) - assert ret == sentinel.WRAPPED_SOCKET + context.load_default_certs.assert_not_called() + context.load_verify_locations.assert_called_with(sentinel.CA_CERTS) def test_wrap_socket_ciphers(self): # testing _wrap_socket_sni() with parameter ciphers - sock = Mock() - with patch( - 'ssl.SSLContext.wrap_socket', - return_value=sentinel.WRAPPED_SOCKET) as mock_ssl_wrap, \ - patch('ssl.SSLContext.set_ciphers') as mock_set_ciphers: - ret = self.t._wrap_socket_sni(sock, ciphers=sentinel.CIPHERS) - - mock_set_ciphers.assert_called_with(sentinel.CIPHERS) - mock_ssl_wrap.assert_called_with(sock=sock, - server_side=False, - do_handshake_on_connect=False, - suppress_ragged_eofs=True, - server_hostname=None) - assert ret == sentinel.WRAPPED_SOCKET + with patch('ssl.SSLContext') as mock_ssl_context_class: + sock = Mock() + context = mock_ssl_context_class() + set_ciphers_method_mock = context.set_ciphers + self.t._wrap_socket_sni(sock, ciphers=sentinel.CIPHERS) + + set_ciphers_method_mock.assert_called_with(sentinel.CIPHERS) def test_wrap_socket_sni_cert_reqs(self): - # testing _wrap_socket_sni() with parameter cert_reqs - sock = Mock() + # testing _wrap_socket_sni() with parameter cert_reqs == ssl.CERT_NONE with patch('ssl.SSLContext') as mock_ssl_context_class: - wrap_socket_method_mock = mock_ssl_context_class().wrap_socket - wrap_socket_method_mock.return_value = sentinel.WRAPPED_SOCKET - ret = self.t._wrap_socket_sni(sock, cert_reqs=sentinel.CERT_REQS) - - wrap_socket_method_mock.assert_called_with( - sock=sock, - server_side=False, - do_handshake_on_connect=False, - suppress_ragged_eofs=True, - server_hostname=None - ) - assert mock_ssl_context_class().check_hostname is True - assert ret == sentinel.WRAPPED_SOCKET + sock = Mock() + context = mock_ssl_context_class() + self.t._wrap_socket_sni(sock, cert_reqs=ssl.CERT_NONE) + + context.load_default_certs.assert_not_called() + assert context.verify_mode == ssl.CERT_NONE + + # testing _wrap_socket_sni() with parameter cert_reqs != ssl.CERT_NONE + with patch('ssl.SSLContext') as mock_ssl_context_class: + sock = Mock() + context = mock_ssl_context_class() + self.t._wrap_socket_sni(sock, cert_reqs=sentinel.CERT_REQS) + + context.load_default_certs.assert_called_with( + ssl.Purpose.SERVER_AUTH + ) + assert context.verify_mode == sentinel.CERT_REQS def test_wrap_socket_sni_setting_sni_header(self): - # testing _wrap_socket_sni() with setting SNI header - sock = Mock() + # testing _wrap_socket_sni() without parameter server_hostname + + # SSL module supports SNI + with patch('ssl.SSLContext') as mock_ssl_context_class, \ + patch('ssl.HAS_SNI', new=True): + sock = Mock() + context = mock_ssl_context_class() + self.t._wrap_socket_sni(sock) + + assert context.check_hostname is False + + # SSL module does not support SNI + with patch('ssl.SSLContext') as mock_ssl_context_class, \ + patch('ssl.HAS_SNI', new=False): + sock = Mock() + context = mock_ssl_context_class() + self.t._wrap_socket_sni(sock) + + assert context.check_hostname is False + + # testing _wrap_socket_sni() with parameter server_hostname + + # SSL module supports SNI with patch('ssl.SSLContext') as mock_ssl_context_class, \ patch('ssl.HAS_SNI', new=True): - # SSL module supports SNI - wrap_socket_method_mock = mock_ssl_context_class().wrap_socket - wrap_socket_method_mock.return_value = sentinel.WRAPPED_SOCKET - ret = self.t._wrap_socket_sni( - sock, cert_reqs=sentinel.CERT_REQS, + sock = Mock() + context = mock_ssl_context_class() + self.t._wrap_socket_sni( + sock, server_hostname=sentinel.SERVER_HOSTNAME + ) + + context.wrap_socket.assert_called_with( + sock=sock, + server_side=False, + do_handshake_on_connect=False, + suppress_ragged_eofs=True, server_hostname=sentinel.SERVER_HOSTNAME ) - wrap_socket_method_mock.assert_called_with( - sock=sock, - server_side=False, - do_handshake_on_connect=False, - suppress_ragged_eofs=True, - server_hostname=sentinel.SERVER_HOSTNAME - ) - assert mock_ssl_context_class().verify_mode == sentinel.CERT_REQS - assert ret == sentinel.WRAPPED_SOCKET + assert context.check_hostname is True + # SSL module does not support SNI with patch('ssl.SSLContext') as mock_ssl_context_class, \ patch('ssl.HAS_SNI', new=False): - # SSL module does not support SNI - wrap_socket_method_mock = mock_ssl_context_class().wrap_socket - wrap_socket_method_mock.return_value = sentinel.WRAPPED_SOCKET - ret = self.t._wrap_socket_sni( - sock, cert_reqs=sentinel.CERT_REQS, + sock = Mock() + context = mock_ssl_context_class() + self.t._wrap_socket_sni( + sock, server_hostname=sentinel.SERVER_HOSTNAME + ) + + context.wrap_socket.assert_called_with( + sock=sock, + server_side=False, + do_handshake_on_connect=False, + suppress_ragged_eofs=True, server_hostname=sentinel.SERVER_HOSTNAME ) - wrap_socket_method_mock.assert_called_with( - sock=sock, - server_side=False, - do_handshake_on_connect=False, - suppress_ragged_eofs=True, - server_hostname=sentinel.SERVER_HOSTNAME - ) - assert mock_ssl_context_class().verify_mode != sentinel.CERT_REQS - assert ret == sentinel.WRAPPED_SOCKET + assert context.check_hostname is False def test_shutdown_transport(self): self.t.sock = None