Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-certbot for openSUSE:Factory checked in at 2022-07-11 19:10:19 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-certbot (Old) and /work/SRC/openSUSE:Factory/.python-certbot.new.1523 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-certbot" Mon Jul 11 19:10:19 2022 rev:43 rq:988433 version:1.29.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-certbot/python-certbot.changes 2022-06-25 10:25:03.274741630 +0200 +++ /work/SRC/openSUSE:Factory/.python-certbot.new.1523/python-certbot.changes 2022-07-11 19:11:29.887791619 +0200 @@ -1,0 +2,15 @@ +Mon Jul 11 13:07:42 UTC 2022 - Dirk M??ller <[email protected]> + +- update to 1.29.0: + * --allow-subset-of-names will now additionally retry in cases where domains + are rejected while creating or finalizing orders. This requires subproblem + support from the ACME server + * The show_account subcommand now uses the "newAccount" ACME endpoint to + fetch the account data, so it doesn't rely on the locally stored account URL. + This fixes situations where Certbot + would use old ACMEv1 registration info with non-functional account URLs. + * The generated Certificate Signing Requests are now generated as version 1 + instead of version 3. This resolves situations in where strict enforcement + of PKCS#10 meant that CSRs that were generated as version 3 were rejected + +------------------------------------------------------------------- Old: ---- certbot-1.28.0.tar.gz certbot-1.28.0.tar.gz.asc New: ---- certbot-1.29.0.tar.gz certbot-1.29.0.tar.gz.asc ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-certbot.spec ++++++ --- /var/tmp/diff_new_pack.OVU7tl/_old 2022-07-11 19:11:30.219792101 +0200 +++ /var/tmp/diff_new_pack.OVU7tl/_new 2022-07-11 19:11:30.223792106 +0200 @@ -19,7 +19,7 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} %define skip_python2 1 Name: python-certbot -Version: 1.28.0 +Version: 1.29.0 Release: 0 Summary: ACME client License: Apache-2.0 ++++++ certbot-1.28.0.tar.gz -> certbot-1.29.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/certbot-1.28.0/CHANGELOG.md new/certbot-1.29.0/CHANGELOG.md --- old/certbot-1.28.0/CHANGELOG.md 2022-06-07 21:41:07.000000000 +0200 +++ new/certbot-1.29.0/CHANGELOG.md 2022-07-05 20:15:47.000000000 +0200 @@ -2,12 +2,33 @@ Certbot adheres to [Semantic Versioning](https://semver.org/). +## 1.29.0 - 2022-07-05 + +### Added + +* Updated Windows installer to be signed and trusted in Windows + +### Changed + +* `--allow-subset-of-names` will now additionally retry in cases where domains are rejected while creating or finalizing orders. This requires subproblem support from the ACME server. + +### Fixed + +* The `show_account` subcommand now uses the "newAccount" ACME endpoint to fetch the account + data, so it doesn't rely on the locally stored account URL. This fixes situations where Certbot + would use old ACMEv1 registration info with non-functional account URLs. + +* The generated Certificate Signing Requests are now generated as version 1 instead of version 3. This resolves situations in where strict enforcement of PKCS#10 meant that CSRs that were generated as version 3 were rejected. + +More details about these changes can be found on our GitHub repo. + ## 1.28.0 - 2022-06-07 ### Added * Updated Apache/NGINX TLS configs to document contents are based on ssl-config.mozilla.org + ### Changed * A change to order finalization has been made to the `acme` module and Certbot: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/certbot-1.28.0/PKG-INFO new/certbot-1.29.0/PKG-INFO --- old/certbot-1.28.0/PKG-INFO 2022-06-07 21:41:09.812716700 +0200 +++ new/certbot-1.29.0/PKG-INFO 2022-07-05 20:15:48.407816600 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: certbot -Version: 1.28.0 +Version: 1.29.0 Summary: ACME client Home-page: https://github.com/letsencrypt/letsencrypt Author: Certbot Project diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/certbot-1.28.0/certbot/__init__.py new/certbot-1.29.0/certbot/__init__.py --- old/certbot-1.28.0/certbot/__init__.py 2022-06-07 21:41:08.000000000 +0200 +++ new/certbot-1.29.0/certbot/__init__.py 2022-07-05 20:15:48.000000000 +0200 @@ -1,3 +1,3 @@ """Certbot client.""" # version number like 1.2.3a0, must have at least 2 parts, like 1.2 -__version__ = '1.28.0' +__version__ = '1.29.0' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/certbot-1.28.0/certbot/_internal/client.py new/certbot-1.29.0/certbot/_internal/client.py --- old/certbot-1.28.0/certbot/_internal/client.py 2022-06-07 21:41:07.000000000 +0200 +++ new/certbot-1.29.0/certbot/_internal/client.py 2022-07-05 20:15:47.000000000 +0200 @@ -438,7 +438,17 @@ csr = crypto_util.generate_csr(key, domains, self.config.csr_dir, self.config.must_staple, self.config.strict_permissions) - orderr = self._get_order_and_authorizations(csr.data, self.config.allow_subset_of_names) + try: + orderr = self._get_order_and_authorizations(csr.data, self.config.allow_subset_of_names) + except messages.Error as error: + # Some domains may be rejected during order creation. + # Certbot can retry the operation without the rejected + # domains contained within subproblems. + if self.config.allow_subset_of_names: + successful_domains = self._successful_domains_from_error(error, domains) + if successful_domains != domains and len(successful_domains) != 0: + return self._retry_obtain_certificate(key, csr, domains, successful_domains) + raise authzr = orderr.authorizations auth_domains = {a.body.identifier.value for a in authzr} successful_domains = [d for d in domains if d in auth_domains] @@ -449,13 +459,20 @@ # domains contains a wildcard because the ACME spec forbids identifiers # in authzs from containing a wildcard character. if self.config.allow_subset_of_names and successful_domains != domains: - if not self.config.dry_run: - os.remove(key.file) - os.remove(csr.file) - return self.obtain_certificate(successful_domains) + return self._retry_obtain_certificate(key, csr, domains, successful_domains) else: - cert, chain = self.obtain_certificate_from_csr(csr, orderr) - return cert, chain, key, csr + try: + cert, chain = self.obtain_certificate_from_csr(csr, orderr) + return cert, chain, key, csr + except messages.Error as error: + # Some domains may be rejected during the very late stage of + # order finalization. Certbot can retry the operation without + # the rejected domains contained within subproblems. + if self.config.allow_subset_of_names: + successful_domains = self._successful_domains_from_error(error, domains) + if successful_domains != domains and len(successful_domains) != 0: + return self._retry_obtain_certificate(key, csr, domains, successful_domains) + raise def _get_order_and_authorizations(self, csr_pem: bytes, best_effort: bool) -> messages.OrderResource: @@ -528,6 +545,27 @@ key.pem, chain, self.config) + def _successful_domains_from_error(self, error: messages.Error, domains: List[str], + ) -> List[str]: + if error.subproblems is not None: + failed_domains = [problem.identifier.value for problem in error.subproblems + if problem.identifier is not None] + successful_domains = [x for x in domains if x not in failed_domains] + return successful_domains + return [] + + def _retry_obtain_certificate(self, key: util.Key, + csr: util.CSR, domains: List[str], successful_domains: List[str] + ) -> Tuple[bytes, bytes, util.Key, util.CSR]: + failed_domains = [d for d in domains if d not in successful_domains] + domains_list = ", ".join(failed_domains) + display_util.notify("Unable to obtain a certificate with every requested " + f"domain. Retrying without: {domains_list}") + if not self.config.dry_run: + os.remove(key.file) + os.remove(csr.file) + return self.obtain_certificate(successful_domains) + def _choose_lineagename(self, domains: List[str], certname: Optional[str]) -> str: """Chooses a name for the new lineage. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/certbot-1.28.0/certbot.egg-info/PKG-INFO new/certbot-1.29.0/certbot.egg-info/PKG-INFO --- old/certbot-1.28.0/certbot.egg-info/PKG-INFO 2022-06-07 21:41:09.000000000 +0200 +++ new/certbot-1.29.0/certbot.egg-info/PKG-INFO 2022-07-05 20:15:48.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: certbot -Version: 1.28.0 +Version: 1.29.0 Summary: ACME client Home-page: https://github.com/letsencrypt/letsencrypt Author: Certbot Project diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/certbot-1.28.0/certbot.egg-info/SOURCES.txt new/certbot-1.29.0/certbot.egg-info/SOURCES.txt --- old/certbot-1.28.0/certbot.egg-info/SOURCES.txt 2022-06-07 21:41:09.000000000 +0200 +++ new/certbot-1.29.0/certbot.egg-info/SOURCES.txt 2022-07-05 20:15:48.000000000 +0200 @@ -139,7 +139,6 @@ docs/make.bat docs/packaging.rst docs/resources.rst -docs/uninstall.rst docs/using.rst docs/what.rst docs/_static/.gitignore diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/certbot-1.28.0/certbot.egg-info/requires.txt new/certbot-1.29.0/certbot.egg-info/requires.txt --- old/certbot-1.28.0/certbot.egg-info/requires.txt 2022-06-07 21:41:09.000000000 +0200 +++ new/certbot-1.29.0/certbot.egg-info/requires.txt 2022-07-05 20:15:48.000000000 +0200 @@ -1,4 +1,4 @@ -acme>=1.28.0 +acme>=1.29.0 ConfigArgParse>=0.9.3 configobj>=5.0.6 cryptography>=2.5.0 @@ -17,7 +17,6 @@ [all] azure-devops ipdb -PyGithub poetry>=1.2.0a1 twine Sphinx>=1.2 @@ -44,7 +43,6 @@ [dev] azure-devops ipdb -PyGithub poetry>=1.2.0a1 twine diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/certbot-1.28.0/docs/cli-help.txt new/certbot-1.29.0/docs/cli-help.txt --- old/certbot-1.28.0/docs/cli-help.txt 2022-06-07 21:41:07.000000000 +0200 +++ new/certbot-1.29.0/docs/cli-help.txt 2022-07-05 20:15:47.000000000 +0200 @@ -126,7 +126,7 @@ case, and to know when to deprecate support for past Python versions and flags. If you wish to hide this information from the Let's Encrypt server, set this to - "". (default: CertbotACMEClient/1.27.0 (certbot; + "". (default: CertbotACMEClient/1.28.0 (certbot; OS_NAME OS_VERSION) Authenticator/XXX Installer/YYY (SUBCOMMAND; flags: FLAGS) Py/major.minor.patchlevel). The flags encoded in the user agent are: --duplicate, @@ -351,7 +351,7 @@ renew", regardless of if the certificate is renewed. This setting does not apply to important TLS configuration updates. (default: False) - --no-autorenew Disable auto renewal of certificates. (default: True) + --no-autorenew Disable auto renewal of certificates. (default: False) certificates: List certificates managed by Certbot diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/certbot-1.28.0/docs/contributing.rst new/certbot-1.29.0/docs/contributing.rst --- old/certbot-1.28.0/docs/contributing.rst 2022-06-07 21:41:07.000000000 +0200 +++ new/certbot-1.29.0/docs/contributing.rst 2022-07-05 20:15:47.000000000 +0200 @@ -313,7 +313,7 @@ Writing your own plugin ----------------------- -.. note:: The Certbot team is not currently accepting any new DNS plugins +.. note:: The Certbot team is not currently accepting any new plugins because we want to rethink our approach to the challenge and resolve some issues like `#6464 <https://github.com/certbot/certbot/issues/6464>`_, `#6503 <https://github.com/certbot/certbot/issues/6503>`_, and `#6504 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/certbot-1.28.0/docs/install.rst new/certbot-1.29.0/docs/install.rst --- old/certbot-1.28.0/docs/install.rst 2022-06-07 21:41:07.000000000 +0200 +++ new/certbot-1.29.0/docs/install.rst 2022-07-05 20:15:47.000000000 +0200 @@ -125,21 +125,6 @@ .. _Docker: https://docker.com .. _`install Docker`: https://docs.docker.com/engine/installation/ -.. _certbot-auto: - -Certbot-Auto ------------- -.. toctree:: - :hidden: - - uninstall - - -We used to have a shell script named ``certbot-auto`` to help people install -Certbot on UNIX operating systems, however, this script is no longer supported. -If you want to uninstall ``certbot-auto``, you can follow our instructions -:doc:`here <uninstall>`. - Pip --- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/certbot-1.28.0/docs/uninstall.rst new/certbot-1.29.0/docs/uninstall.rst --- old/certbot-1.28.0/docs/uninstall.rst 2022-06-07 21:41:07.000000000 +0200 +++ new/certbot-1.29.0/docs/uninstall.rst 1970-01-01 01:00:00.000000000 +0100 @@ -1,16 +0,0 @@ -========================= -Uninstalling certbot-auto -========================= - -To uninstall ``certbot-auto``, you need to do three things: - -1. If you added a cron job or systemd timer to automatically run - ``certbot-auto`` to renew your certificates, you should delete it. If you - did this by following our instructions, you can delete the entry added to - ``/etc/crontab`` by running a command like ``sudo sed -i '/certbot-auto/d' - /etc/crontab``. -2. Delete the ``certbot-auto`` script. If you placed it in ``/usr/local/bin`` - like we recommended, you can delete it by running ``sudo rm - /usr/local/bin/certbot-auto``. -3. Delete the Certbot installation created by ``certbot-auto`` by running - ``sudo rm -rf /opt/eff.org``. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/certbot-1.28.0/docs/using.rst new/certbot-1.29.0/docs/using.rst --- old/certbot-1.28.0/docs/using.rst 2022-06-07 21:41:07.000000000 +0200 +++ new/certbot-1.29.0/docs/using.rst 2022-07-05 20:15:47.000000000 +0200 @@ -14,7 +14,7 @@ and commonly-used commands will be discussed throughout this document; an exhaustive list also appears near the end of the document. -The ``certbot`` script on your web server might be named ``letsencrypt`` if your system uses an older package, or ``certbot-auto`` if you used a now-deprecated installation method. Throughout the docs, whenever you see ``certbot``, swap in the correct name as needed. +The ``certbot`` script on your web server might be named ``letsencrypt`` if your system uses an older package. Throughout the docs, whenever you see ``certbot``, swap in the correct name as needed. .. _plugins: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/certbot-1.28.0/setup.py new/certbot-1.29.0/setup.py --- old/certbot-1.28.0/setup.py 2022-06-07 21:41:07.000000000 +0200 +++ new/certbot-1.29.0/setup.py 2022-07-05 20:15:47.000000000 +0200 @@ -67,7 +67,6 @@ dev_extras = [ 'azure-devops', 'ipdb', - 'PyGithub', # poetry 1.2.0+ is required for it to pin pip, setuptools, and wheel. See # https://github.com/python-poetry/poetry/issues/1584. 'poetry>=1.2.0a1', diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/certbot-1.28.0/tests/client_test.py new/certbot-1.29.0/tests/client_test.py --- old/certbot-1.28.0/tests/client_test.py 2022-06-07 21:41:07.000000000 +0200 +++ new/certbot-1.29.0/tests/client_test.py 2022-07-05 20:15:47.000000000 +0200 @@ -396,6 +396,193 @@ self.assertEqual(mock_crypto_util.cert_and_chain_from_fullchain.call_count, 1) @mock.patch("certbot._internal.client.crypto_util") + @mock.patch("certbot.compat.os.remove") + def test_obtain_certificate_finalize_order_partial_success(self, mock_remove, mock_crypto_util): + from acme import messages + csr = util.CSR(form="pem", file=mock.sentinel.csr_file, data=CSR_SAN) + key = util.CSR(form="pem", file=mock.sentinel.key_file, data=CSR_SAN) + mock_crypto_util.generate_csr.return_value = csr + mock_crypto_util.generate_key.return_value = key + self._set_mock_from_fullchain(mock_crypto_util.cert_and_chain_from_fullchain) + + self._mock_obtain_certificate() + authzr = self._authzr_from_domains(self.eg_domains) + self.eg_order.authorizations = authzr + self.client.auth_handler.handle_authorizations.return_value = authzr + + identifier = messages.Identifier(typ=messages.IDENTIFIER_FQDN, value='example.com') + subproblem = messages.Error.with_code('caa', detail='bar', title='title', identifier=identifier) + error_with_subproblems = messages.Error.with_code('malformed', detail='foo', title='title', subproblems=[subproblem]) + self.client.acme.finalize_order.side_effect = [error_with_subproblems, mock.DEFAULT] + + self.config.allow_subset_of_names = True + + with test_util.patch_display_util(): + result = self.client.obtain_certificate(self.eg_domains) + + self.assertEqual( + result, + (mock.sentinel.cert, mock.sentinel.chain, key, csr)) + self.assertEqual(self.client.auth_handler.handle_authorizations.call_count, 2) + self.assertEqual(self.acme.finalize_order.call_count, 2) + + successful_domains = [d for d in self.eg_domains if d != 'example.com'] + self.assertEqual(mock_crypto_util.generate_key.call_count, 2) + mock_crypto_util.generate_csr.assert_has_calls([ + mock.call(key, self.eg_domains, self.config.csr_dir, self.config.must_staple, self.config.strict_permissions), + mock.call(key, successful_domains, self.config.csr_dir, self.config.must_staple, self.config.strict_permissions)]) + self.assertEqual(mock_remove.call_count, 2) + self.assertEqual(mock_crypto_util.cert_and_chain_from_fullchain.call_count, 1) + + @mock.patch("certbot._internal.client.crypto_util") + def test_obtain_certificate_finalize_order_no_retryable_domains(self, mock_crypto_util): + from acme import messages + csr = util.CSR(form="pem", file=mock.sentinel.csr_file, data=CSR_SAN) + key = util.CSR(form="pem", file=mock.sentinel.key_file, data=CSR_SAN) + mock_crypto_util.generate_csr.return_value = csr + mock_crypto_util.generate_key.return_value = key + self._set_mock_from_fullchain(mock_crypto_util.cert_and_chain_from_fullchain) + + self._mock_obtain_certificate() + authzr = self._authzr_from_domains(self.eg_domains) + self.eg_order.authorizations = authzr + self.client.auth_handler.handle_authorizations.return_value = authzr + + identifier1 = messages.Identifier(typ=messages.IDENTIFIER_FQDN, value='example.com') + identifier2 = messages.Identifier(typ=messages.IDENTIFIER_FQDN, value='www.example.com') + subproblem1 = messages.Error.with_code('caa', detail='bar', title='title', identifier=identifier1) + subproblem2 = messages.Error.with_code('caa', detail='bar', title='title', identifier=identifier2) + error_with_subproblems = messages.Error.with_code('malformed', detail='foo', title='title', subproblems=[subproblem1, subproblem2]) + self.client.acme.finalize_order.side_effect = error_with_subproblems + + self.config.allow_subset_of_names = True + + self.assertRaises(messages.Error, self.client.obtain_certificate, self.eg_domains) + self.assertEqual(self.client.auth_handler.handle_authorizations.call_count, 1) + self.assertEqual(self.acme.finalize_order.call_count, 1) + self.assertEqual(mock_crypto_util.generate_key.call_count, 1) + self.assertEqual(mock_crypto_util.cert_and_chain_from_fullchain.call_count, 0) + + @mock.patch("certbot._internal.client.crypto_util") + def test_obtain_certificate_finalize_order_rejected_identifier_no_subproblems(self, mock_crypto_util): + from acme import messages + csr = util.CSR(form="pem", file=mock.sentinel.csr_file, data=CSR_SAN) + key = util.CSR(form="pem", file=mock.sentinel.key_file, data=CSR_SAN) + mock_crypto_util.generate_csr.return_value = csr + mock_crypto_util.generate_key.return_value = key + self._set_mock_from_fullchain(mock_crypto_util.cert_and_chain_from_fullchain) + + self._mock_obtain_certificate() + authzr = self._authzr_from_domains(self.eg_domains) + self.eg_order.authorizations = authzr + self.client.auth_handler.handle_authorizations.return_value = authzr + + error = messages.Error.with_code('caa', detail='foo', title='title') + self.client.acme.finalize_order.side_effect = error + + self.config.allow_subset_of_names = True + + self.assertRaises(messages.Error, self.client.obtain_certificate, + self.eg_domains) + self.assertEqual(self.client.auth_handler.handle_authorizations.call_count, 1) + self.assertEqual(self.acme.finalize_order.call_count, 1) + self.assertEqual(mock_crypto_util.generate_key.call_count, 1) + self.assertEqual(mock_crypto_util.cert_and_chain_from_fullchain.call_count, 0) + + @mock.patch("certbot._internal.client.crypto_util") + @mock.patch("certbot.compat.os.remove") + def test_obtain_certificate_get_order_partial_success(self, mock_remove, mock_crypto_util): + from acme import messages + csr = util.CSR(form="pem", file=mock.sentinel.csr_file, data=CSR_SAN) + key = util.CSR(form="pem", file=mock.sentinel.key_file, data=CSR_SAN) + mock_crypto_util.generate_csr.return_value = csr + mock_crypto_util.generate_key.return_value = key + self._set_mock_from_fullchain(mock_crypto_util.cert_and_chain_from_fullchain) + + self._mock_obtain_certificate() + authzr = self._authzr_from_domains(self.eg_domains) + self.eg_order.authorizations = authzr + self.client.auth_handler.handle_authorizations.return_value = authzr + + identifier = messages.Identifier(typ=messages.IDENTIFIER_FQDN, value='example.com') + subproblem = messages.Error.with_code('caa', detail='bar', title='title', identifier=identifier) + error_with_subproblems = messages.Error.with_code('malformed', detail='foo', title='title', subproblems=[subproblem]) + self.client.acme.new_order.side_effect = [error_with_subproblems, mock.DEFAULT] + + self.config.allow_subset_of_names = True + + with test_util.patch_display_util(): + result = self.client.obtain_certificate(self.eg_domains) + + self.assertEqual( + result, + (mock.sentinel.cert, mock.sentinel.chain, key, csr)) + self.assertEqual(self.client.auth_handler.handle_authorizations.call_count, 1) + self.assertEqual(self.acme.new_order.call_count, 2) + + successful_domains = [d for d in self.eg_domains if d != 'example.com'] + self.assertEqual(mock_crypto_util.generate_key.call_count, 2) + mock_crypto_util.generate_csr.assert_has_calls([ + mock.call(key, self.eg_domains, self.config.csr_dir, self.config.must_staple, self.config.strict_permissions), + mock.call(key, successful_domains, self.config.csr_dir, self.config.must_staple, self.config.strict_permissions)]) + self.assertEqual(mock_remove.call_count, 2) + self.assertEqual(mock_crypto_util.cert_and_chain_from_fullchain.call_count, 1) + + @mock.patch("certbot._internal.client.crypto_util") + def test_obtain_certificate_get_order_no_retryable_domains(self, mock_crypto_util): + from acme import messages + csr = util.CSR(form="pem", file=mock.sentinel.csr_file, data=CSR_SAN) + key = util.CSR(form="pem", file=mock.sentinel.key_file, data=CSR_SAN) + mock_crypto_util.generate_csr.return_value = csr + mock_crypto_util.generate_key.return_value = key + self._set_mock_from_fullchain(mock_crypto_util.cert_and_chain_from_fullchain) + + self._mock_obtain_certificate() + authzr = self._authzr_from_domains(self.eg_domains) + self.eg_order.authorizations = authzr + self.client.auth_handler.handle_authorizations.return_value = authzr + + identifier1 = messages.Identifier(typ=messages.IDENTIFIER_FQDN, value='example.com') + identifier2 = messages.Identifier(typ=messages.IDENTIFIER_FQDN, value='www.example.com') + subproblem1 = messages.Error.with_code('caa', detail='bar', title='title', identifier=identifier1) + subproblem2 = messages.Error.with_code('caa', detail='bar', title='title', identifier=identifier2) + error_with_subproblems = messages.Error.with_code('malformed', detail='foo', title='title', subproblems=[subproblem1, subproblem2]) + self.client.acme.new_order.side_effect = error_with_subproblems + + self.config.allow_subset_of_names = True + + self.assertRaises(messages.Error, self.client.obtain_certificate, self.eg_domains) + self.assertEqual(self.client.auth_handler.handle_authorizations.call_count, 0) + self.assertEqual(self.acme.new_order.call_count, 1) + self.assertEqual(mock_crypto_util.generate_key.call_count, 1) + self.assertEqual(mock_crypto_util.cert_and_chain_from_fullchain.call_count, 0) + + @mock.patch("certbot._internal.client.crypto_util") + def test_obtain_certificate_get_order_rejected_identifier_no_subproblems(self, mock_crypto_util): + from acme import messages + csr = util.CSR(form="pem", file=mock.sentinel.csr_file, data=CSR_SAN) + key = util.CSR(form="pem", file=mock.sentinel.key_file, data=CSR_SAN) + mock_crypto_util.generate_csr.return_value = csr + mock_crypto_util.generate_key.return_value = key + self._set_mock_from_fullchain(mock_crypto_util.cert_and_chain_from_fullchain) + + self._mock_obtain_certificate() + authzr = self._authzr_from_domains(self.eg_domains) + self.eg_order.authorizations = authzr + self.client.auth_handler.handle_authorizations.return_value = authzr + + error = messages.Error.with_code('caa', detail='foo', title='title') + self.client.acme.new_order.side_effect = error + + self.config.allow_subset_of_names = True + + self.assertRaises(messages.Error, self.client.obtain_certificate, self.eg_domains) + self.assertEqual(self.client.auth_handler.handle_authorizations.call_count, 0) + self.assertEqual(self.acme.new_order.call_count, 1) + self.assertEqual(mock_crypto_util.generate_key.call_count, 1) + self.assertEqual(mock_crypto_util.cert_and_chain_from_fullchain.call_count, 0) + + @mock.patch("certbot._internal.client.crypto_util") @mock.patch("certbot._internal.client.acme_crypto_util") def test_obtain_certificate_dry_run(self, mock_acme_crypto, mock_crypto): csr = util.CSR(form="pem", file=None, data=CSR_SAN)
