Hello community,

here is the log from the commit of package python-certbot for openSUSE:Factory 
checked in at 2019-11-15 00:20:59
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-certbot (Old)
 and      /work/SRC/openSUSE:Factory/.python-certbot.new.26869 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-certbot"

Fri Nov 15 00:20:59 2019 rev:20 rq:748664 version:0.40.1

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-certbot/python-certbot.changes    
2019-10-31 18:13:58.953930837 +0100
+++ /work/SRC/openSUSE:Factory/.python-certbot.new.26869/python-certbot.changes 
2019-11-15 00:21:00.975935178 +0100
@@ -1,0 +2,15 @@
+Thu Nov 14 12:19:12 UTC 2019 - Marketa Calabkova <mcalabk...@suse.com>
+
+- update to version 0.40.1
+  * --server may now be combined with --dry-run.
+  * --dry-run now requests fresh authorizations every time, fixing 
+    the issue where it was prone to falsely reporting success.
+  * The OS detection logic again uses distro library for Linux OSes
+  * certbot.plugins.common.TLSSNI01 has been deprecated and will be 
+    removed in a future release.
+  * CLI flags --tls-sni-01-port and --tls-sni-01-address have been removed.
+  * The values tls-sni and tls-sni-01 for the --preferred-challenges 
+    flag are no longer accepted.
+  * Removed the flags: --agree-dev-preview, --dialog, and --apache-init-script
+
+-------------------------------------------------------------------

Old:
----
  certbot-0.39.0.tar.gz

New:
----
  certbot-0.40.1.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-certbot.spec ++++++
--- /var/tmp/diff_new_pack.vjmFuD/_old  2019-11-15 00:21:01.787934896 +0100
+++ /var/tmp/diff_new_pack.vjmFuD/_new  2019-11-15 00:21:01.787934896 +0100
@@ -1,7 +1,7 @@
 #
 # spec file for package python-certbot
 #
-# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany.
+# Copyright (c) 2019 SUSE LLC.
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -18,13 +18,13 @@
 
 %{?!python_module:%define python_module() python-%{**} python3-%{**}}
 Name:           python-certbot
-Version:        0.39.0
+Version:        0.40.1
 Release:        0
 Summary:        ACME client
 License:        Apache-2.0
 URL:            https://github.com/certbot/certbot
 Source:         
https://files.pythonhosted.org/packages/source/c/certbot/certbot-%{version}.tar.gz
-BuildRequires:  %{python_module acme >= 0.29.0}
+BuildRequires:  %{python_module acme >= 0.40.0}
 BuildRequires:  %{python_module configargparse >= 0.9.3}
 BuildRequires:  %{python_module configobj}
 BuildRequires:  %{python_module cryptography >= 1.2.3}
@@ -42,7 +42,7 @@
 BuildRequires:  fdupes
 BuildRequires:  python-rpm-macros
 BuildRequires:  python2-typing
-Requires:       python-acme >= 0.29.0
+Requires:       python-acme >= 0.40.0
 Requires:       python-configargparse >= 0.9.3
 Requires:       python-configobj
 Requires:       python-cryptography >= 1.2.3

++++++ certbot-0.39.0.tar.gz -> certbot-0.40.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/certbot-0.39.0/CHANGELOG.md 
new/certbot-0.40.1/CHANGELOG.md
--- old/certbot-0.39.0/CHANGELOG.md     2019-10-01 21:48:40.000000000 +0200
+++ new/certbot-0.40.1/CHANGELOG.md     2019-11-06 03:24:51.000000000 +0100
@@ -2,6 +2,52 @@
 
 Certbot adheres to [Semantic Versioning](https://semver.org/).
 
+## 0.40.1 - 2019-11-05
+
+### Changed
+
+* Added back support for Python 3.4 to Certbot components and certbot-auto due
+  to a bug when requiring Python 2.7 or 3.5+ on RHEL 6 based systems.
+
+More details about these changes can be found on our GitHub repo.
+
+## 0.40.0 - 2019-11-05
+
+### Added
+
+*
+
+### Changed
+
+* We deprecated support for Python 3.4 in Certbot and its ACME library. Support
+  for Python 3.4 will be removed in the next major release of Certbot.
+  certbot-auto users on RHEL 6 based systems will be asked to enable Software
+  Collections (SCL) repository so Python 3.6 can be installed. certbot-auto can
+  enable the SCL repo for you on CentOS 6 while users on other RHEL 6 based
+  systems will be asked to do this manually.
+* `--server` may now be combined with `--dry-run`. Certbot will, as before, 
use the
+  staging server instead of the live server when `--dry-run` is used.
+* `--dry-run` now requests fresh authorizations every time, fixing the issue
+  where it was prone to falsely reporting success.
+* Updated certbot-dns-google to depend on newer versions of
+  google-api-python-client and oauth2client.
+* The OS detection logic again uses distro library for Linux OSes
+* certbot.plugins.common.TLSSNI01 has been deprecated and will be removed in a
+  future release.
+* CLI flags --tls-sni-01-port and --tls-sni-01-address have been removed.
+* The values tls-sni and tls-sni-01 for the --preferred-challenges flag are no
+  longer accepted.
+* Removed the flags: `--agree-dev-preview`, `--dialog`, and 
`--apache-init-script`
+* acme.standalone.BaseRequestHandlerWithLogging and
+  acme.standalone.simple_tls_sni_01_server have been deprecated and will be
+  removed in a future release of the library.
+
+### Fixed
+
+*
+
+More details about these changes can be found on our GitHub repo.
+
 ## 0.39.0 - 2019-10-01
 
 ### Added
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/certbot-0.39.0/PKG-INFO new/certbot-0.40.1/PKG-INFO
--- old/certbot-0.39.0/PKG-INFO 2019-10-01 21:48:41.000000000 +0200
+++ new/certbot-0.40.1/PKG-INFO 2019-11-06 03:24:52.000000000 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: certbot
-Version: 0.39.0
+Version: 0.40.1
 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-0.39.0/certbot/__init__.py 
new/certbot-0.40.1/certbot/__init__.py
--- old/certbot-0.39.0/certbot/__init__.py      2019-10-01 21:48:41.000000000 
+0200
+++ new/certbot-0.40.1/certbot/__init__.py      2019-11-06 03:24:52.000000000 
+0100
@@ -1,4 +1,4 @@
 """Certbot client."""
 
 # version number like 1.2.3a0, must have at least 2 parts, like 1.2
-__version__ = '0.39.0'
+__version__ = '0.40.1'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/certbot-0.39.0/certbot/account.py 
new/certbot-0.40.1/certbot/account.py
--- old/certbot-0.39.0/certbot/account.py       2019-10-01 21:48:40.000000000 
+0200
+++ new/certbot-0.40.1/certbot/account.py       2019-11-06 03:24:51.000000000 
+0100
@@ -231,12 +231,7 @@
         except IOError as error:
             raise errors.AccountStorageError(error)
 
-        acc = Account(regr, key, meta)
-        if acc.id != account_id:
-            raise errors.AccountStorageError(
-                "Account ids mismatch (expected: {0}, found: {1}".format(
-                    account_id, acc.id))
-        return acc
+        return Account(regr, key, meta)
 
     def load(self, account_id):
         return self._load_for_server_path(account_id, self.config.server_path)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/certbot-0.39.0/certbot/auth_handler.py 
new/certbot-0.40.1/certbot/auth_handler.py
--- old/certbot-0.39.0/certbot/auth_handler.py  2019-10-01 21:48:40.000000000 
+0200
+++ new/certbot-0.40.1/certbot/auth_handler.py  2019-11-06 03:24:51.000000000 
+0100
@@ -7,8 +7,9 @@
 
 from acme import challenges
 from acme import messages
+from acme import errors as acme_errors
 # pylint: disable=unused-import, no-name-in-module
-from acme.magic_typing import Dict, List
+from acme.magic_typing import Dict, List, Tuple
 # pylint: enable=unused-import, no-name-in-module
 from certbot import achallenges
 from certbot import errors
@@ -97,6 +98,31 @@
 
             return authzrs_validated
 
+    def deactivate_valid_authorizations(self, orderr):
+        # type: (messages.OrderResource) -> Tuple[List, List]
+        """
+        Deactivate all `valid` authorizations in the order, so that they 
cannot be re-used
+        in subsequent orders.
+        :param messages.OrderResource orderr: must have authorizations filled 
in
+        :returns: tuple of list of successfully deactivated authorizations, and
+                  list of unsuccessfully deactivated authorizations.
+        :rtype: tuple
+        """
+        to_deactivate = [authzr for authzr in orderr.authorizations
+                         if authzr.body.status == messages.STATUS_VALID]
+        deactivated = []
+        failed = []
+
+        for authzr in to_deactivate:
+            try:
+                authzr = self.acme.deactivate_authorization(authzr)
+                deactivated.append(authzr)
+            except acme_errors.Error as e:
+                failed.append(authzr)
+                logger.debug('Failed to deactivate authorization %s: %s', 
authzr.uri, e)
+
+        return (deactivated, failed)
+
     def _poll_authorizations(self, authzrs, max_retries, best_effort):
         """
         Poll the ACME CA server, to wait for confirmation that authorizations 
have their challenges
@@ -182,9 +208,6 @@
 
             achalls.extend(self._challenge_factory(authzr, path))
 
-        if any(isinstance(achall.chall, challenges.TLSSNI01) for achall in 
achalls):
-            logger.warning("TLS-SNI-01 is deprecated, and will stop working 
soon.")
-
         return achalls
 
     def _get_chall_pref(self, domain):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/certbot-0.39.0/certbot/cli.py 
new/certbot-0.40.1/certbot/cli.py
--- old/certbot-0.39.0/certbot/cli.py   2019-10-01 21:48:40.000000000 +0200
+++ new/certbot-0.40.1/certbot/cli.py   2019-11-06 03:24:51.000000000 +0100
@@ -163,24 +163,6 @@
         VAR_MODIFIERS.setdefault(var, set()).update(modifiers)
 
 
-def possible_deprecation_warning(config):
-    "A deprecation warning for users with the old, not-self-upgrading 
letsencrypt-auto."
-    if cli_command != LEAUTO:
-        return
-    if config.no_self_upgrade:
-        # users setting --no-self-upgrade might be hanging on a client version 
like 0.3.0
-        # or 0.5.0 which is the new script, but doesn't set CERTBOT_AUTO; they 
don't
-        # need warnings
-        return
-    if "CERTBOT_AUTO" not in os.environ:
-        logger.warning("You are running with an old copy of letsencrypt-auto"
-            " that does not receive updates, and is less reliable than more"
-            " recent versions. The letsencrypt client has also been renamed"
-            " to Certbot. We recommend upgrading to the latest certbot-auto"
-            " script, or using native OS packages.")
-        logger.debug("Deprecation warning circumstances: %s / %s", 
sys.argv[0], os.environ)
-
-
 class _Default(object):
     """A class to use as a default to detect if a value is set by a user"""
 
@@ -642,20 +624,25 @@
             raise errors.Error(
                 "Parameters --hsts and --auto-hsts cannot be used 
simultaneously.")
 
-        possible_deprecation_warning(parsed_args)
-
         return parsed_args
 
     def set_test_server(self, parsed_args):
         """We have --staging/--dry-run; perform sanity check and set 
config.server"""
 
-        if parsed_args.server not in (flag_default("server"), 
constants.STAGING_URI):
-            conflicts = ["--staging"] if parsed_args.staging else []
-            conflicts += ["--dry-run"] if parsed_args.dry_run else []
-            raise errors.Error("--server value conflicts with {0}".format(
-                " and ".join(conflicts)))
+        # Flag combinations should produce these results:
+        #                             | --staging      | --dry-run   |
+        # ------------------------------------------------------------
+        # | --server acme-v02         | Use staging    | Use staging |
+        # | --server acme-staging-v02 | Use staging    | Use staging |
+        # | --server <other>          | Conflict error | Use <other> |
+
+        default_servers = (flag_default("server"), constants.STAGING_URI)
 
-        parsed_args.server = constants.STAGING_URI
+        if parsed_args.staging and parsed_args.server not in default_servers:
+            raise errors.Error("--server value conflicts with --staging")
+
+        if parsed_args.server in default_servers:
+            parsed_args.server = constants.STAGING_URI
 
         if parsed_args.dry_run:
             if self.verb not in ["certonly", "renew"]:
@@ -1259,20 +1246,6 @@
         default=flag_default("autorenew"), dest="autorenew",
         help="Disable auto renewal of certificates.")
 
-    helpful.add_deprecated_argument("--agree-dev-preview", 0)
-    helpful.add_deprecated_argument("--dialog", 0)
-
-    # Deprecation of tls-sni-01 related cli flags
-    # TODO: remove theses flags completely in few releases
-    class _DeprecatedTLSSNIAction(util._ShowWarning):  # pylint: 
disable=protected-access
-        def __call__(self, parser, namespace, values, option_string=None):
-            super(_DeprecatedTLSSNIAction, self).__call__(parser, namespace, 
values, option_string)
-            namespace.https_port = values
-    helpful.add(
-        ["testing", "standalone", "apache", "nginx"], "--tls-sni-01-port",
-        type=int, action=_DeprecatedTLSSNIAction, help=argparse.SUPPRESS)
-    helpful.add_deprecated_argument("--tls-sni-01-address", 1)
-
     # Populate the command line parameters for new style enhancements
     enhancements.populate_cli(helpful.add)
 
@@ -1559,18 +1532,10 @@
     :raises errors.Error: if pref_challs is invalid
 
     """
-    aliases = {"dns": "dns-01", "http": "http-01", "tls-sni": "tls-sni-01"}
+    aliases = {"dns": "dns-01", "http": "http-01"}
     challs = [c.strip() for c in pref_challs]
     challs = [aliases.get(c, c) for c in challs]
 
-    # Ignore tls-sni-01 from the list, and generates a deprecation warning
-    # TODO: remove this option completely in few releases
-    if "tls-sni-01" in challs:
-        logger.warning('TLS-SNI-01 support is deprecated. This value is being 
dropped from the '
-                       'setting of --preferred-challenges and future versions 
of Certbot will '
-                       'error if it is included.')
-        challs = [chall for chall in challs if chall != "tls-sni-01"]
-
     unrecognized = ", ".join(name for name in challs
                              if name not in challenges.Challenge.TYPES)
     if unrecognized:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/certbot-0.39.0/certbot/client.py 
new/certbot-0.40.1/certbot/client.py
--- old/certbot-0.39.0/certbot/client.py        2019-10-01 21:48:40.000000000 
+0200
+++ new/certbot-0.40.1/certbot/client.py        2019-11-06 03:24:51.000000000 
+0100
@@ -15,7 +15,7 @@
 from acme import crypto_util as acme_crypto_util
 from acme import errors as acme_errors
 from acme import messages
-from acme.magic_typing import Optional  # pylint: 
disable=unused-import,no-name-in-module
+from acme.magic_typing import Optional, List  # pylint: 
disable=unused-import,no-name-in-module
 
 import certbot
 from certbot import account
@@ -366,6 +366,7 @@
             return cert, chain, key, csr
 
     def _get_order_and_authorizations(self, csr_pem, best_effort):
+        # type: (str, bool) -> List[messages.OrderResource]
         """Request a new order and complete its authorizations.
 
         :param str csr_pem: A CSR in PEM format.
@@ -381,6 +382,17 @@
         except acme_errors.WildcardUnsupportedError:
             raise errors.Error("The currently selected ACME CA endpoint does"
                                " not support issuing wildcard certificates.")
+
+        # For a dry run, ensure we have an order with fresh authorizations
+        if orderr and self.config.dry_run:
+            deactivated, failed = 
self.auth_handler.deactivate_valid_authorizations(orderr)
+            if deactivated:
+                logger.debug("Recreating order after authz deactivations")
+                orderr = self.acme.new_order(csr_pem)
+            if failed:
+                logger.warning("Certbot was unable to obtain fresh 
authorizations for every domain"
+                               ". The dry run will continue, but results may 
not be accurate.")
+
         authzr = self.auth_handler.handle_authorizations(orderr, best_effort)
         return orderr.update(authorizations=authzr)
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/certbot-0.39.0/certbot/compat/filesystem.py 
new/certbot-0.40.1/certbot/compat/filesystem.py
--- old/certbot-0.39.0/certbot/compat/filesystem.py     2019-10-01 
21:48:40.000000000 +0200
+++ new/certbot-0.40.1/certbot/compat/filesystem.py     2019-11-06 
03:24:51.000000000 +0100
@@ -546,13 +546,7 @@
     if rights_desc['write']:
         flag = flag | (ntsecuritycon.FILE_ALL_ACCESS
                        ^ ntsecuritycon.FILE_GENERIC_READ
-                       ^ ntsecuritycon.FILE_GENERIC_EXECUTE
-                       # Despite bit `512` being present in 
ntsecuritycon.FILE_ALL_ACCESS, it is
-                       # not effectively applied to the file or the directory.
-                       # As _generate_windows_flags is also used to compare 
two dacls, we remove
-                       # it right now to have flags that contain only the bits 
effectively applied
-                       # by Windows.
-                       ^ 512)
+                       ^ ntsecuritycon.FILE_GENERIC_EXECUTE)
     if rights_desc['execute']:
         flag = flag | ntsecuritycon.FILE_GENERIC_EXECUTE
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/certbot-0.39.0/certbot/plugins/common.py 
new/certbot-0.40.1/certbot/plugins/common.py
--- old/certbot-0.39.0/certbot/plugins/common.py        2019-10-01 
21:48:40.000000000 +0200
+++ new/certbot-0.40.1/certbot/plugins/common.py        2019-11-06 
03:24:51.000000000 +0100
@@ -2,6 +2,7 @@
 import logging
 import re
 import shutil
+import sys
 import tempfile
 import warnings
 
@@ -503,3 +504,34 @@
         test_configs, os.path.join(temp_dir, test_dir), symlinks=True)
 
     return temp_dir, config_dir, work_dir
+
+
+# This class takes a similar approach to the cryptography project to deprecate 
attributes
+# in public modules. See the _ModuleWithDeprecation class here:
+# 
https://github.com/pyca/cryptography/blob/91105952739442a74582d3e62b3d2111365b0dc7/src/cryptography/utils.py#L129
+class _TLSSNI01DeprecationModule(object):
+    """
+    Internal class delegating to a module, and displaying warnings when
+    attributes related to TLS-SNI-01 are accessed.
+    """
+    def __init__(self, module):
+        self.__dict__['_module'] = module
+
+    def __getattr__(self, attr):
+        if attr == 'TLSSNI01':
+            warnings.warn('TLSSNI01 is deprecated and will be removed soon.',
+                          DeprecationWarning, stacklevel=2)
+        return getattr(self._module, attr)
+
+    def __setattr__(self, attr, value):  # pragma: no cover
+        setattr(self._module, attr, value)
+
+    def __delattr__(self, attr):  # pragma: no cover
+        delattr(self._module, attr)
+
+    def __dir__(self):  # pragma: no cover
+        return ['_module'] + dir(self._module)
+
+
+# Patching ourselves to warn about TLS-SNI challenge deprecation and removal.
+sys.modules[__name__] = _TLSSNI01DeprecationModule(sys.modules[__name__])
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/certbot-0.39.0/certbot/plugins/common_test.py 
new/certbot-0.40.1/certbot/plugins/common_test.py
--- old/certbot-0.39.0/certbot/plugins/common_test.py   2019-10-01 
21:48:40.000000000 +0200
+++ new/certbot-0.40.1/certbot/plugins/common_test.py   2019-11-06 
03:24:51.000000000 +0100
@@ -352,6 +352,11 @@
         self.assertEqual(self.sni.get_z_domain(achall),
             achall.response(achall.account_key).z_domain.decode("utf-8"))
 
+    def test_warning(self):
+        with mock.patch('certbot.plugins.common.warnings.warn') as mock_warn:
+            from certbot.plugins.common import TLSSNI01  # pylint: 
disable=unused-variable
+        self.assertTrue(mock_warn.call_args[0][0].startswith('TLSSNI01'))
+
 
 class InstallVersionControlledFileTest(test_util.TempDirTestCase):
     """Tests for certbot.plugins.common.install_version_controlled_file."""
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/certbot-0.39.0/certbot/renewal.py 
new/certbot-0.40.1/certbot/renewal.py
--- old/certbot-0.39.0/certbot/renewal.py       2019-10-01 21:48:40.000000000 
+0200
+++ new/certbot-0.40.1/certbot/renewal.py       2019-11-06 03:24:51.000000000 
+0100
@@ -434,7 +434,7 @@
                 if should_renew(lineage_config, renewal_candidate):
                     # Apply random sleep upon first renewal if needed
                     if apply_random_sleep:
-                        sleep_time = random.randint(1, 60 * 8)
+                        sleep_time = random.uniform(1, 60 * 8)
                         logger.info("Non-interactive renewal: random delay of 
%s seconds",
                                     sleep_time)
                         time.sleep(sleep_time)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/certbot-0.39.0/certbot/tests/account_test.py 
new/certbot-0.40.1/certbot/tests/account_test.py
--- old/certbot-0.39.0/certbot/tests/account_test.py    2019-10-01 
21:48:40.000000000 +0200
+++ new/certbot-0.40.1/certbot/tests/account_test.py    2019-11-06 
03:24:51.000000000 +0100
@@ -1,7 +1,6 @@
 """Tests for certbot.account."""
 import datetime
 import json
-import shutil
 import unittest
 
 import josepy as jose
@@ -170,13 +169,6 @@
     def test_load_non_existent_raises_error(self):
         self.assertRaises(errors.AccountNotFound, self.storage.load, "missing")
 
-    def test_load_id_mismatch_raises_error(self):
-        self.storage.save(self.acc, self.mock_client)
-        shutil.move(os.path.join(self.config.accounts_dir, self.acc.id),
-                    os.path.join(self.config.accounts_dir, "x" + self.acc.id))
-        self.assertRaises(errors.AccountStorageError, self.storage.load,
-                          "x" + self.acc.id)
-
     def _set_server(self, server):
         self.config.server = server
         from certbot.account import AccountFileStorage
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/certbot-0.39.0/certbot/tests/acme_util.py 
new/certbot-0.40.1/certbot/tests/acme_util.py
--- old/certbot-0.39.0/certbot/tests/acme_util.py       2019-10-01 
21:48:40.000000000 +0200
+++ new/certbot-0.40.1/certbot/tests/acme_util.py       2019-11-06 
03:24:51.000000000 +0100
@@ -18,12 +18,10 @@
 # Challenges
 HTTP01 = challenges.HTTP01(
     token=b"evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ+PCt92wr+oA")
-TLSSNI01 = challenges.TLSSNI01(
-    token=jose.b64decode(b"evaGxfADs6pSRb2LAv9IZf17Dt3juxGJyPCt92wrDoA"))
 DNS01 = challenges.DNS01(token=b"17817c66b60ce2e4012dfad92657527a")
 DNS01_2 = challenges.DNS01(token=b"cafecafecafecafecafecafe0feedbac")
 
-CHALLENGES = [HTTP01, TLSSNI01, DNS01]
+CHALLENGES = [HTTP01, DNS01]
 
 
 def gen_combos(challbs):
@@ -47,21 +45,19 @@
 
 
 # Pending ChallengeBody objects
-TLSSNI01_P = chall_to_challb(TLSSNI01, messages.STATUS_PENDING)
 HTTP01_P = chall_to_challb(HTTP01, messages.STATUS_PENDING)
 DNS01_P = chall_to_challb(DNS01, messages.STATUS_PENDING)
 DNS01_P_2 = chall_to_challb(DNS01_2, messages.STATUS_PENDING)
 
-CHALLENGES_P = [HTTP01_P, TLSSNI01_P, DNS01_P]
+CHALLENGES_P = [HTTP01_P, DNS01_P]
 
 
 # AnnotatedChallenge objects
 HTTP01_A = auth_handler.challb_to_achall(HTTP01_P, JWK, "example.com")
-TLSSNI01_A = auth_handler.challb_to_achall(TLSSNI01_P, JWK, "example.net")
 DNS01_A = auth_handler.challb_to_achall(DNS01_P, JWK, "example.org")
 DNS01_A_2 = auth_handler.challb_to_achall(DNS01_P_2, JWK, 
"esimerkki.example.org")
 
-ACHALLENGES = [HTTP01_A, TLSSNI01_A, DNS01_A]
+ACHALLENGES = [HTTP01_A, DNS01_A]
 
 
 def gen_authzr(authz_status, domain, challs, statuses, combos=True):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/certbot-0.39.0/certbot/tests/auth_handler_test.py 
new/certbot-0.40.1/certbot/tests/auth_handler_test.py
--- old/certbot-0.39.0/certbot/tests/auth_handler_test.py       2019-10-01 
21:48:40.000000000 +0200
+++ new/certbot-0.40.1/certbot/tests/auth_handler_test.py       2019-11-06 
03:24:51.000000000 +0100
@@ -9,6 +9,7 @@
 from acme import challenges
 from acme import client as acme_client
 from acme import messages
+from acme import errors as acme_errors
 
 from certbot import achallenges
 from certbot import errors
@@ -39,11 +40,11 @@
         self.assertEqual(
             [achall.chall for achall in achalls], acme_util.CHALLENGES)
 
-    def test_one_tls_sni(self):
-        achalls = self.handler._challenge_factory(self.authzr, [1])
+    def test_one_http(self):
+        achalls = self.handler._challenge_factory(self.authzr, [0])
 
         self.assertEqual(
-            [achall.chall for achall in achalls], [acme_util.TLSSNI01])
+            [achall.chall for achall in achalls], [acme_util.HTTP01])
 
     def test_unrecognized(self):
         authzr = acme_util.gen_authzr(
@@ -73,7 +74,7 @@
 
         self.mock_auth = mock.MagicMock(name="ApacheConfigurator")
 
-        self.mock_auth.get_chall_pref.return_value = [challenges.TLSSNI01]
+        self.mock_auth.get_chall_pref.return_value = [challenges.HTTP01]
 
         self.mock_auth.perform.side_effect = gen_auth_resp
 
@@ -90,7 +91,7 @@
     def tearDown(self):
         logging.disable(logging.NOTSET)
 
-    def _test_name1_tls_sni_01_1_common(self, combos):
+    def _test_name1_http_01_1_common(self, combos):
         authzr = gen_dom_authzr(domain="0", challs=acme_util.CHALLENGES, 
combos=combos)
         mock_order = mock.MagicMock(authorizations=[authzr])
 
@@ -109,44 +110,42 @@
             self.assertTrue(mock_time.sleep.call_args_list[1][0][0] > 3)
 
             self.assertEqual(self.mock_auth.cleanup.call_count, 1)
-            # Test if list first element is TLSSNI01, use typ because it is an 
achall
+            # Test if list first element is http-01, use typ because it is an 
achall
             self.assertEqual(
-                self.mock_auth.cleanup.call_args[0][0][0].typ, "tls-sni-01")
+                self.mock_auth.cleanup.call_args[0][0][0].typ, "http-01")
 
             self.assertEqual(len(authzr), 1)
 
-    def test_name1_tls_sni_01_1_acme_1(self):
-        self._test_name1_tls_sni_01_1_common(combos=True)
+    def test_name1_http_01_1_acme_1(self):
+        self._test_name1_http_01_1_common(combos=True)
 
-    def test_name1_tls_sni_01_1_acme_2(self):
+    def test_name1_http_01_1_acme_2(self):
         self.mock_net.acme_version = 2
-        self._test_name1_tls_sni_01_1_common(combos=False)
+        self._test_name1_http_01_1_common(combos=False)
 
-    def test_name1_tls_sni_01_1_http_01_1_dns_1_acme_1(self):
+    def test_name1_http_01_1_dns_1_acme_1(self):
         self.mock_net.poll.side_effect = _gen_mock_on_poll()
-        self.mock_auth.get_chall_pref.return_value.append(challenges.HTTP01)
         self.mock_auth.get_chall_pref.return_value.append(challenges.DNS01)
 
         authzr = gen_dom_authzr(domain="0", challs=acme_util.CHALLENGES, 
combos=False)
         mock_order = mock.MagicMock(authorizations=[authzr])
         authzr = self.handler.handle_authorizations(mock_order)
 
-        self.assertEqual(self.mock_net.answer_challenge.call_count, 3)
+        self.assertEqual(self.mock_net.answer_challenge.call_count, 2)
 
         self.assertEqual(self.mock_net.poll.call_count, 1)
 
         self.assertEqual(self.mock_auth.cleanup.call_count, 1)
-        # Test if list first element is TLSSNI01, use typ because it is an 
achall
+        # Test if list first element is http-01, use typ because it is an 
achall
         for achall in self.mock_auth.cleanup.call_args[0][0]:
-            self.assertTrue(achall.typ in ["tls-sni-01", "http-01", "dns-01"])
+            self.assertTrue(achall.typ in ["http-01", "dns-01"])
 
         # Length of authorizations list
         self.assertEqual(len(authzr), 1)
 
-    def test_name1_tls_sni_01_1_http_01_1_dns_1_acme_2(self):
+    def test_name1_http_01_1_dns_1_acme_2(self):
         self.mock_net.acme_version = 2
         self.mock_net.poll.side_effect = _gen_mock_on_poll()
-        self.mock_auth.get_chall_pref.return_value.append(challenges.HTTP01)
         self.mock_auth.get_chall_pref.return_value.append(challenges.DNS01)
 
         authzr = gen_dom_authzr(domain="0", challs=acme_util.CHALLENGES, 
combos=False)
@@ -160,12 +159,12 @@
         self.assertEqual(self.mock_auth.cleanup.call_count, 1)
         cleaned_up_achalls = self.mock_auth.cleanup.call_args[0][0]
         self.assertEqual(len(cleaned_up_achalls), 1)
-        self.assertEqual(cleaned_up_achalls[0].typ, "tls-sni-01")
+        self.assertEqual(cleaned_up_achalls[0].typ, "http-01")
 
         # Length of authorizations list
         self.assertEqual(len(authzr), 1)
 
-    def _test_name3_tls_sni_01_3_common(self, combos):
+    def _test_name3_http_01_3_common(self, combos):
         self.mock_net.request_domain_challenges.side_effect = 
functools.partial(
             gen_dom_authzr, challs=acme_util.CHALLENGES, combos=combos)
 
@@ -186,12 +185,12 @@
 
         self.assertEqual(len(authzr), 3)
 
-    def test_name3_tls_sni_01_3_common_acme_1(self):
-        self._test_name3_tls_sni_01_3_common(combos=True)
+    def test_name3_http_01_3_common_acme_1(self):
+        self._test_name3_http_01_3_common(combos=True)
 
-    def test_name3_tls_sni_01_3_common_acme_2(self):
+    def test_name3_http_01_3_common_acme_2(self):
         self.mock_net.acme_version = 2
-        self._test_name3_tls_sni_01_3_common(combos=False)
+        self._test_name3_http_01_3_common(combos=False)
 
     def test_debug_challenges(self):
         zope.component.provideUtility(
@@ -257,7 +256,7 @@
     def _test_preferred_challenges_not_supported_common(self, combos):
         authzrs = [gen_dom_authzr(domain="0", challs=acme_util.CHALLENGES, 
combos=combos)]
         mock_order = mock.MagicMock(authorizations=authzrs)
-        self.handler.pref_challs.append(challenges.HTTP01.typ)
+        self.handler.pref_challs.append(challenges.DNS01.typ)
         self.assertRaises(
             errors.AuthorizationError, self.handler.handle_authorizations, 
mock_order)
 
@@ -283,7 +282,7 @@
 
         self.assertEqual(self.mock_auth.cleanup.call_count, 1)
         self.assertEqual(
-            self.mock_auth.cleanup.call_args[0][0][0].typ, "tls-sni-01")
+            self.mock_auth.cleanup.call_args[0][0][0].typ, "http-01")
 
     def test_answer_error(self):
         self.mock_net.answer_challenge.side_effect = errors.AuthorizationError
@@ -295,7 +294,7 @@
             errors.AuthorizationError, self.handler.handle_authorizations, 
mock_order)
         self.assertEqual(self.mock_auth.cleanup.call_count, 1)
         self.assertEqual(
-            self.mock_auth.cleanup.call_args[0][0][0].typ, "tls-sni-01")
+            self.mock_auth.cleanup.call_args[0][0][0].typ, "http-01")
 
     def test_incomplete_authzr_error(self):
         authzrs = [gen_dom_authzr(domain="0", challs=acme_util.CHALLENGES)]
@@ -308,7 +307,7 @@
         self.assertTrue('Some challenges have failed.' in str(error.exception))
         self.assertEqual(self.mock_auth.cleanup.call_count, 1)
         self.assertEqual(
-            self.mock_auth.cleanup.call_args[0][0][0].typ, "tls-sni-01")
+            self.mock_auth.cleanup.call_args[0][0][0].typ, "http-01")
 
     def test_best_effort(self):
         def _conditional_mock_on_poll(authzr):
@@ -344,27 +343,58 @@
         self.assertTrue('All challenges have failed.' in str(error.exception))
 
     def test_validated_challenge_not_rerun(self):
-        # With pending challenge, we expect the challenge to be tried, and 
fail.
+        # With a pending challenge that is not supported by the plugin, we
+        # expect an exception to be raised.
         authzr = acme_util.gen_authzr(
                 messages.STATUS_PENDING, "0",
-                [acme_util.HTTP01],
+                [acme_util.DNS01],
                 [messages.STATUS_PENDING], False)
         mock_order = mock.MagicMock(authorizations=[authzr])
         self.assertRaises(
             errors.AuthorizationError, self.handler.handle_authorizations, 
mock_order)
 
-        # With validated challenge; we expect the challenge not be tried 
again, and succeed.
+        # With a validated challenge that is not supported by the plugin, we
+        # expect the challenge to not be solved again and
+        # handle_authorizations() to succeed.
         authzr = acme_util.gen_authzr(
                 messages.STATUS_VALID, "0",
-                [acme_util.HTTP01],
+                [acme_util.DNS01],
                 [messages.STATUS_VALID], False)
         mock_order = mock.MagicMock(authorizations=[authzr])
         self.handler.handle_authorizations(mock_order)
 
-    @mock.patch("certbot.auth_handler.logger")
-    def test_tls_sni_logs(self, logger):
-        self._test_name1_tls_sni_01_1_common(combos=True)
-        self.assertTrue("deprecated" in logger.warning.call_args[0][0])
+    def test_valid_authzrs_deactivated(self):
+        """When we deactivate valid authzrs in an orderr, we expect them to 
become deactivated
+        and to receive a list of deactivated authzrs in return."""
+        def _mock_deactivate(authzr):
+            if authzr.body.status == messages.STATUS_VALID:
+                if authzr.body.identifier.value == "is_valid_but_will_fail":
+                    raise acme_errors.Error("Mock deactivation ACME error")
+                authzb = authzr.body.update(status=messages.STATUS_DEACTIVATED)
+                authzr = messages.AuthorizationResource(body=authzb)
+            else: # pragma: no cover
+                raise errors.Error("Can't deactivate non-valid authz")
+            return authzr
+
+        to_deactivate = [("is_valid", messages.STATUS_VALID),
+                         ("is_pending", messages.STATUS_PENDING),
+                         ("is_valid_but_will_fail", messages.STATUS_VALID)]
+
+        to_deactivate = [acme_util.gen_authzr(a[1], a[0], [acme_util.HTTP01],
+                         [a[1], False]) for a in to_deactivate]
+        orderr = mock.MagicMock(authorizations=to_deactivate)
+
+        self.mock_net.deactivate_authorization.side_effect = _mock_deactivate
+
+        authzrs, failed = self.handler.deactivate_valid_authorizations(orderr)
+
+        self.assertEqual(self.mock_net.deactivate_authorization.call_count, 2)
+        self.assertEqual(len(authzrs), 1)
+        self.assertEqual(len(failed), 1)
+        self.assertEqual(authzrs[0].body.identifier.value, "is_valid")
+        self.assertEqual(authzrs[0].body.status, messages.STATUS_DEACTIVATED)
+        self.assertEqual(failed[0].body.identifier.value, 
"is_valid_but_will_fail")
+        self.assertEqual(failed[0].body.status, messages.STATUS_VALID)
 
 
 def _gen_mock_on_poll(status=messages.STATUS_VALID, retry=0, wait_value=1):
@@ -417,9 +447,9 @@
         return gen_challenge_path(challbs, preferences, combinations)
 
     def test_common_case(self):
-        """Given TLSSNI01 and HTTP01 with appropriate combos."""
-        challbs = (acme_util.TLSSNI01_P, acme_util.HTTP01_P)
-        prefs = [challenges.TLSSNI01, challenges.HTTP01]
+        """Given DNS01 and HTTP01 with appropriate combos."""
+        challbs = (acme_util.DNS01_P, acme_util.HTTP01_P)
+        prefs = [challenges.DNS01, challenges.HTTP01]
         combos = ((0,), (1,))
 
         # Smart then trivial dumb path test
@@ -430,8 +460,8 @@
         self.assertTrue(self._call(challbs[::-1], prefs, None))
 
     def test_not_supported(self):
-        challbs = (acme_util.DNS01_P, acme_util.TLSSNI01_P)
-        prefs = [challenges.TLSSNI01]
+        challbs = (acme_util.DNS01_P, acme_util.HTTP01_P)
+        prefs = [challenges.HTTP01]
         combos = ((0, 1),)
 
         # smart path fails because no challs in perfs satisfies combos
@@ -459,19 +489,19 @@
 
         http_01 = messages.ChallengeBody(**kwargs)
 
-        kwargs["chall"] = acme_util.TLSSNI01
-        tls_sni_01 = messages.ChallengeBody(**kwargs)
+        kwargs["chall"] = acme_util.HTTP01
+        http_01 = messages.ChallengeBody(**kwargs)
 
         self.authzr1 = mock.MagicMock()
         self.authzr1.body.identifier.value = 'example.com'
-        self.authzr1.body.challenges = [http_01, tls_sni_01]
+        self.authzr1.body.challenges = [http_01, http_01]
 
         kwargs["error"] = messages.Error(typ="dnssec", detail="detail")
-        tls_sni_01_diff = messages.ChallengeBody(**kwargs)
+        http_01_diff = messages.ChallengeBody(**kwargs)
 
         self.authzr2 = mock.MagicMock()
         self.authzr2.body.identifier.value = 'foo.bar'
-        self.authzr2.body.challenges = [tls_sni_01_diff]
+        self.authzr2.body.challenges = [http_01_diff]
 
     @test_util.patch_get_utility()
     def test_same_error_and_domain(self, mock_zope):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/certbot-0.39.0/certbot/tests/cli_test.py 
new/certbot-0.40.1/certbot/tests/cli_test.py
--- old/certbot-0.39.0/certbot/tests/cli_test.py        2019-10-01 
21:48:40.000000000 +0200
+++ new/certbot-0.40.1/certbot/tests/cli_test.py        2019-11-06 
03:24:51.000000000 +0100
@@ -249,13 +249,6 @@
         expected = [challenges.HTTP01.typ, challenges.DNS01.typ]
         self.assertEqual(namespace.pref_challs, expected)
 
-        # TODO: to be removed once tls-sni deprecation logic is removed
-        with mock.patch('certbot.cli.logger.warning') as mock_warn:
-            self.assertEqual(self.parse(['--preferred-challenges', 'http, 
tls-sni']).pref_challs,
-                             [challenges.HTTP01.typ])
-        self.assertEqual(mock_warn.call_count, 1)
-        self.assertTrue('deprecated' in mock_warn.call_args[0][0])
-
         short_args = ['--preferred-challenges', 'jumping-over-the-moon']
         # argparse.ArgumentError makes argparse print more information
         # to stderr and call sys.exit()
@@ -272,16 +265,6 @@
         self.assertTrue(namespace.must_staple)
         self.assertTrue(namespace.staple)
 
-    def test_no_gui(self):
-        args = ['renew', '--dialog']
-        with mock.patch("certbot.util.logger.warning") as mock_warn:
-            namespace = self.parse(args)
-
-        self.assertTrue(namespace.noninteractive_mode)
-        self.assertEqual(mock_warn.call_count, 1)
-        self.assertTrue("is deprecated" in mock_warn.call_args[0][0])
-        self.assertEqual("--dialog", mock_warn.call_args[0][1])
-
     def _check_server_conflict_message(self, parser_args, conflicting_args):
         try:
             self.parse(parser_args)
@@ -333,16 +316,26 @@
 
         self._assert_dry_run_flag_worked(self.parse(short_args + ['auth']), 
True)
         self._assert_dry_run_flag_worked(self.parse(short_args + ['renew']), 
True)
-        short_args += ['certonly']
-        self._assert_dry_run_flag_worked(self.parse(short_args), True)
+        self._assert_dry_run_flag_worked(self.parse(short_args + 
['certonly']), True)
 
-        short_args += '--server example.com'.split()
-        conflicts = ['--dry-run']
-        self._check_server_conflict_message(short_args, '--dry-run')
+        short_args += ['certonly']
 
-        short_args += ['--staging']
-        conflicts += ['--staging']
-        self._check_server_conflict_message(short_args, conflicts)
+        # `--dry-run --server example.com` should emit example.com
+        self.assertEqual(self.parse(short_args + ['--server', 
'example.com']).server,
+                         'example.com')
+
+        # `--dry-run --server STAGING_URI` should emit STAGING_URI
+        self.assertEqual(self.parse(short_args + ['--server', 
constants.STAGING_URI]).server,
+                         constants.STAGING_URI)
+
+        # `--dry-run --server LIVE` should emit STAGING_URI
+        self.assertEqual(self.parse(short_args + ['--server', 
cli.flag_default("server")]).server,
+                         constants.STAGING_URI)
+
+        # `--dry-run --server example.com --staging` should emit an error
+        conflicts = ['--staging']
+        self._check_server_conflict_message(short_args + ['--server', 
'example.com', '--staging'],
+                                            conflicts)
 
     def test_option_was_set(self):
         key_size_option = 'rsa_key_size'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/certbot-0.39.0/certbot/tests/client_test.py 
new/certbot-0.40.1/certbot/tests/client_test.py
--- old/certbot-0.39.0/certbot/tests/client_test.py     2019-10-01 
21:48:40.000000000 +0200
+++ new/certbot-0.40.1/certbot/tests/client_test.py     2019-11-06 
03:24:51.000000000 +0100
@@ -256,6 +256,7 @@
     def _mock_obtain_certificate(self):
         self.client.auth_handler = mock.MagicMock()
         self.client.auth_handler.handle_authorizations.return_value = [None]
+        self.client.auth_handler.deactivate_valid_authorizations.return_value 
= ([], [])
         self.acme.finalize_order.return_value = self.eg_order
         self.acme.new_order.return_value = self.eg_order
         self.eg_order.update.return_value = self.eg_order
@@ -360,6 +361,47 @@
         mock_crypto.init_save_csr.assert_not_called()
         self.assertEqual(mock_crypto.cert_and_chain_from_fullchain.call_count, 
1)
 
+    @mock.patch("certbot.client.logger")
+    @mock.patch("certbot.client.crypto_util")
+    @mock.patch("certbot.client.acme_crypto_util")
+    def test_obtain_certificate_dry_run_authz_deactivations_failed(self, 
mock_acme_crypto,
+                                                                   
mock_crypto, mock_log):
+        from acme import messages
+        csr = util.CSR(form="pem", file=None, data=CSR_SAN)
+        mock_acme_crypto.make_csr.return_value = CSR_SAN
+        mock_crypto.make_key.return_value = mock.sentinel.key_pem
+        key = util.Key(file=None, pem=mock.sentinel.key_pem)
+        
self._set_mock_from_fullchain(mock_crypto.cert_and_chain_from_fullchain)
+
+        self._mock_obtain_certificate()
+        self.client.config.dry_run = True
+
+        # Two authzs that are already valid and should get deactivated (dry 
run)
+        authzrs = self._authzr_from_domains(["example.com", "www.example.com"])
+        for authzr in authzrs:
+            authzr.body.status = messages.STATUS_VALID
+
+        # One deactivation succeeds, one fails
+        auth_handler = self.client.auth_handler
+        auth_handler.deactivate_valid_authorizations.return_value = 
([authzrs[0]], [authzrs[1]])
+
+        # Certificate should get issued despite one failed deactivation
+        self.eg_order.authorizations = authzrs
+        self.client.auth_handler.handle_authorizations.return_value = authzrs
+        with test_util.patch_get_utility():
+            result = self.client.obtain_certificate(self.eg_domains)
+        self.assertEqual(result, (mock.sentinel.cert, mock.sentinel.chain, 
key, csr))
+        self._check_obtain_certificate(1)
+
+        # Deactivation success/failure should have been handled properly
+        
self.assertEqual(auth_handler.deactivate_valid_authorizations.call_count, 1,
+                        "Deactivate authorizations should be called")
+        self.assertEqual(self.acme.new_order.call_count, 2,
+                        "Order should be recreated due to successfully 
deactivated authorizations")
+        mock_log.warning.assert_called_with("Certbot was unable to obtain 
fresh authorizations for"
+                                            " every domain. The dry run will 
continue, but results"
+                                            " may not be accurate.")
+
     def _set_mock_from_fullchain(self, mock_from_fullchain):
         mock_cert = mock.Mock()
         mock_cert.encode.return_value = mock.sentinel.cert
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/certbot-0.39.0/certbot/tests/compat/filesystem_test.py 
new/certbot-0.40.1/certbot/tests/compat/filesystem_test.py
--- old/certbot-0.39.0/certbot/tests/compat/filesystem_test.py  2019-10-01 
21:48:40.000000000 +0200
+++ new/certbot-0.40.1/certbot/tests/compat/filesystem_test.py  2019-11-06 
03:24:51.000000000 +0100
@@ -89,8 +89,8 @@
         self.assertEqual(len(system_aces), 1)
         self.assertEqual(len(admin_aces), 1)
 
-        self.assertEqual(system_aces[0][1], ntsecuritycon.FILE_ALL_ACCESS ^ 
512)
-        self.assertEqual(admin_aces[0][1], ntsecuritycon.FILE_ALL_ACCESS ^ 512)
+        self.assertEqual(system_aces[0][1], ntsecuritycon.FILE_ALL_ACCESS)
+        self.assertEqual(admin_aces[0][1], ntsecuritycon.FILE_ALL_ACCESS)
 
     def test_read_flag(self):
         self._test_flag(4, ntsecuritycon.FILE_GENERIC_READ)
@@ -101,12 +101,10 @@
     def test_write_flag(self):
         self._test_flag(2, (ntsecuritycon.FILE_ALL_ACCESS
                             ^ ntsecuritycon.FILE_GENERIC_READ
-                            ^ ntsecuritycon.FILE_GENERIC_EXECUTE
-                            ^ 512))
+                            ^ ntsecuritycon.FILE_GENERIC_EXECUTE))
 
     def test_full_flag(self):
-        self._test_flag(7, (ntsecuritycon.FILE_ALL_ACCESS
-                            ^ 512))
+        self._test_flag(7, ntsecuritycon.FILE_ALL_ACCESS)
 
     def _test_flag(self, everyone_mode, windows_flag):
         # Note that flag is tested against `everyone`, not `user`, because 
practically these unit
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/certbot-0.39.0/certbot/tests/main_test.py 
new/certbot-0.40.1/certbot/tests/main_test.py
--- old/certbot-0.39.0/certbot/tests/main_test.py       2019-10-01 
21:48:40.000000000 +0200
+++ new/certbot-0.40.1/certbot/tests/main_test.py       2019-11-06 
03:24:51.000000000 +0100
@@ -1381,11 +1381,6 @@
                     jose.ComparableX509(cert),
                     mock.ANY)
 
-    def test_agree_dev_preview_config(self):
-        with mock.patch('certbot.main.run') as mocked_run:
-            self._call(['-c', test_util.vector_path('cli.ini')])
-        self.assertTrue(mocked_run.called)
-
     @mock.patch('certbot.log.post_arg_parse_setup')
     def test_register(self, _):
         with mock.patch('certbot.main.client') as mocked_client:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/certbot-0.39.0/certbot/tests/renewal_test.py 
new/certbot-0.40.1/certbot/tests/renewal_test.py
--- old/certbot-0.39.0/certbot/tests/renewal_test.py    2019-10-01 
21:48:40.000000000 +0200
+++ new/certbot-0.40.1/certbot/tests/renewal_test.py    2019-11-06 
03:24:51.000000000 +0100
@@ -75,15 +75,10 @@
     @mock.patch('certbot.renewal.cli.set_by_cli')
     def test_pref_challs_list(self, mock_set_by_cli):
         mock_set_by_cli.return_value = False
-        # TODO: remove tls-sni and related assertions to logger.warning call 
once
-        #  the deprecation logic has been removed
-        renewalparams = {'pref_challs': 'tls-sni, http-01, dns'.split(',')}
-        with mock.patch('certbot.renewal.cli.logger.warning') as mock_warn:
-            self._call(self.config, renewalparams)
+        renewalparams = {'pref_challs': 'http-01, dns'.split(',')}
+        self._call(self.config, renewalparams)
         expected = [challenges.HTTP01.typ, challenges.DNS01.typ]
         self.assertEqual(self.config.pref_challs, expected)
-        self.assertEqual(mock_warn.call_count, 1)
-        self.assertTrue('deprecated' in mock_warn.call_args[0][0])
 
     @mock.patch('certbot.renewal.cli.set_by_cli')
     def test_pref_challs_str(self, mock_set_by_cli):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/certbot-0.39.0/certbot/tests/util.py 
new/certbot-0.40.1/certbot/tests/util.py
--- old/certbot-0.39.0/certbot/tests/util.py    2019-10-01 21:48:40.000000000 
+0200
+++ new/certbot-0.40.1/certbot/tests/util.py    2019-11-06 03:24:51.000000000 
+0100
@@ -94,26 +94,6 @@
     return OpenSSL.crypto.load_privatekey(loader, load_vector(*names))
 
 
-def skip_unless(condition, reason):  # pragma: no cover
-    """Skip tests unless a condition holds.
-
-    This implements the basic functionality of unittest.skipUnless
-    which is only available on Python 2.7+.
-
-    :param bool condition: If ``False``, the test will be skipped
-    :param str reason: the reason for skipping the test
-
-    :rtype: callable
-    :returns: decorator that hides tests unless condition is ``True``
-
-    """
-    if hasattr(unittest, "skipUnless"):
-        return unittest.skipUnless(condition, reason)
-    elif condition:
-        return lambda cls: cls
-    return lambda cls: None
-
-
 def make_lineage(config_dir, testfile):
     """Creates a lineage defined by testfile.
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/certbot-0.39.0/certbot/tests/util_test.py 
new/certbot-0.40.1/certbot/tests/util_test.py
--- old/certbot-0.39.0/certbot/tests/util_test.py       2019-10-01 
21:48:40.000000000 +0200
+++ new/certbot-0.40.1/certbot/tests/util_test.py       2019-11-06 
03:24:51.000000000 +0100
@@ -1,6 +1,7 @@
 """Tests for certbot.util."""
 import argparse
 import errno
+import sys
 import unittest
 
 import mock
@@ -473,74 +474,92 @@
 class OsInfoTest(unittest.TestCase):
     """Test OS / distribution detection"""
 
-    def test_systemd_os_release(self):
-        from certbot.util import (get_os_info, get_systemd_os_info,
-                                  get_os_info_ua)
-
-        with mock.patch('certbot.compat.os.path.isfile', return_value=True):
-            self.assertEqual(get_os_info(
-                test_util.vector_path("os-release"))[0], 'systemdos')
-            self.assertEqual(get_os_info(
-                test_util.vector_path("os-release"))[1], '42')
-            self.assertEqual(get_systemd_os_info(os.devnull), ("", ""))
-            self.assertEqual(get_os_info_ua(
-                test_util.vector_path("os-release")), "SystemdOS")
-        with mock.patch('certbot.compat.os.path.isfile', return_value=False):
-            self.assertEqual(get_systemd_os_info(), ("", ""))
-
-    def test_systemd_os_release_like(self):
-        from certbot.util import get_systemd_os_like
-
-        with mock.patch('certbot.compat.os.path.isfile', return_value=True):
-            id_likes = get_systemd_os_like(test_util.vector_path(
-                "os-release"))
-            self.assertEqual(len(id_likes), 3)
-            self.assertTrue("debian" in id_likes)
+    @mock.patch("certbot.util.distro")
+    @unittest.skipUnless(sys.platform.startswith("linux"), "requires Linux")
+    def test_systemd_os_release_like(self, m_distro):
+        import certbot.util as cbutil
+        m_distro.like.return_value = "first debian third"
+        id_likes = cbutil.get_systemd_os_like()
+        self.assertEqual(len(id_likes), 3)
+        self.assertTrue("debian" in id_likes)
+
+    @mock.patch("certbot.util.distro")
+    @unittest.skipUnless(sys.platform.startswith("linux"), "requires Linux")
+    def test_get_os_info_ua(self, m_distro):
+        import certbot.util as cbutil
+        with mock.patch('platform.system_alias',
+                        return_value=('linux', '42', '42')):
+            m_distro.name.return_value = ""
+            m_distro.linux_distribution.return_value = ("something", "1.0", 
"codename")
+            cbutil.get_python_os_info(pretty=True)
+            self.assertEqual(cbutil.get_os_info_ua(),
+                            " ".join(cbutil.get_python_os_info(pretty=True)))
+
+        m_distro.name.return_value = "whatever"
+        self.assertEqual(cbutil.get_os_info_ua(), "whatever")
+
+    @mock.patch("certbot.util.distro")
+    @unittest.skipUnless(sys.platform.startswith("linux"), "requires Linux")
+    def test_get_os_info(self, m_distro):
+        import certbot.util as cbutil
+        with mock.patch("platform.system") as mock_platform:
+            m_distro.linux_distribution.return_value = ("name", "version", 'x')
+            mock_platform.return_value = "linux"
+            self.assertEqual(cbutil.get_os_info(), ("name", "version"))
+
+            m_distro.linux_distribution.return_value = ("something", "else")
+            self.assertEqual(cbutil.get_os_info(), ("something", "else"))
+
+    @mock.patch("warnings.warn")
+    @mock.patch("certbot.util.distro")
+    @unittest.skipUnless(sys.platform.startswith("linux"), "requires Linux")
+    def test_get_systemd_os_info_deprecation(self, _, mock_warn):
+        import certbot.util as cbutil
+        cbutil.get_systemd_os_info()
+        self.assertTrue(mock_warn.called)
 
     @mock.patch("certbot.util.subprocess.Popen")
     def test_non_systemd_os_info(self, popen_mock):
-        from certbot.util import (get_os_info, get_python_os_info,
-                                     get_os_info_ua)
-        with mock.patch('certbot.compat.os.path.isfile', return_value=False):
+        import certbot.util as cbutil
+        with mock.patch('certbot.util._USE_DISTRO', False):
             with mock.patch('platform.system_alias',
                             return_value=('NonSystemD', '42', '42')):
-                self.assertEqual(get_os_info()[0], 'nonsystemd')
-                self.assertEqual(get_os_info_ua(),
-                                 " ".join(get_python_os_info()))
+                self.assertEqual(cbutil.get_python_os_info()[0], 'nonsystemd')
 
             with mock.patch('platform.system_alias',
                             return_value=('darwin', '', '')):
                 comm_mock = mock.Mock()
                 comm_attrs = {'communicate.return_value':
-                              ('42.42.42', 'error')}
+                            ('42.42.42', 'error')}
                 comm_mock.configure_mock(**comm_attrs)
                 popen_mock.return_value = comm_mock
-                self.assertEqual(get_os_info()[0], 'darwin')
-                self.assertEqual(get_os_info()[1], '42.42.42')
-
-            with mock.patch('platform.system_alias',
-                            return_value=('linux', '', '')):
-                with mock.patch('platform.linux_distribution',
-                                side_effect=AttributeError,
-                                create=True):
-                    with mock.patch('distro.linux_distribution',
-                                    return_value=('', '', '')):
-                        self.assertEqual(get_python_os_info(), ("linux", ""))
-
-                    with mock.patch('distro.linux_distribution',
-                                    return_value=('testdist', '42', '')):
-                        self.assertEqual(get_python_os_info(), ("testdist", 
"42"))
+                self.assertEqual(cbutil.get_python_os_info()[0], 'darwin')
+                self.assertEqual(cbutil.get_python_os_info()[1], '42.42.42')
 
             with mock.patch('platform.system_alias',
                             return_value=('freebsd', '9.3-RC3-p1', '')):
-                self.assertEqual(get_python_os_info(), ("freebsd", "9"))
+                self.assertEqual(cbutil.get_python_os_info(), ("freebsd", "9"))
 
             with mock.patch('platform.system_alias',
                             return_value=('windows', '', '')):
                 with mock.patch('platform.win32_ver',
                                 return_value=('4242', '95', '2', '')):
-                    self.assertEqual(get_python_os_info(),
-                                     ("windows", "95"))
+                    self.assertEqual(cbutil.get_python_os_info(),
+                                    ("windows", "95"))
+
+    @mock.patch("certbot.util.distro")
+    @unittest.skipUnless(sys.platform.startswith("linux"), "requires Linux")
+    def test_python_os_info_notfound(self, m_distro):
+        import certbot.util as cbutil
+        m_distro.linux_distribution.return_value = ('', '', '')
+        self.assertEqual(cbutil.get_python_os_info()[0], "linux")
+
+    @mock.patch("certbot.util.distro")
+    @unittest.skipUnless(sys.platform.startswith("linux"), "requires Linux")
+    def test_python_os_info_custom(self, m_distro):
+        import certbot.util as cbutil
+        m_distro.linux_distribution.return_value = ('testdist', '42', '')
+        self.assertEqual(cbutil.get_python_os_info(), ("testdist", "42"))
 
 
 class AtexitRegisterTest(unittest.TestCase):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/certbot-0.39.0/certbot/util.py 
new/certbot-0.40.1/certbot/util.py
--- old/certbot-0.39.0/certbot/util.py  2019-10-01 21:48:40.000000000 +0200
+++ new/certbot-0.40.1/certbot/util.py  2019-11-06 03:24:51.000000000 +0100
@@ -12,9 +12,10 @@
 import re
 import socket
 import subprocess
+import sys
+import warnings
 
 import configargparse
-import distro
 import six
 
 from acme.magic_typing import Tuple, Union  # pylint: disable=unused-import, 
no-name-in-module
@@ -25,6 +26,12 @@
 from certbot.compat import os
 from certbot.compat import filesystem
 
+if sys.platform.startswith('linux'):
+    import distro
+    _USE_DISTRO = True
+else:
+    _USE_DISTRO = False
+
 logger = logging.getLogger(__name__)
 
 
@@ -277,77 +284,59 @@
             logger.debug('Not suggesting name "%s"', name, exc_info=True)
     return filtered_names
 
-
-def get_os_info(filepath="/etc/os-release"):
+def get_os_info():
     """
     Get OS name and version
 
-    :param str filepath: File path of os-release file
     :returns: (os_name, os_version)
     :rtype: `tuple` of `str`
     """
 
-    if os.path.isfile(filepath):
-        # Systemd os-release parsing might be viable
-        os_name, os_version = get_systemd_os_info(filepath=filepath)
-        if os_name:
-            return os_name, os_version
-
-    # Fallback to platform module
-    return get_python_os_info()
-
+    return get_python_os_info(pretty=False)
 
-def get_os_info_ua(filepath="/etc/os-release"):
+def get_os_info_ua():
     """
     Get OS name and version string for User Agent
 
-    :param str filepath: File path of os-release file
     :returns: os_ua
     :rtype: `str`
     """
+    if _USE_DISTRO:
+        os_info = distro.name(pretty=True)
 
-    if os.path.isfile(filepath):
-        os_ua = get_var_from_file("PRETTY_NAME", filepath=filepath)
-        if not os_ua:
-            os_ua = get_var_from_file("NAME", filepath=filepath)
-        if os_ua:
-            return os_ua
-
-    # Fallback
-    return " ".join(get_python_os_info())
-
+    if not _USE_DISTRO or not os_info:
+        return " ".join(get_python_os_info(pretty=True))
+    return os_info
 
-def get_systemd_os_info(filepath="/etc/os-release"):
+def get_systemd_os_info():
     """
     Parse systemd /etc/os-release for distribution information
 
-    :param str filepath: File path of os-release file
     :returns: (os_name, os_version)
     :rtype: `tuple` of `str`
     """
 
-    os_name = get_var_from_file("ID", filepath=filepath)
-    os_version = get_var_from_file("VERSION_ID", filepath=filepath)
-
-    return (os_name, os_version)
+    warnings.warn(
+        "The get_sytemd_os_like() function is deprecated and will be removed 
in "
+        "a future release.", DeprecationWarning, stacklevel=2)
+    return get_os_info()[:2]
 
-
-def get_systemd_os_like(filepath="/etc/os-release"):
+def get_systemd_os_like():
     """
     Get a list of strings that indicate the distribution likeness to
     other distributions.
 
-    :param str filepath: File path of os-release file
     :returns: List of distribution acronyms
     :rtype: `list` of `str`
     """
 
-    return get_var_from_file("ID_LIKE", filepath).split(" ")
-
+    if _USE_DISTRO:
+        return distro.like().split(" ")
+    return []
 
 def get_var_from_file(varname, filepath="/etc/os-release"):
     """
-    Get single value from systemd /etc/os-release
+    Get single value from a file formatted like systemd /etc/os-release
 
     :param str varname: Name of variable to fetch
     :param str filepath: File path of os-release file
@@ -367,7 +356,6 @@
             return _normalize_string(line.strip()[len(var_string):])
     return ""
 
-
 def _normalize_string(orig):
     """
     Helper function for get_var_from_file() to remove quotes
@@ -375,12 +363,13 @@
     """
     return orig.replace('"', '').replace("'", "").strip()
 
-
-def get_python_os_info():
+def get_python_os_info(pretty=False):
     """
     Get Operating System type/distribution and major version
     using python platform module
 
+    :param bool pretty: If the returned OS name should be in longer (pretty) 
form
+
     :returns: (os_name, os_version)
     :rtype: `tuple` of `str`
     """
@@ -391,8 +380,8 @@
     )
     os_type, os_ver, _ = info
     os_type = os_type.lower()
-    if os_type.startswith('linux'):
-        info = _get_linux_distribution()
+    if os_type.startswith('linux') and _USE_DISTRO:
+        info = distro.linux_distribution(pretty)
         # On arch, distro.linux_distribution() is reportedly ('','',''),
         # so handle it defensively
         if info[0]:
@@ -424,14 +413,6 @@
         os_ver = ''
     return os_type, os_ver
 
-def _get_linux_distribution():
-    """Gets the linux distribution name from the underlying OS"""
-
-    try:
-        return platform.linux_distribution()
-    except AttributeError:
-        return distro.linux_distribution()
-
 # Just make sure we don't get pwned... Make sure that it also doesn't
 # start with a period or have two consecutive periods <- this needs to
 # be done in addition to the regex
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/certbot-0.39.0/certbot.egg-info/PKG-INFO 
new/certbot-0.40.1/certbot.egg-info/PKG-INFO
--- old/certbot-0.39.0/certbot.egg-info/PKG-INFO        2019-10-01 
21:48:41.000000000 +0200
+++ new/certbot-0.40.1/certbot.egg-info/PKG-INFO        2019-11-06 
03:24:52.000000000 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: certbot
-Version: 0.39.0
+Version: 0.40.1
 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-0.39.0/certbot.egg-info/requires.txt 
new/certbot-0.40.1/certbot.egg-info/requires.txt
--- old/certbot-0.39.0/certbot.egg-info/requires.txt    2019-10-01 
21:48:41.000000000 +0200
+++ new/certbot-0.40.1/certbot.egg-info/requires.txt    2019-11-06 
03:24:52.000000000 +0100
@@ -1,4 +1,4 @@
-acme>=0.29.0
+acme>=0.40.0
 ConfigArgParse>=0.9.3
 configobj
 cryptography>=1.2.3
@@ -13,7 +13,7 @@
 zope.interface
 
 [:sys_platform == "win32"]
-pywin32>=224
+pywin32>=225
 
 [dev]
 astroid==1.6.5
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/certbot-0.39.0/docs/cli-help.txt 
new/certbot-0.40.1/docs/cli-help.txt
--- old/certbot-0.39.0/docs/cli-help.txt        2019-10-01 21:48:40.000000000 
+0200
+++ new/certbot-0.40.1/docs/cli-help.txt        2019-11-06 03:24:51.000000000 
+0100
@@ -113,7 +113,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/0.38.0
+                        "". (default: CertbotACMEClient/0.40.0
                         (certbot(-auto); OS_NAME OS_VERSION) Authenticator/XXX
                         Installer/YYY (SUBCOMMAND; flags: FLAGS)
                         Py/major.minor.patchlevel). The flags encoded in the
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/certbot-0.39.0/docs/contributing.rst 
new/certbot-0.40.1/docs/contributing.rst
--- old/certbot-0.39.0/docs/contributing.rst    2019-10-01 21:48:40.000000000 
+0200
+++ new/certbot-0.40.1/docs/contributing.rst    2019-11-06 03:24:51.000000000 
+0100
@@ -197,15 +197,20 @@
 Code components and layout
 ==========================
 
+The following components of the Certbot repository are distributed to users:
+
 acme
   contains all protocol specific code
 certbot
   main client code
 certbot-apache and certbot-nginx
   client code to configure specific web servers
-certbot.egg-info
-  configuration for packaging Certbot
-
+certbot-dns-*
+  client code to configure DNS providers
+certbot-auto and letsencrypt-auto
+  shell scripts to install Certbot and its dependencies on UNIX systems
+windows installer
+  Installs Certbot on Windows and is built using the files in 
windows-installer/
 
 Plugin-architecture
 -------------------
@@ -234,7 +239,7 @@
 
 Authenticators are plugins that prove control of a domain name by solving a
 challenge provided by the ACME server. ACME currently defines several types of
-challenges: HTTP, TLS-SNI (deprecated), TLS-ALPR, and DNS, represented by 
classes in `acme.challenges`.
+challenges: HTTP, TLS-ALPN, and DNS, represented by classes in 
`acme.challenges`.
 An authenticator plugin should implement support for at least one challenge 
type.
 
 An Authenticator indicates which challenges it supports by implementing
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/certbot-0.39.0/docs/using.rst 
new/certbot-0.40.1/docs/using.rst
--- old/certbot-0.39.0/docs/using.rst   2019-10-01 21:48:40.000000000 +0200
+++ new/certbot-0.40.1/docs/using.rst   2019-11-06 03:24:51.000000000 +0100
@@ -917,8 +917,9 @@
 of Certbot. Certificate specific configuration choices should be set in the 
``.conf``
 files that can be found in ``/etc/letsencrypt/renewal``.
 
-By default no cli.ini file is created, after creating one
-it is possible to specify the location of this configuration file with
+By default no cli.ini file is created (though it may exist already if you 
installed Certbot
+via a package manager, for instance).
+After creating one it is possible to specify the location of this 
configuration file with
 ``certbot --config cli.ini`` (or shorter ``-c cli.ini``). An
 example configuration file is shown below:
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/certbot-0.39.0/setup.py new/certbot-0.40.1/setup.py
--- old/certbot-0.39.0/setup.py 2019-10-01 21:48:41.000000000 +0200
+++ new/certbot-0.40.1/setup.py 2019-11-06 03:24:52.000000000 +0100
@@ -34,7 +34,7 @@
 # specified here to avoid masking the more specific request requirements in
 # acme. See https://github.com/pypa/pip/issues/988 for more info.
 install_requires = [
-    'acme>=0.29.0',
+    'acme>=0.40.0',
     # We technically need ConfigArgParse 0.10.0 for Python 2.6 support, but
     # saying so here causes a runtime error against our temporary fork of 0.9.3
     # in which we added 2.6 support (see #2243), so we relax the requirement.
@@ -59,7 +59,7 @@
 # However environment markers are supported only with setuptools >= 36.2.
 # So this dependency is not added for old Linux distributions with old 
setuptools,
 # in order to allow these systems to build certbot from sources.
-pywin32_req = 'pywin32>=224'
+pywin32_req = 'pywin32>=225'  # do not forget to edit pywin32 dependency 
accordingly in windows-installer/construct.py
 if StrictVersion(setuptools_version) >= StrictVersion('36.2'):
     install_requires.append(pywin32_req + " ; sys_platform == 'win32'")
 elif 'bdist_wheel' in sys.argv[1:]:


Reply via email to