Hello community, here is the log from the commit of package python-acme for openSUSE:Factory checked in at 2018-11-18 23:33:16 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-acme (Old) and /work/SRC/openSUSE:Factory/.python-acme.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-acme" Sun Nov 18 23:33:16 2018 rev:24 rq:649947 version:0.28.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-acme/python-acme.changes 2018-09-15 15:41:13.508789743 +0200 +++ /work/SRC/openSUSE:Factory/.python-acme.new/python-acme.changes 2018-11-18 23:33:18.069401286 +0100 @@ -1,0 +2,8 @@ +Fri Nov 16 16:48:27 UTC 2018 - Marketa Calabkova <mcalabk...@suse.com> + +- update to version 0.28.0 + * Use the ACMEv2 newNonce endpoint when a new nonce is needed, and + newNonce is available in the directory. + * Warn when using deprecated acme.challenges.TLSSNI01 + +------------------------------------------------------------------- Old: ---- acme-0.27.1.tar.gz acme-0.27.1.tar.gz.asc New: ---- acme-0.28.0.tar.gz acme-0.28.0.tar.gz.asc ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-acme.spec ++++++ --- /var/tmp/diff_new_pack.WUQEi7/_old 2018-11-18 23:33:20.077398876 +0100 +++ /var/tmp/diff_new_pack.WUQEi7/_new 2018-11-18 23:33:20.077398876 +0100 @@ -12,14 +12,14 @@ # license that conforms to the Open Source Definition (Version 1.9) # published by the Open Source Initiative. -# Please submit bugfixes or comments via http://bugs.opensuse.org/ +# Please submit bugfixes or comments via https://bugs.opensuse.org/ # %{?!python_module:%define python_module() python-%{**} python3-%{**}} %define libname acme Name: python-%{libname} -Version: 0.27.1 +Version: 0.28.0 Release: 0 Summary: Python library for the ACME protocol License: Apache-2.0 @@ -34,12 +34,14 @@ BuildRequires: %{python_module mock} BuildRequires: %{python_module pyOpenSSL >= 0.13} BuildRequires: %{python_module pyRFC3339} +BuildRequires: %{python_module pytest-xdist} BuildRequires: %{python_module pytest} BuildRequires: %{python_module pytz} BuildRequires: %{python_module requests >= 2.4.1} BuildRequires: %{python_module requests-toolbelt >= 0.3.0} BuildRequires: %{python_module setuptools} BuildRequires: %{python_module six >= 1.9.0} +BuildRequires: %{python_module tox} BuildRequires: fdupes BuildRequires: python-rpm-macros Requires: python-cryptography >= 0.8 ++++++ acme-0.27.1.tar.gz -> acme-0.28.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/acme-0.27.1/PKG-INFO new/acme-0.28.0/PKG-INFO --- old/acme-0.27.1/PKG-INFO 2018-09-07 01:13:37.000000000 +0200 +++ new/acme-0.28.0/PKG-INFO 2018-11-07 22:15:05.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: acme -Version: 0.27.1 +Version: 0.28.0 Summary: ACME protocol implementation in Python Home-page: https://github.com/letsencrypt/letsencrypt Author: Certbot Project diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/acme-0.27.1/acme/challenges.py new/acme-0.28.0/acme/challenges.py --- old/acme-0.27.1/acme/challenges.py 2018-09-07 01:13:29.000000000 +0200 +++ new/acme-0.28.0/acme/challenges.py 2018-11-07 22:14:56.000000000 +0100 @@ -4,6 +4,7 @@ import hashlib import logging import socket +import warnings from cryptography.hazmat.primitives import hashes # type: ignore import josepy as jose @@ -493,6 +494,11 @@ # boulder#962, ietf-wg-acme#22 #n = jose.Field("n", encoder=int, decoder=int) + def __init__(self, *args, **kwargs): + warnings.warn("TLS-SNI-01 is deprecated, and will stop working soon.", + DeprecationWarning, stacklevel=2) + super(TLSSNI01, self).__init__(*args, **kwargs) + def validation(self, account_key, **kwargs): """Generate validation. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/acme-0.27.1/acme/challenges_test.py new/acme-0.28.0/acme/challenges_test.py --- old/acme-0.27.1/acme/challenges_test.py 2018-09-07 01:13:29.000000000 +0200 +++ new/acme-0.28.0/acme/challenges_test.py 2018-11-07 22:14:56.000000000 +0100 @@ -1,5 +1,6 @@ """Tests for acme.challenges.""" import unittest +import warnings import josepy as jose import mock @@ -360,20 +361,29 @@ class TLSSNI01Test(unittest.TestCase): def setUp(self): - from acme.challenges import TLSSNI01 - self.msg = TLSSNI01( - token=jose.b64decode('a82d5ff8ef740d12881f6d3c2277ab2e')) self.jmsg = { 'type': 'tls-sni-01', 'token': 'a82d5ff8ef740d12881f6d3c2277ab2e', } + def _msg(self): + from acme.challenges import TLSSNI01 + with warnings.catch_warnings(record=True) as warn: + warnings.simplefilter("always") + msg = TLSSNI01( + token=jose.b64decode('a82d5ff8ef740d12881f6d3c2277ab2e')) + assert warn is not None # using a raw assert for mypy + self.assertTrue(len(warn) == 1) + self.assertTrue(issubclass(warn[-1].category, DeprecationWarning)) + self.assertTrue('deprecated' in str(warn[-1].message)) + return msg + def test_to_partial_json(self): - self.assertEqual(self.jmsg, self.msg.to_partial_json()) + self.assertEqual(self.jmsg, self._msg().to_partial_json()) def test_from_json(self): from acme.challenges import TLSSNI01 - self.assertEqual(self.msg, TLSSNI01.from_json(self.jmsg)) + self.assertEqual(self._msg(), TLSSNI01.from_json(self.jmsg)) def test_from_json_hashable(self): from acme.challenges import TLSSNI01 @@ -388,7 +398,7 @@ @mock.patch('acme.challenges.TLSSNI01Response.gen_cert') def test_validation(self, mock_gen_cert): mock_gen_cert.return_value = ('cert', 'key') - self.assertEqual(('cert', 'key'), self.msg.validation( + self.assertEqual(('cert', 'key'), self._msg().validation( KEY, cert_key=mock.sentinel.cert_key)) mock_gen_cert.assert_called_once_with(key=mock.sentinel.cert_key) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/acme-0.27.1/acme/client.py new/acme-0.28.0/acme/client.py --- old/acme-0.27.1/acme/client.py 2018-09-07 01:13:29.000000000 +0200 +++ new/acme-0.28.0/acme/client.py 2018-11-07 22:14:56.000000000 +0100 @@ -89,6 +89,8 @@ """ kwargs.setdefault('acme_version', self.acme_version) + if hasattr(self.directory, 'newNonce'): + kwargs.setdefault('new_nonce_url', getattr(self.directory, 'newNonce')) return self.net.post(*args, **kwargs) def update_registration(self, regr, update=None): @@ -1106,10 +1108,15 @@ else: raise errors.MissingNonce(response) - def _get_nonce(self, url): + def _get_nonce(self, url, new_nonce_url): if not self._nonces: logger.debug('Requesting fresh nonce') - self._add_nonce(self.head(url)) + if new_nonce_url is None: + response = self.head(url) + else: + # request a new nonce from the acme newNonce endpoint + response = self._check_response(self.head(new_nonce_url), content_type=None) + self._add_nonce(response) return self._nonces.pop() def post(self, *args, **kwargs): @@ -1130,8 +1137,13 @@ def _post_once(self, url, obj, content_type=JOSE_CONTENT_TYPE, acme_version=1, **kwargs): - data = self._wrap_in_jws(obj, self._get_nonce(url), url, acme_version) + try: + new_nonce_url = kwargs.pop('new_nonce_url') + except KeyError: + new_nonce_url = None + data = self._wrap_in_jws(obj, self._get_nonce(url, new_nonce_url), url, acme_version) kwargs.setdefault('headers', {'Content-Type': content_type}) response = self._send_request('POST', url, data=data, **kwargs) + response = self._check_response(response, content_type=content_type) self._add_nonce(response) - return self._check_response(response, content_type=content_type) + return response diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/acme-0.27.1/acme/client_test.py new/acme-0.28.0/acme/client_test.py --- old/acme-0.27.1/acme/client_test.py 2018-09-07 01:13:29.000000000 +0200 +++ new/acme-0.28.0/acme/client_test.py 2018-11-07 22:14:56.000000000 +0100 @@ -805,7 +805,8 @@ def test_revoke(self): self.client.revoke(messages_test.CERT, self.rsn) self.net.post.assert_called_once_with( - self.directory["revokeCert"], mock.ANY, acme_version=2) + self.directory["revokeCert"], mock.ANY, acme_version=2, + new_nonce_url=DIRECTORY_V2['newNonce']) def test_update_registration(self): # "Instance of 'Field' has no to_json/update member" bug: @@ -1038,8 +1039,8 @@ # Requests Library Exceptions except requests.exceptions.ConnectionError as z: #pragma: no cover - self.assertEqual("('Connection aborted.', " - "error(111, 'Connection refused'))", str(z)) + self.assertTrue("('Connection aborted.', error(111, 'Connection refused'))" + == str(z) or "[WinError 10061]" in str(z)) class ClientNetworkWithMockedResponseTest(unittest.TestCase): """Tests for acme.client.ClientNetwork which mock out response.""" @@ -1052,7 +1053,10 @@ self.response = mock.MagicMock(ok=True, status_code=http_client.OK) self.response.headers = {} self.response.links = {} - self.checked_response = mock.MagicMock() + self.response.checked = False + self.acmev1_nonce_response = mock.MagicMock(ok=False, + status_code=http_client.METHOD_NOT_ALLOWED) + self.acmev1_nonce_response.headers = {} self.obj = mock.MagicMock() self.wrapped_obj = mock.MagicMock() self.content_type = mock.sentinel.content_type @@ -1064,13 +1068,21 @@ def send_request(*args, **kwargs): # pylint: disable=unused-argument,missing-docstring + self.assertFalse("new_nonce_url" in kwargs) + method = args[0] + uri = args[1] + if method == 'HEAD' and uri != "new_nonce_uri": + response = self.acmev1_nonce_response + else: + response = self.response + if self.available_nonces: - self.response.headers = { + response.headers = { self.net.REPLAY_NONCE_HEADER: self.available_nonces.pop().decode()} else: - self.response.headers = {} - return self.response + response.headers = {} + return response # pylint: disable=protected-access self.net._send_request = self.send_request = mock.MagicMock( @@ -1082,28 +1094,39 @@ # pylint: disable=missing-docstring self.assertEqual(self.response, response) self.assertEqual(self.content_type, content_type) - return self.checked_response + self.assertTrue(self.response.ok) + self.response.checked = True + return self.response def test_head(self): - self.assertEqual(self.response, self.net.head( + self.assertEqual(self.acmev1_nonce_response, self.net.head( 'http://example.com/', 'foo', bar='baz')) self.send_request.assert_called_once_with( 'HEAD', 'http://example.com/', 'foo', bar='baz') + def test_head_v2(self): + self.assertEqual(self.response, self.net.head( + 'new_nonce_uri', 'foo', bar='baz')) + self.send_request.assert_called_once_with( + 'HEAD', 'new_nonce_uri', 'foo', bar='baz') + def test_get(self): - self.assertEqual(self.checked_response, self.net.get( + self.assertEqual(self.response, self.net.get( 'http://example.com/', content_type=self.content_type, bar='baz')) + self.assertTrue(self.response.checked) self.send_request.assert_called_once_with( 'GET', 'http://example.com/', bar='baz') def test_post_no_content_type(self): self.content_type = self.net.JOSE_CONTENT_TYPE - self.assertEqual(self.checked_response, self.net.post('uri', self.obj)) + self.assertEqual(self.response, self.net.post('uri', self.obj)) + self.assertTrue(self.response.checked) def test_post(self): # pylint: disable=protected-access - self.assertEqual(self.checked_response, self.net.post( + self.assertEqual(self.response, self.net.post( 'uri', self.obj, content_type=self.content_type)) + self.assertTrue(self.response.checked) self.net._wrap_in_jws.assert_called_once_with( self.obj, jose.b64decode(self.all_nonces.pop()), "uri", 1) @@ -1135,7 +1158,7 @@ def test_post_not_retried(self): check_response = mock.MagicMock() check_response.side_effect = [messages.Error.with_code('malformed'), - self.checked_response] + self.response] # pylint: disable=protected-access self.net._check_response = check_response @@ -1143,13 +1166,12 @@ self.obj, content_type=self.content_type) def test_post_successful_retry(self): - check_response = mock.MagicMock() - check_response.side_effect = [messages.Error.with_code('badNonce'), - self.checked_response] + post_once = mock.MagicMock() + post_once.side_effect = [messages.Error.with_code('badNonce'), + self.response] # pylint: disable=protected-access - self.net._check_response = check_response - self.assertEqual(self.checked_response, self.net.post( + self.assertEqual(self.response, self.net.post( 'uri', self.obj, content_type=self.content_type)) def test_head_get_post_error_passthrough(self): @@ -1160,6 +1182,26 @@ self.assertRaises(requests.exceptions.RequestException, self.net.post, 'uri', obj=self.obj) + def test_post_bad_nonce_head(self): + # pylint: disable=protected-access + # regression test for https://github.com/certbot/certbot/issues/6092 + bad_response = mock.MagicMock(ok=False, status_code=http_client.SERVICE_UNAVAILABLE) + self.net._send_request = mock.MagicMock() + self.net._send_request.return_value = bad_response + self.content_type = None + check_response = mock.MagicMock() + self.net._check_response = check_response + self.assertRaises(errors.ClientError, self.net.post, 'uri', + self.obj, content_type=self.content_type, acme_version=2, + new_nonce_url='new_nonce_uri') + self.assertEqual(check_response.call_count, 1) + + def test_new_nonce_uri_removed(self): + self.content_type = None + self.net.post('uri', self.obj, content_type=None, + acme_version=2, new_nonce_url='new_nonce_uri') + + class ClientNetworkSourceAddressBindingTest(unittest.TestCase): """Tests that if ClientNetwork has a source IP set manually, the underlying library has used the provided source address.""" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/acme-0.27.1/acme/crypto_util.py new/acme-0.28.0/acme/crypto_util.py --- old/acme-0.27.1/acme/crypto_util.py 2018-09-07 01:13:29.000000000 +0200 +++ new/acme-0.28.0/acme/crypto_util.py 2018-11-07 22:14:56.000000000 +0100 @@ -136,22 +136,16 @@ socket_kwargs = {'source_address': source_address} - host_protocol_agnostic = host - if host == '::' or host == '0': - # https://github.com/python/typeshed/pull/2136 - # while PR is not merged, we need to ignore - host_protocol_agnostic = None - try: # pylint: disable=star-args logger.debug( - "Attempting to connect to %s:%d%s.", host_protocol_agnostic, port, + "Attempting to connect to %s:%d%s.", host, port, " from {0}:{1}".format( source_address[0], source_address[1] ) if socket_kwargs else "" ) - socket_tuple = (host_protocol_agnostic, port) # type: Tuple[Optional[str], int] + socket_tuple = (host, port) # type: Tuple[str, int] sock = socket.create_connection(socket_tuple, **socket_kwargs) # type: ignore except socket.error as error: raise errors.Error(error) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/acme-0.27.1/acme/messages.py new/acme-0.28.0/acme/messages.py --- old/acme-0.27.1/acme/messages.py 2018-09-07 01:13:29.000000000 +0200 +++ new/acme-0.28.0/acme/messages.py 2018-11-07 22:14:56.000000000 +0100 @@ -523,7 +523,7 @@ """ identifiers = jose.Field('identifiers', omitempty=True) status = jose.Field('status', decoder=Status.from_json, - omitempty=True, default=STATUS_PENDING) + omitempty=True) authorizations = jose.Field('authorizations', omitempty=True) certificate = jose.Field('certificate', omitempty=True) finalize = jose.Field('finalize', omitempty=True) @@ -553,4 +553,3 @@ class NewOrder(Order): """New order.""" resource_type = 'new-order' - resource = fields.Resource(resource_type) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/acme-0.27.1/acme/messages_test.py new/acme-0.28.0/acme/messages_test.py --- old/acme-0.27.1/acme/messages_test.py 2018-09-07 01:13:29.000000000 +0200 +++ new/acme-0.28.0/acme/messages_test.py 2018-11-07 22:14:56.000000000 +0100 @@ -424,6 +424,19 @@ 'authorizations': None, }) +class NewOrderTest(unittest.TestCase): + """Tests for acme.messages.NewOrder.""" + + def setUp(self): + from acme.messages import NewOrder + self.reg = NewOrder( + identifiers=mock.sentinel.identifiers) + + def test_to_partial_json(self): + self.assertEqual(self.reg.to_json(), { + 'identifiers': mock.sentinel.identifiers, + }) + if __name__ == '__main__': unittest.main() # pragma: no cover diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/acme-0.27.1/acme/standalone_test.py new/acme-0.28.0/acme/standalone_test.py --- old/acme-0.27.1/acme/standalone_test.py 2018-09-07 01:13:29.000000000 +0200 +++ new/acme-0.28.0/acme/standalone_test.py 2018-11-07 22:14:56.000000000 +0100 @@ -48,7 +48,7 @@ test_util.load_cert('rsa2048_cert.pem'), )} from acme.standalone import TLSSNI01Server - self.server = TLSSNI01Server(("", 0), certs=self.certs) + self.server = TLSSNI01Server(('localhost', 0), certs=self.certs) # pylint: disable=no-member self.thread = threading.Thread(target=self.server.serve_forever) self.thread.start() @@ -133,8 +133,11 @@ self.address_family = socket.AF_INET socketserver.TCPServer.__init__(self, *args, **kwargs) if ipv6: + # NB: On Windows, socket.IPPROTO_IPV6 constant may be missing. + # We use the corresponding value (41) instead. + level = getattr(socket, "IPPROTO_IPV6", 41) # pylint: disable=no-member - self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 1) + self.socket.setsockopt(level, socket.IPV6_V6ONLY, 1) try: self.server_bind() self.server_activate() @@ -147,15 +150,15 @@ mock_bind.side_effect = socket.error from acme.standalone import BaseDualNetworkedServers self.assertRaises(socket.error, BaseDualNetworkedServers, - BaseDualNetworkedServersTest.SingleProtocolServer, - ("", 0), - socketserver.BaseRequestHandler) + BaseDualNetworkedServersTest.SingleProtocolServer, + ('', 0), + socketserver.BaseRequestHandler) def test_ports_equal(self): from acme.standalone import BaseDualNetworkedServers servers = BaseDualNetworkedServers( BaseDualNetworkedServersTest.SingleProtocolServer, - ("", 0), + ('', 0), socketserver.BaseRequestHandler) socknames = servers.getsocknames() prev_port = None @@ -177,7 +180,7 @@ test_util.load_cert('rsa2048_cert.pem'), )} from acme.standalone import TLSSNI01DualNetworkedServers - self.servers = TLSSNI01DualNetworkedServers(("", 0), certs=self.certs) + self.servers = TLSSNI01DualNetworkedServers(('localhost', 0), certs=self.certs) self.servers.serve_forever() def tearDown(self): @@ -245,6 +248,7 @@ self.assertFalse(self._test_http01(add=False)) +@test_util.broken_on_windows class TestSimpleTLSSNI01Server(unittest.TestCase): """Tests for acme.standalone.simple_tls_sni_01_server.""" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/acme-0.27.1/acme/test_util.py new/acme-0.28.0/acme/test_util.py --- old/acme-0.27.1/acme/test_util.py 2018-09-07 01:13:29.000000000 +0200 +++ new/acme-0.28.0/acme/test_util.py 2018-11-07 22:14:56.000000000 +0100 @@ -4,6 +4,7 @@ """ import os +import sys import pkg_resources import unittest @@ -94,3 +95,11 @@ return lambda cls: cls else: return lambda cls: None + +def broken_on_windows(function): + """Decorator to skip temporarily a broken test on Windows.""" + reason = 'Test is broken and ignored on windows but should be fixed.' + return unittest.skipIf( + sys.platform == 'win32' + and os.environ.get('SKIP_BROKEN_TESTS_ON_WINDOWS', 'true') == 'true', + reason)(function) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/acme-0.27.1/acme.egg-info/PKG-INFO new/acme-0.28.0/acme.egg-info/PKG-INFO --- old/acme-0.27.1/acme.egg-info/PKG-INFO 2018-09-07 01:13:37.000000000 +0200 +++ new/acme-0.28.0/acme.egg-info/PKG-INFO 2018-11-07 22:15:05.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: acme -Version: 0.27.1 +Version: 0.28.0 Summary: ACME protocol implementation in Python Home-page: https://github.com/letsencrypt/letsencrypt Author: Certbot Project diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/acme-0.27.1/setup.py new/acme-0.28.0/setup.py --- old/acme-0.27.1/setup.py 2018-09-07 01:13:30.000000000 +0200 +++ new/acme-0.28.0/setup.py 2018-11-07 22:14:57.000000000 +0100 @@ -3,7 +3,7 @@ from setuptools.command.test import test as TestCommand import sys -version = '0.27.1' +version = '0.28.0' # Please update tox.ini when modifying dependency version requirements install_requires = [