From e5247ced5e08bd797ce79c5a07a9fbd0d0cfe54c Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Wed, 23 Dec 2015 18:32:20 -0500 Subject: [PATCH 01/24] Include the python version being used in the user agent for ACME requests. --- acme/acme/client.py | 5 ++++- letsencrypt/client.py | 9 +++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/acme/acme/client.py b/acme/acme/client.py index c3e28ef47..cc90b7866 100644 --- a/acme/acme/client.py +++ b/acme/acme/client.py @@ -2,6 +2,7 @@ import datetime import heapq import logging +import platform import time import six @@ -483,6 +484,8 @@ class Client(object): # pylint: disable=too-many-instance-attributes 'Successful revocation must return HTTP OK status') +DEFAULT_USER_AGENT = 'acme-python Python/%s' % platform.python_version() + class ClientNetwork(object): """Client network.""" JSON_CONTENT_TYPE = 'application/json' @@ -490,7 +493,7 @@ class ClientNetwork(object): REPLAY_NONCE_HEADER = 'Replay-Nonce' def __init__(self, key, alg=jose.RS256, verify_ssl=True, - user_agent='acme-python'): + user_agent=DEFAULT_USER_AGENT): self.key = key self.alg = alg self.verify_ssl = verify_ssl diff --git a/letsencrypt/client.py b/letsencrypt/client.py index 080ee7991..b7c4ccab8 100644 --- a/letsencrypt/client.py +++ b/letsencrypt/client.py @@ -1,6 +1,7 @@ """Let's Encrypt client API.""" import logging import os +import platform from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives.asymmetric import rsa @@ -51,9 +52,13 @@ def _determine_user_agent(config): """ if config.user_agent is None: - ua = "LetsEncryptPythonClient/{0} ({1}) Authenticator/{2} Installer/{3}" + ua = ( + "LetsEncryptPythonClient/{0} ({1}) Authenticator/{2} Installer/{3}" + " Python/{4}" + ) ua = ua.format(letsencrypt.__version__, " ".join(le_util.get_os_info()), - config.authenticator, config.installer) + config.authenticator, config.installer, + platform.python_version()) else: ua = config.user_agent return ua From 5376c3074599bbbcf5f44b8fe9127a2f62490d80 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Tue, 5 Jan 2016 07:30:49 -0500 Subject: [PATCH 02/24] Revert changes to the acme directory Only send the Python version in the user agent for the letsencrypt tool. --- acme/acme/client.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/acme/acme/client.py b/acme/acme/client.py index cc90b7866..c3e28ef47 100644 --- a/acme/acme/client.py +++ b/acme/acme/client.py @@ -2,7 +2,6 @@ import datetime import heapq import logging -import platform import time import six @@ -484,8 +483,6 @@ class Client(object): # pylint: disable=too-many-instance-attributes 'Successful revocation must return HTTP OK status') -DEFAULT_USER_AGENT = 'acme-python Python/%s' % platform.python_version() - class ClientNetwork(object): """Client network.""" JSON_CONTENT_TYPE = 'application/json' @@ -493,7 +490,7 @@ class ClientNetwork(object): REPLAY_NONCE_HEADER = 'Replay-Nonce' def __init__(self, key, alg=jose.RS256, verify_ssl=True, - user_agent=DEFAULT_USER_AGENT): + user_agent='acme-python'): self.key = key self.alg = alg self.verify_ssl = verify_ssl From 64a7608956f1cd19924d12312cba6f8f6bcbb94d Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 15 Mar 2017 17:19:00 -0700 Subject: [PATCH 03/24] Add auto-ness to the UA --- certbot/client.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/certbot/client.py b/certbot/client.py index 0f6b35f09..e1d578087 100644 --- a/certbot/client.py +++ b/certbot/client.py @@ -52,9 +52,10 @@ def determine_user_agent(config): """ if config.user_agent is None: - ua = "CertbotACMEClient/{0} ({1}) Authenticator/{2} Installer/{3}" + auto = "" if cli.cli_command == "certbot" else "auto" + ua = "CertbotACMEClient{4}/{0} ({1}) Authenticator/{2} Installer/{3}" ua = ua.format(certbot.__version__, util.get_os_info_ua(), - config.authenticator, config.installer) + config.authenticator, config.installer, auto) else: ua = config.user_agent return ua From 6fa521bc5f82f19f187c870cf633b1a43a6a6001 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 15 Mar 2017 18:02:03 -0700 Subject: [PATCH 04/24] Also report if we're renewing --- certbot/cli.py | 1 + certbot/client.py | 8 +++++--- certbot/main.py | 13 +++++++++---- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/certbot/cli.py b/certbot/cli.py index b9172f21d..abcac6c94 100644 --- a/certbot/cli.py +++ b/certbot/cli.py @@ -526,6 +526,7 @@ class HelpfulArgumentParser(object): parsed_args = self.parser.parse_args(self.args) parsed_args.func = self.VERBS[self.verb] parsed_args.verb = self.verb + parsed_args.renewing = None # an important config property we don't know until later if self.detect_defaults: return parsed_args diff --git a/certbot/client.py b/certbot/client.py index 95de3ccb1..ab13da1f0 100644 --- a/certbot/client.py +++ b/certbot/client.py @@ -54,10 +54,11 @@ def determine_user_agent(config): if config.user_agent is None: auto = "no" if cli.cli_command == "certbot" else "yes" - ua = "CertbotACMEClient/{0} ({1}) Authenticator/{2} Installer/{3} Verb/{4} Auto/{5} Py/{6}" + ua = ("CertbotACMEClient/{0} ({1}) Authenticator/{2} Installer/{3} Verb/{4} " + "Renewing/{5} Auto/{6} Py/{7}") ua = ua.format(certbot.__version__, util.get_os_info_ua(), - config.authenticator, config.installer, config.verb, auto, - platform.python_version()) + config.authenticator, config.installer, config.verb, config.renewing, + auto, platform.python_version()) else: ua = config.user_agent return ua @@ -71,6 +72,7 @@ def sample_user_agent(): self.installer = "YYY" self.user_agent = None self.verb = "SUBCOMMAND" + self.renewing = "YESNOMAYBE" return determine_user_agent(DummyConfig()) diff --git a/certbot/main.py b/certbot/main.py index 1f247a7d6..43cc0b101 100644 --- a/certbot/main.py +++ b/certbot/main.py @@ -225,6 +225,10 @@ def _find_cert(config, domains, certname): RenewableCert instance or None. """ action, lineage = _find_lineage_for_domains_and_certname(config, domains, certname) + if action == "renew": + config.renewing = True + elif action == "newcert": + config.renewing = False if action == "reinstall": logger.info("Keeping the existing certificate") return (action != "reinstall"), lineage @@ -595,12 +599,12 @@ def run(config, plugins): # pylint: disable=too-many-branches,too-many-locals except errors.PluginSelectionError as e: return e.message - # TODO: Handle errors from _init_le_client? - le_client = _init_le_client(config, authenticator, installer) - domains, certname = _find_domains_or_certname(config, installer) should_get_cert, lineage = _find_cert(config, domains, certname) + # TODO: Handle errors from _init_le_client? + le_client = _init_le_client(config, authenticator, installer) + new_lineage = lineage if should_get_cert: new_lineage = _get_and_save_cert(le_client, config, domains, @@ -673,9 +677,9 @@ def certonly(config, plugins): except errors.PluginSelectionError as e: logger.info("Could not choose appropriate plugin: %s", e) raise - le_client = _init_le_client(config, auth, installer) if config.csr: + le_client = _init_le_client(config, auth, installer) cert_path, fullchain_path = _csr_get_and_save_cert(config, le_client) _report_new_cert(config, cert_path, fullchain_path) _suggest_donation_if_appropriate(config) @@ -683,6 +687,7 @@ def certonly(config, plugins): domains, certname = _find_domains_or_certname(config, installer) should_get_cert, lineage = _find_cert(config, domains, certname) + le_client = _init_le_client(config, auth, installer) # run after _find_cert for config.renewing if possible if not should_get_cert: notify = zope.component.getUtility(interfaces.IDisplay).notification From f259a1754944dda291083d3c2155ecb932cc6fb7 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Thu, 16 Mar 2017 01:14:06 -0700 Subject: [PATCH 05/24] Lint --- certbot/cli.py | 2 +- certbot/main.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/certbot/cli.py b/certbot/cli.py index abcac6c94..88b53796b 100644 --- a/certbot/cli.py +++ b/certbot/cli.py @@ -526,7 +526,7 @@ class HelpfulArgumentParser(object): parsed_args = self.parser.parse_args(self.args) parsed_args.func = self.VERBS[self.verb] parsed_args.verb = self.verb - parsed_args.renewing = None # an important config property we don't know until later + parsed_args.renewing = "unknown" # an important config property we don't know until later if self.detect_defaults: return parsed_args diff --git a/certbot/main.py b/certbot/main.py index 43cc0b101..d0dc0721a 100644 --- a/certbot/main.py +++ b/certbot/main.py @@ -687,7 +687,8 @@ def certonly(config, plugins): domains, certname = _find_domains_or_certname(config, installer) should_get_cert, lineage = _find_cert(config, domains, certname) - le_client = _init_le_client(config, auth, installer) # run after _find_cert for config.renewing if possible + # _init_le_client needs to run after _find_cert for config.renewing if possible + le_client = _init_le_client(config, auth, installer) if not should_get_cert: notify = zope.component.getUtility(interfaces.IDisplay).notification From 4846217445af161cd14ed76e3eb6ca8f521080ad Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Fri, 17 Mar 2017 15:20:24 -0700 Subject: [PATCH 06/24] Make config.renewing always a string. --- certbot/main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/certbot/main.py b/certbot/main.py index d0dc0721a..bc61ef568 100644 --- a/certbot/main.py +++ b/certbot/main.py @@ -226,9 +226,9 @@ def _find_cert(config, domains, certname): """ action, lineage = _find_lineage_for_domains_and_certname(config, domains, certname) if action == "renew": - config.renewing = True + config.renewing = "Yes" elif action == "newcert": - config.renewing = False + config.renewing = "No" if action == "reinstall": logger.info("Keeping the existing certificate") return (action != "reinstall"), lineage From a313eebc7fca0b1985b270e7ebf365034e2a2b0f Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 5 Apr 2017 11:54:23 -0700 Subject: [PATCH 07/24] Conform to RFC 1945 --- certbot/client.py | 13 ++++++++----- certbot/main.py | 4 ++-- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/certbot/client.py b/certbot/client.py index ab13da1f0..db5590e00 100644 --- a/certbot/client.py +++ b/certbot/client.py @@ -53,12 +53,15 @@ def determine_user_agent(config): """ if config.user_agent is None: - auto = "no" if cli.cli_command == "certbot" else "yes" - ua = ("CertbotACMEClient/{0} ({1}) Authenticator/{2} Installer/{3} Verb/{4} " - "Renewing/{5} Auto/{6} Py/{7}") - ua = ua.format(certbot.__version__, util.get_os_info_ua(), + if cli.cli_command in ["certbot", "letsencrypt", "certbot-auto", "letsencrypt-auto"]: + auto = cli.cli_command + else: + auto = "other" + ua = ("CertbotACMEClient/{0} ({1}; {2}) Authenticator/{3} Installer/{4} " + "({5}; renewing:{6}) Py/{7}") + ua = ua.format(certbot.__version__, auto, util.get_os_info_ua(), config.authenticator, config.installer, config.verb, config.renewing, - auto, platform.python_version()) + platform.python_version()) else: ua = config.user_agent return ua diff --git a/certbot/main.py b/certbot/main.py index bc61ef568..6b6ff06b2 100644 --- a/certbot/main.py +++ b/certbot/main.py @@ -226,9 +226,9 @@ def _find_cert(config, domains, certname): """ action, lineage = _find_lineage_for_domains_and_certname(config, domains, certname) if action == "renew": - config.renewing = "Yes" + config.renewing = "yes" elif action == "newcert": - config.renewing = "No" + config.renewing = "no" if action == "reinstall": logger.info("Keeping the existing certificate") return (action != "reinstall"), lineage From 2f0ec5c38804619139ca69a7d87fc89d42e50b15 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 5 Apr 2017 12:40:26 -0700 Subject: [PATCH 08/24] Set renewing: correctly for the "renew" case. --- certbot/main.py | 1 + certbot/renewal.py | 1 + 2 files changed, 2 insertions(+) diff --git a/certbot/main.py b/certbot/main.py index ee178aa50..448ed21d5 100644 --- a/certbot/main.py +++ b/certbot/main.py @@ -696,6 +696,7 @@ def certonly(config, plugins): def renew(config, unused_plugins): """Renew previously-obtained certificates.""" try: + config.renewing = "yes" renewal.handle_renewal_request(config) finally: hooks.run_saved_post_hooks() diff --git a/certbot/renewal.py b/certbot/renewal.py index 6eb171763..7b651a92a 100644 --- a/certbot/renewal.py +++ b/certbot/renewal.py @@ -393,6 +393,7 @@ def handle_renewal_request(config): # elements from within the renewal configuration file). try: renewal_candidate = _reconstitute(lineage_config, renewal_file) + renewal_candidate.renewing = "yes" except Exception as e: # pylint: disable=broad-except logger.warning("Renewal configuration file %s produced an " "unexpected error: %s. Skipping.", renewal_file, e) From 23a2ecb36e931fd62cfbde53c3e5d3f8cdb7d1ca Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 5 Apr 2017 15:28:14 -0700 Subject: [PATCH 09/24] Fixup --- certbot/renewal.py | 1 - 1 file changed, 1 deletion(-) diff --git a/certbot/renewal.py b/certbot/renewal.py index 7b651a92a..6eb171763 100644 --- a/certbot/renewal.py +++ b/certbot/renewal.py @@ -393,7 +393,6 @@ def handle_renewal_request(config): # elements from within the renewal configuration file). try: renewal_candidate = _reconstitute(lineage_config, renewal_file) - renewal_candidate.renewing = "yes" except Exception as e: # pylint: disable=broad-except logger.warning("Renewal configuration file %s produced an " "unexpected error: %s. Skipping.", renewal_file, e) From c35ca9775b8058b4b6acad11ffc1ca060fa07b03 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Thu, 6 Apr 2017 14:41:40 -0700 Subject: [PATCH 10/24] tweak comment --- certbot/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/certbot/cli.py b/certbot/cli.py index 0efec4352..14e3adf6d 100644 --- a/certbot/cli.py +++ b/certbot/cli.py @@ -532,7 +532,7 @@ class HelpfulArgumentParser(object): parsed_args = self.parser.parse_args(self.args) parsed_args.func = self.VERBS[self.verb] parsed_args.verb = self.verb - parsed_args.renewing = "unknown" # an important config property we don't know until later + parsed_args.renewing = "unknown" # used for the User Agent; not known until later if self.detect_defaults: return parsed_args From cb66ba95e1502741e8f97e21f53265093244c9b5 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Thu, 13 Apr 2017 20:00:31 -0700 Subject: [PATCH 11/24] Revert "Also report if we're renewing" This reverts commit 6fa521bc5f82f19f187c870cf633b1a43a6a6001. --- certbot/client.py | 5 ++--- certbot/main.py | 14 ++++---------- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/certbot/client.py b/certbot/client.py index f42911921..1c0b71e20 100644 --- a/certbot/client.py +++ b/certbot/client.py @@ -59,9 +59,9 @@ def determine_user_agent(config): else: auto = "other" ua = ("CertbotACMEClient/{0} ({1}; {2}) Authenticator/{3} Installer/{4} " - "({5}; renewing:{6}) Py/{7}") + "({5}) Py/{7}") ua = ua.format(certbot.__version__, auto, util.get_os_info_ua(), - config.authenticator, config.installer, config.verb, config.renewing, + config.authenticator, config.installer, config.verb, platform.python_version()) else: ua = config.user_agent @@ -76,7 +76,6 @@ def sample_user_agent(): self.installer = "YYY" self.user_agent = None self.verb = "SUBCOMMAND" - self.renewing = "YESNOMAYBE" return determine_user_agent(DummyConfig()) diff --git a/certbot/main.py b/certbot/main.py index 448ed21d5..e9bf3b9ed 100644 --- a/certbot/main.py +++ b/certbot/main.py @@ -215,10 +215,6 @@ def _find_cert(config, domains, certname): RenewableCert instance or None. """ action, lineage = _find_lineage_for_domains_and_certname(config, domains, certname) - if action == "renew": - config.renewing = "yes" - elif action == "newcert": - config.renewing = "no" if action == "reinstall": logger.info("Keeping the existing certificate") return (action != "reinstall"), lineage @@ -590,12 +586,12 @@ def run(config, plugins): # pylint: disable=too-many-branches,too-many-locals except errors.PluginSelectionError as e: return e.message - domains, certname = _find_domains_or_certname(config, installer) - should_get_cert, lineage = _find_cert(config, domains, certname) - # TODO: Handle errors from _init_le_client? le_client = _init_le_client(config, authenticator, installer) + domains, certname = _find_domains_or_certname(config, installer) + should_get_cert, lineage = _find_cert(config, domains, certname) + new_lineage = lineage if should_get_cert: new_lineage = _get_and_save_cert(le_client, config, domains, @@ -668,9 +664,9 @@ def certonly(config, plugins): except errors.PluginSelectionError as e: logger.info("Could not choose appropriate plugin: %s", e) raise + le_client = _init_le_client(config, auth, installer) if config.csr: - le_client = _init_le_client(config, auth, installer) cert_path, fullchain_path = _csr_get_and_save_cert(config, le_client) _report_new_cert(config, cert_path, fullchain_path) _suggest_donation_if_appropriate(config) @@ -678,8 +674,6 @@ def certonly(config, plugins): domains, certname = _find_domains_or_certname(config, installer) should_get_cert, lineage = _find_cert(config, domains, certname) - # _init_le_client needs to run after _find_cert for config.renewing if possible - le_client = _init_le_client(config, auth, installer) if not should_get_cert: notify = zope.component.getUtility(interfaces.IDisplay).notification From 5be0811bdcf707624ab020036ef8834952f7ca20 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Thu, 13 Apr 2017 20:07:03 -0700 Subject: [PATCH 12/24] Replace removed "renewing" with potential future substitutes :/ --- certbot/client.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/certbot/client.py b/certbot/client.py index 1c0b71e20..c450d19e7 100644 --- a/certbot/client.py +++ b/certbot/client.py @@ -58,10 +58,13 @@ def determine_user_agent(config): auto = cli.cli_command else: auto = "other" + flags = "dup" if config.duplicate else "" + flags += "force" if config.renew_by_default else "" + ua = ("CertbotACMEClient/{0} ({1}; {2}) Authenticator/{3} Installer/{4} " - "({5}) Py/{7}") + "({5}; flags: {6}) Py/{7}") ua = ua.format(certbot.__version__, auto, util.get_os_info_ua(), - config.authenticator, config.installer, config.verb, + config.authenticator, config.installer, config.verb, flags, platform.python_version()) else: ua = config.user_agent From c6da818d93d905f4bd456b31e6082718dab6fe31 Mon Sep 17 00:00:00 2001 From: Noah Swartz Date: Mon, 24 Apr 2017 11:54:20 -0700 Subject: [PATCH 13/24] fix the word plugin --- docs/using.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/using.rst b/docs/using.rst index 7eaa92f84..1bcf1483b 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -182,7 +182,7 @@ The ``http`` challenge will ask you to place a file with a specific name and specific content in the ``/.well-known/acme-challenge/`` directory directly in the top-level directory (“web root”) containing the files served by your webserver. In essence it's the same as the webroot_ plugin, but not automated. -When using the ``dns`` plugin, ``certbot`` will ask you to place a TXT DNS +When using the ``dns`` challenge, ``certbot`` will ask you to place a TXT DNS record with specific contents under the domain name consisting of the hostname for which you want a certificate issued, prepended by ``_acme-challenge``. From 419c541b1848e4ba9a9a1ae5b29946f1c8ae30b2 Mon Sep 17 00:00:00 2001 From: Kubilay Kocak Date: Tue, 25 Apr 2017 04:58:00 +1000 Subject: [PATCH 14/24] Update FreeBSD package entries (#4538) Add port/package URL for py-acme Use Freshports URL's instead of SVNWeb (repository) links as they provide (binary) package installation installation as well as further port/package information such as vulnerabilities, revision history and bug reports for each port respectively. --- docs/packaging.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/packaging.rst b/docs/packaging.rst index 5b37c2e24..a60ce72d5 100644 --- a/docs/packaging.rst +++ b/docs/packaging.rst @@ -68,7 +68,8 @@ In Fedora 23+. FreeBSD ------- -- https://svnweb.freebsd.org/ports/head/security/py-certbot/ +- https://www.freshports.org/security/py-acme/ +- https://www.freshports.org/security/py-certbot/ Gentoo ------ From b41472afce8d96761e8ab14d04931337635635b7 Mon Sep 17 00:00:00 2001 From: Zach Shepherd Date: Mon, 24 Apr 2017 17:36:00 -0700 Subject: [PATCH 15/24] Enhance display.util to support input validation (#4372) * display: support validation of user input To avoid each caller of `display.input` and `display.directory_select` needing to implement validation logic, this allows for a validator to be supplied as a part of the call. Following the existing pattern from `webroot`, this validator is expected to throw a `Error` when it encounters invalid input. The user then receives a `notification` is re-prompted. Testing Done: * tox -e py27 * tox -e lint * plugins: update webroot to use display's validation functionality This change updates the webroot plugin to use the now-built-in validation functionality in display, reducing duplicated code. Testing Done: * tox -e py27 * tox -e lint * display: move validation logic to ops To avoid adding complexity to `IDisplay` methods, move validation logic to helper methods in `display.ops`. Testing Done: * tox -e py27 * tox -e lint --- certbot/display/ops.py | 57 ++++++++++++++++++++++++ certbot/display/util.py | 1 - certbot/plugins/webroot.py | 28 +++++------- certbot/plugins/webroot_test.py | 18 +++----- certbot/tests/display/ops_test.py | 74 ++++++++++++++++++++++++++++++- 5 files changed, 148 insertions(+), 30 deletions(-) diff --git a/certbot/display/ops.py b/certbot/display/ops.py index 4bbf9e5b8..ffd4947ad 100644 --- a/certbot/display/ops.py +++ b/certbot/display/ops.py @@ -289,3 +289,60 @@ def _gen_https_names(domains): domains[-1]) return "" + + +def _get_validated(method, validator, message, default=None, **kwargs): + if default is not None: + try: + validator(default) + except errors.Error as error: + logger.debug('Encountered invalid default value "%s" when prompting for "%s"', + default, + message, + exc_info=True) + raise AssertionError('Invalid default "{0}"'.format(default)) + + while True: + code, raw = method(message, default=default, **kwargs) + if code == display_util.OK: + try: + validator(raw) + return code, raw + except errors.Error as error: + logger.debug('Validator rejected "%s" when prompting for "%s"', + raw, + message, + exc_info=True) + zope.component.getUtility(interfaces.IDisplay).notification(str(error), pause=False) + else: + return code, raw + + +def validated_input(validator, *args, **kwargs): + """Like `~certbot.interfaces.IDisplay.input`, but with validation. + + :param callable validator: A method which will be called on the + supplied input. If the method raises a `errors.Error`, its + text will be displayed and the user will be re-prompted. + :param list *args: Arguments to be passed to `~certbot.interfaces.IDisplay.input` + :param dict **kwargs: Arguments to be passed to `~certbot.interfaces.IDisplay.input` + :return: as `~certbot.interfaces.IDisplay.input` + :rtype: tuple + """ + return _get_validated(zope.component.getUtility(interfaces.IDisplay).input, + validator, *args, **kwargs) + + +def validated_directory(validator, *args, **kwargs): + """Like `~certbot.interfaces.IDisplay.directory_select`, but with validation. + + :param callable validator: A method which will be called on the + supplied input. If the method raises a `errors.Error`, its + text will be displayed and the user will be re-prompted. + :param list *args: Arguments to be passed to `~certbot.interfaces.IDisplay.directory_select` + :param dict **kwargs: Arguments to be passed to `~certbot.interfaces.IDisplay.directory_select` + :return: as `~certbot.interfaces.IDisplay.directory_select` + :rtype: tuple + """ + return _get_validated(zope.component.getUtility(interfaces.IDisplay).directory_select, + validator, *args, **kwargs) diff --git a/certbot/display/util.py b/certbot/display/util.py index 87c75739b..4d69f1263 100644 --- a/certbot/display/util.py +++ b/certbot/display/util.py @@ -123,7 +123,6 @@ class FileDisplay(object): def input(self, message, default=None, cli_flag=None, force_interactive=False, **unused_kwargs): - # pylint: disable=no-self-use """Accept input from the user. :param str message: message to display to the user diff --git a/certbot/plugins/webroot.py b/certbot/plugins/webroot.py index b3ec4a692..aad6ffc82 100644 --- a/certbot/plugins/webroot.py +++ b/certbot/plugins/webroot.py @@ -16,6 +16,7 @@ from certbot import cli from certbot import errors from certbot import interfaces from certbot.display import util as display_util +from certbot.display import ops from certbot.plugins import common @@ -136,22 +137,17 @@ to serve all files under specified web root ({0}).""" return None if index == 0 else known_webroots[index - 1] def _prompt_for_new_webroot(self, domain): - display = zope.component.getUtility(interfaces.IDisplay) - - while True: - code, webroot = display.directory_select( - "Input the webroot for {0}:".format(domain), - force_interactive=True) - if code == display_util.HELP: - # Displaying help is not currently implemented - return None - elif code == display_util.CANCEL: - return None - else: # code == display_util.OK - try: - return _validate_webroot(webroot) - except errors.PluginError as error: - display.notification(str(error), pause=False) + code, webroot = ops.validated_directory( + _validate_webroot, + "Input the webroot for {0}:".format(domain), + force_interactive=True) + if code == display_util.HELP: + # Displaying help is not currently implemented + return None + elif code == display_util.CANCEL or code == display_util.ESC: + return None + else: # code == display_util.OK + return _validate_webroot(webroot) def _create_challenge_dirs(self): path_map = self.conf("map") diff --git a/certbot/plugins/webroot_test.py b/certbot/plugins/webroot_test.py index 3e9a68b84..53809764b 100644 --- a/certbot/plugins/webroot_test.py +++ b/certbot/plugins/webroot_test.py @@ -100,22 +100,16 @@ class AuthenticatorTest(unittest.TestCase): self.config.webroot_path = [] self.config.webroot_map = {} - imaginary_dir = os.path.join(os.sep, "imaginary", "dir") - mock_display = mock_get_utility() mock_display.menu.return_value = (display_util.OK, 0,) - mock_display.directory_select.side_effect = ( - (display_util.HELP, -1,), (display_util.CANCEL, -1,), - (display_util.OK, imaginary_dir,), (display_util.OK, self.path,),) - self.auth.perform([self.achall]) + with mock.patch('certbot.display.ops.validated_directory') as m: + m.side_effect = ((display_util.HELP, -1), + (display_util.CANCEL, -1), + (display_util.OK, self.path,)) - self.assertTrue(mock_display.notification.called) - for call in mock_display.notification.call_args_list: - self.assertTrue(imaginary_dir in call[0][0]) + self.auth.perform([self.achall]) - self.assertTrue(mock_display.directory_select.called) - for call in mock_display.directory_select.call_args_list: - self.assertTrue(self.achall.domain in call[0][0]) + self.assertEqual(self.config.webroot_map[self.achall.domain], self.path) def test_perform_missing_root(self): self.config.webroot_path = None diff --git a/certbot/tests/display/ops_test.py b/certbot/tests/display/ops_test.py index 89cd9e43d..306b95841 100644 --- a/certbot/tests/display/ops_test.py +++ b/certbot/tests/display/ops_test.py @@ -14,6 +14,7 @@ from certbot import account from certbot import errors from certbot.display import util as display_util +from certbot.display import ops import certbot.tests.util as test_util @@ -112,7 +113,6 @@ class ChooseAccountTest(test_util.TempDirTestCase): @classmethod def _call(cls, accounts): - from certbot.display import ops return ops.choose_account(accounts) @test_util.patch_get_utility("certbot.display.ops.z_util") @@ -405,5 +405,77 @@ class SuccessRevocationTest(unittest.TestCase): os.linesep), pause=False) self.assertTrue(path in mock_util().notification.call_args[0][0]) + +class ValidatorTests(unittest.TestCase): + """Tests for `validated_input` and `validated_directory`.""" + + __ERROR = "Must be non-empty" + + valid_input = "asdf" + valid_directory = "/var/www/html" + + @staticmethod + def __validator(m): + if m == "": + raise errors.PluginError(ValidatorTests.__ERROR) + + @test_util.patch_get_utility() + def test_input_blank_with_validator(self, mock_util): + mock_util().input.side_effect = [(display_util.OK, ""), + (display_util.OK, ""), + (display_util.OK, ""), + (display_util.OK, self.valid_input)] + + returned = ops.validated_input(self.__validator, "message", force_interactive=True) + self.assertEqual(ValidatorTests.__ERROR, mock_util().notification.call_args[0][0]) + self.assertEqual(returned, (display_util.OK, self.valid_input)) + + @test_util.patch_get_utility() + def test_input_validation_with_default(self, mock_util): + mock_util().input.side_effect = [(display_util.OK, self.valid_input)] + + returned = ops.validated_input(self.__validator, "msg", default="other") + self.assertEqual(returned, (display_util.OK, self.valid_input)) + + @test_util.patch_get_utility() + def test_input_validation_with_bad_default(self, mock_util): + mock_util().input.side_effect = [(display_util.OK, self.valid_input)] + + self.assertRaises(AssertionError, + ops.validated_input, + self.__validator, "msg", default="") + + @test_util.patch_get_utility() + def test_input_cancel_with_validator(self, mock_util): + mock_util().input.side_effect = [(display_util.CANCEL, "")] + + code, unused_raw = ops.validated_input(self.__validator, "message", force_interactive=True) + self.assertEqual(code, display_util.CANCEL) + + @test_util.patch_get_utility() + def test_directory_select_validation(self, mock_util): + mock_util().directory_select.side_effect = [(display_util.OK, ""), + (display_util.OK, self.valid_directory)] + + returned = ops.validated_directory(self.__validator, "msg", force_interactive=True) + self.assertEqual(ValidatorTests.__ERROR, mock_util().notification.call_args[0][0]) + self.assertEqual(returned, (display_util.OK, self.valid_directory)) + + @test_util.patch_get_utility() + def test_directory_select_validation_with_default(self, mock_util): + mock_util().directory_select.side_effect = [(display_util.OK, self.valid_directory)] + + returned = ops.validated_directory(self.__validator, "msg", default="other") + self.assertEqual(returned, (display_util.OK, self.valid_directory)) + + @test_util.patch_get_utility() + def test_directory_select_validation_with_bad_default(self, mock_util): + mock_util().directory_select.side_effect = [(display_util.OK, self.valid_directory)] + + self.assertRaises(AssertionError, + ops.validated_directory, + self.__validator, "msg", default="") + + if __name__ == "__main__": unittest.main() # pragma: no cover From 0c4e813a72fcf3ad3f1d90dd5a8890748c2248fe Mon Sep 17 00:00:00 2001 From: schoen Date: Wed, 26 Apr 2017 10:54:40 -0700 Subject: [PATCH 16/24] Demote PEM generation to logger.debug (#4549) --- certbot/crypto_util.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/certbot/crypto_util.py b/certbot/crypto_util.py index 5671341e8..7de173568 100644 --- a/certbot/crypto_util.py +++ b/certbot/crypto_util.py @@ -54,13 +54,13 @@ def init_save_key(key_size, key_dir, keyname="key-certbot.pem"): config.strict_permissions) if config.dry_run: key_path = None - logger.info("Generating key (%d bits), not saving to file", key_size) + logger.debug("Generating key (%d bits), not saving to file", key_size) else: key_f, key_path = util.unique_file( os.path.join(key_dir, keyname), 0o600, "wb") with key_f: key_f.write(key_pem) - logger.info("Generating key (%d bits): %s", key_size, key_path) + logger.debug("Generating key (%d bits): %s", key_size, key_path) return util.Key(key_path, key_pem) @@ -89,13 +89,13 @@ def init_save_csr(privkey, names, path): config.strict_permissions) if config.dry_run: csr_filename = None - logger.info("Creating CSR: not saving to file") + logger.debug("Creating CSR: not saving to file") else: csr_f, csr_filename = util.unique_file( os.path.join(path, "csr-certbot.pem"), 0o644, "wb") with csr_f: csr_f.write(csr_pem) - logger.info("Creating CSR: %s", csr_filename) + logger.debug("Creating CSR: %s", csr_filename) return util.CSR(csr_filename, csr_pem, "pem") From 5f9c6539d5a4e277770f30865ca4d162bad03cf8 Mon Sep 17 00:00:00 2001 From: Noah Swartz Date: Wed, 26 Apr 2017 14:57:23 -0700 Subject: [PATCH 17/24] make a list of contributors (#4508) * make a list of contributors * make all links websites * alphebetize and remove extra file * remove ref to contributors * add one more! * sort using linux sort command --- AUTHORS.md | 259 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 259 insertions(+) create mode 100644 AUTHORS.md diff --git a/AUTHORS.md b/AUTHORS.md new file mode 100644 index 000000000..6ee739bc0 --- /dev/null +++ b/AUTHORS.md @@ -0,0 +1,259 @@ +Authors +======= + +* [Aaron Zirbes](https://github.com/aaronzirbes) +* Aaron Zuehlke +* Ada Lovelace +* [Adam Woodbeck](https://github.com/awoodbeck) +* [Aidin Gharibnavaz](https://github.com/aidin36) +* [AJ ONeal](https://github.com/coolaj86) +* [Alcaro](https://github.com/Alcaro) +* [Alexander Mankuta](https://github.com/pointlessone) +* [Alex Bowers](https://github.com/alexbowers) +* [Alex Conlin](https://github.com/alexconlin) +* [Alex Gaynor](https://github.com/alex) +* [Alex Halderman](https://github.com/jhalderm) +* [Alex Jordan](https://github.com/strugee) +* [Amjad Mashaal](https://github.com/TheNavigat) +* [Andrew Murray](https://github.com/radarhere) +* [Anselm Levskaya](https://github.com/levskaya) +* [Antoine Jacoutot](https://github.com/ajacoutot) +* [asaph](https://github.com/asaph) +* [Axel Beckert](https://github.com/xtaran) +* [Bas](https://github.com/Mechazawa) +* [benbankes](https://github.com/benbankes) +* [Ben Irving](https://github.com/benileo) +* [Benjamin Kerensa](https://github.com/bkerensa) +* [Benjamin Neff](https://github.com/SuperTux88) +* [Benjamin Piouffle](https://github.com/Betree) +* [Ben Ubois](https://github.com/benubois) +* [Ben Wolfe](https://github.com/bwolfe) +* [Bigfish](https://github.com/bwolfe) +* [Blake Griffith](https://github.com/cowlicks) +* [Brad Warren](https://github.com/bmw) +* [Brandon Kraft](https://github.com/kraftbj) +* [Brandon Kreisel](https://github.com/kraftbj) +* [Ceesjan Luiten](https://github.com/quinox) +* [Chad Whitacre](https://github.com/whit537) +* [Chhatoi Pritam Baral](https://github.com/pritambaral) +* [Chris Johns](https://github.com/ter0) +* [Chris Lamb](https://github.com/lamby) +* [chrismarget](https://github.com/chrismarget) +* [Christian Gärtner](https://github.com/ChristianGaertner) +* [Christian Rosentreter](https://github.com/the-real-tokai) +* [Christopher Brown](https://github.com/chbrown) +* [Christopher Manning](https://github.com/christophermanning) +* [Christoph Kisfeld](https://github.com/chk1) +* [Clif Houck](https://github.com/ClifHouck) +* [Cooper Quintin](https://github.com/cooperq) +* [Corey Farwell](https://github.com/frewsxcv) +* [Craig Smith](https://github.com/dashaxiong) +* [Damian Poddebniak](https://github.com/duesee) +* [Damien Nozay](https://github.com/dnozay) +* [Damien Tournoud](https://github.com/damz) +* [DanCld](https://github.com/DanCld) +* [Daniel Albers](https://github.com/AID) +* [Daniel Aleksandersen](https://github.com/da2x) +* [Daniel Convissor](https://github.com/convissor) +* [Daniel Huang](https://github.com/dhuang) +* [Dave Guarino](https://github.com/daguar) +* [David cz](https://github.com/dave-cz) +* [David Dworken](https://github.com/ddworken) +* [David Kreitschmann](https://kreitschmann.de) +* [David Xia](https://github.com/davidxia) +* [Devin Howard](https://github.com/devvmh) +* [dokazaki](https://github.com/dokazaki) +* [Dominic Cleal](https://github.com/domcleal) +* [Dominic Lüchinger](https://github.com/dol) +* [Douglas José](https://github.com/douglasjose) +* [Erica Portnoy](https://github.com/ohemorange) +* [Eric Engestrom](https://github.com/1ace) +* [Eric Rescorla](https://github.com/ekr) +* [Eric Wustrow](https://github.com/ewust) +* [Erik Rose](https://github.com/erikrose) +* [Eugene Kazakov](https://github.com/xgin) +* [Fabian](https://github.com/faerbit) +* [Faidon Liambotis](https://github.com/paravoid) +* [Fan Jiang](https://github.com/tcz001) +* [Felix Schwarz](https://github.com/FelixSchwarz) +* [Felix Yan](https://github.com/felixonmars) +* [Filip Ochnik](https://github.com/filipochnik) +* [Francois Marier](https://github.com/fmarier) +* [Frank](https://github.com/Frankkkkk) +* [Frederic BLANC](https://github.com/fblanc) +* [Garrett Robinson](https://github.com/garrettr) +* [Gene Wood](https://github.com/gene1wood) +* [Geoffroy Doucet](https://www.geoffroydoucet.com) +* [Gian Carlo Pace](https://github.com/gicappa) +* [Gilles Pietri](https://github.com/gilou) +* [Giovanni Pellerano](https://github.com/evilaliv3) +* [Giovanni Toraldo](https://github.com/gionn) +* [Gordin](https://github.com/Gordin) +* [Gregor Dschung](https://github.com/chkpnt) +* [Gregory L. Dietsche](https://github.com/farmergreg) +* [Greg Osuri](https://github.com/gosuri) +* [Guillaume Boudreau](https://github.com/gboudreau) +* [Harlan Lieberman-Berg](https://github.com/hlieberman) +* [Henri Salo](https://github.com/fgeek) +* [Henry Chen](https://github.com/henrychen95) +* [Ingolf Becker](https://github.com/watercrossing) +* [Jaap Eldering](https://github.com/eldering) +* [Jacob Hoffman-Andrews](https://github.com/jsha) +* [Jacob Sachs](https://github.com/jsachs) +* [Jairo Llopis](https://github.com/Yajo) +* [Jakub Warmuz](https://github.com/kuba) +* [James Kasten](https://github.com/jdkasten) +* [Jason Grinblat](https://github.com/ptychomancer) +* [Jay Faulkner](https://github.com/jayofdoom) +* [J.C. Jones](https://github.com/jcjones) +* [Jeff Hodges](https://github.com/jmhodges) +* [Jeremy Gillula](https://github.com/jgillula) +* [Jeroen Ketelaar](https://github.com/JKetelaar) +* [Jeroen Pluimers](https://github.com/jpluimers) +* [j](https://github.com/bit) +* [Jim Tittsler](https://github.com/jimt) +* [Joe Ranweiler](https://github.com/ranweiler) +* [Joerg Sonnenberger](https://github.com/jsonn) +* [John Leach](https://github.com/johnl) +* [John Reed](https://github.com/leerspace) +* [Jonas Berlin](https://github.com/xkr47) +* [Jonathan Herlin](https://github.com/Jonher937) +* [Jon Walsh](https://github.com/code-tree) +* [Joona Hoikkala](https://github.com/joohoi) +* [Josh Soref](https://github.com/jsoref) +* [Joubin Jabbari](https://github.com/joubin) +* [Juho Juopperi](https://github.com/jkjuopperi) +* [Kane York](https://github.com/riking) +* [Kenneth Skovhede](https://github.com/kenkendk) +* [Kevin Burke](https://github.com/kevinburke) +* [Kevin London](https://github.com/kevinlondon) +* [Kubilay Kocak](https://github.com/koobs) +* [LeCoyote](https://github.com/LeCoyote) +* [Lee Watson](https://github.com/TheReverend403) +* [Leo Famulari](https://github.com/lfam) +* [lf](https://github.com/lf-) +* [Liam Marshall](https://github.com/liamim) +* [Lior Sabag](https://github.com/liorsbg) +* [Lipis](https://github.com/lipis) +* [lord63](https://github.com/lord63) +* [Luca Beltrame](https://github.com/lbeltrame) +* [Luca Ebach](https://github.com/lucebac) +* [Luca Olivetti](https://github.com/olivluca) +* [Luke Rogers](https://github.com/lukeroge) +* [Maarten](https://github.com/mrtndwrd) +* [Maikel Martens](https://github.com/krukas) +* [Malte Janduda](https://github.com/MalteJ) +* [Mantas Mikulėnas](https://github.com/grawity) +* [Marcel Krüger](https://github.com/zauguin) +* [Marcos Caceres](https://github.com/marcoscaceres) +* [Marek Viger](https://github.com/freezy-sk) +* [Mario Villaplana](https://github.com/supermari0) +* [Marius Gedminas](https://github.com/mgedmin) +* [Martey Dodoo](https://github.com/martey) +* [Martijn Bastiaan](https://github.com/martijnbastiaan) +* [Martijn Braam](https://github.com/MartijnBraam) +* [Martin Brugger](https://github.com/mbrugger) +* [Mathieu Leduc-Hamel](https://github.com/mlhamel) +* [Matt Bostock](https://github.com/mattbostock) +* [Matthew Ames](https://github.com/SuperMatt) +* [Michael Schumacher](https://github.com/schumaml) +* [Michael Strache](https://github.com/Jarodiv) +* [Michael Sverdlin](https://github.com/sveder) +* [Michal Moravec](https://github.com/https://github.com/Majkl578) +* [Michal Papis](https://github.com/mpapis) +* [Minn Soe](https://github.com/MinnSoe) +* [Min RK](https://github.com/minrk) +* [Miquel Ruiz](https://github.com/miquelruiz) +* [Môshe van der Sterre](https://github.com/moshevds) +* [mrstanwell](https://github.com/mrstanwell) +* [Nav Aulakh](https://github.com/Nav) +* [Nelson Elhage](https://github.com/nelhage) +* [Nick Fong](https://github.com/nickfong) +* [Nick Le Mouton](https://github.com/NoodlesNZ) +* [Nikos Roussos](https://github.com/comzeradd) +* [Noah Swartz](https://github.com/swartzcr) +* [Ola Bini](https://github.com/olabini) +* [Ondřej Súkup](https://github.com/mimi1vx) +* [Ondřej Surý](https://github.com/oerdnj) +* [osirisinferi](https://github.com/osirisinferi) +* Patrick Figel +* [Patrick Heppler](https://github.com/PatrickHeppler) +* [Paul Feitzinger](https://github.com/pfeyz) +* [Pavan Gupta](https://github.com/pavgup) +* [Pavel Pavlov](https://github.com/ghost355) +* [Peter Conrad](https://github.com/pconrad-fb) +* [Peter Eckersley](https://github.com/pde) +* [Peter Mosmans](https://github.com/PeterMosmans) +* [Philippe Langlois](https://github.com/langloisjp) +* [Philipp Spitzer](https://github.com/spitza) +* [Piero Steinger](https://github.com/Jadaw1n) +* [Pierre Jaury](https://github.com/kaiyou) +* [Piotr Kasprzyk](https://github.com/kwadrat) +* [Prayag Verma](https://github.com/pra85) +* [Reinaldo de Souza Jr](https://github.com/juniorz) +* [Remi Rampin](https://github.com/remram44) +* [Rémy HUBSCHER](https://github.com/Natim) +* [Rémy Léone](https://github.com/sieben) +* [Richard Barnes](https://github.com/r-barnes) +* [Richard Panek](https://github.com/kernelpanek) +* [Robert Buchholz](https://github.com/rbu) +* [Robert Habermann](https://github.com/frennkie) +* [Robert Xiao](https://github.com/nneonneo) +* [Roland Shoemaker](https://github.com/rolandshoemaker) +* [Roy Wellington Ⅳ](https://github.com/thanatos) +* [rugk](https://github.com/rugk) +* [Sachi King](https://github.com/nakato) +* [Sagi Kedmi](https://github.com/sagi) +* [Sam Lanning](https://github.com/samlanning) +* [sapics](https://github.com/sapics) +* [Scott Barr](https://github.com/scottjbarr) +* [Scott Merrill](https://github.com/skpy) +* [Sebastian Bögl](https://github.com/TheBoegl) +* [Sebastian Wagner](https://github.com/sebix) +* [sedrubal](https://github.com/sedrubal) +* [Seppe Stas](https://github.com/seppestas) +* [Sergey Nuzdhin](https://github.com/lwolf) +* [Seth Schoen](https://github.com/schoen) +* [Sharif Nassar](https://github.com/mrwacky42) +* [Shaun Cummiskey](https://github.com/ampersign) +* [Shiloh Heurich](https://github.com/sheurich) +* [silverwind](https://github.com/silverwind) +* [Sorvani](https://github.com/sorvani) +* [Spencer Bliven](https://github.com/sbliven) +* [Stacey Sheldon](https://github.com/solidgoldbomb) +* [Stavros Korokithakis](https://github.com/skorokithakis) +* [Stefan Weil](https://github.com/stweil) +* [Steve Desmond](https://github.com/stevedesmond-ca) +* [Tan Jay Jun](https://github.com/jayjun) +* [Tapple Gao](https://github.com/tapple) +* [Telepenin Nikolay](https://github.com/telepenin) +* [Thomas Cottier](https://github.com/tcottier-enalean) +* [Thomas Mayer](https://github.com/thomaszbz) +* [Thomas Waldmann](https://github.com/ThomasWaldmann) +* [Thom Wiggers](https://github.com/thomwiggers) +* [Till Maas](https://github.com/tyll) +* [Timothy Guan-tin Chien](https://github.com/timdream) +* [Torsten Bögershausen](https://github.com/tboegi) +* [Travis Raines](https://github.com/rainest) +* [Trung Ngo](https://github.com/Ngo-The-Trung) +* [Valentin](https://github.com/e00E) +* [venyii](https://github.com/venyii) +* [Viktor Szakats](https://github.com/vszakats) +* [Ville Skyttä](https://github.com/scop) +* [Vinney Cavallo](https://github.com/vcavallo) +* [Vladimir Rutsky](https://github.com/rutsky) +* [Wang Yu](https://github.com/wyhitcs) +* [Ward Vandewege](https://github.com/cure) +* [Whyfoo](https://github.com/whyfoo) +* [Wilfried Teiken](https://github.com/wteiken) +* [Willem Fibbe](https://github.com/fibbers) +* [William Budington](https://github.com/Hainish) +* [Will Newby](https://github.com/willnewby) +* [Will Oller](https://github.com/willoller) +* [Yan](https://github.com/diracdeltas) +* [Yen Chi Hsuan](https://github.com/yan12125) +* [Yomna](https://github.com/ynasser) +* [Yoni Jah](https://github.com/yonjah) +* [YourDaddyIsHere](https://github.com/YourDaddyIsHere) +* [Zach Shepherd](https://github.com/zjs) +* [陈三](https://github.com/chenxsan) From 1611df4120bc6ed5bbbe9202070b52c6d2db0741 Mon Sep 17 00:00:00 2001 From: ohemorange Date: Wed, 26 Apr 2017 18:44:06 -0700 Subject: [PATCH 18/24] Allow empty nginx blocks (#4555) * modify test config file to allow valid config that fails to parse in parser.py * make failing tests pass by fixing the problem --- certbot-nginx/certbot_nginx/parser.py | 2 +- certbot-nginx/certbot_nginx/tests/parser_test.py | 6 +++--- .../certbot_nginx/tests/testdata/etc_nginx/nginx.conf | 3 +++ 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/certbot-nginx/certbot_nginx/parser.py b/certbot-nginx/certbot_nginx/parser.py index 6f3f344db..9f1a08b3b 100644 --- a/certbot-nginx/certbot_nginx/parser.py +++ b/certbot-nginx/certbot_nginx/parser.py @@ -110,7 +110,7 @@ class NginxParser(object): srv = servers[filename] # workaround undefined loop var in lambdas # Find all the server blocks - _do_for_subarray(tree, lambda x: x[0] == ['server'], + _do_for_subarray(tree, lambda x: len(x) >= 2 and x[0] == ['server'], lambda x, y: srv.append((x[1], y))) # Find 'include' statements in server blocks and append their trees diff --git a/certbot-nginx/certbot_nginx/tests/parser_test.py b/certbot-nginx/certbot_nginx/tests/parser_test.py index 9c2b8656e..8a8bd0ff1 100644 --- a/certbot-nginx/certbot_nginx/tests/parser_test.py +++ b/certbot-nginx/certbot_nginx/tests/parser_test.py @@ -125,13 +125,13 @@ class NginxParserTest(util.NginxTest): False, True, set(['localhost', r'~^(www\.)?(example|bar)\.']), - [], [9, 1, 9]) + [], [10, 1, 9]) vhost2 = obj.VirtualHost(nparser.abs_path('nginx.conf'), [obj.Addr('somename', '8080', False, False), obj.Addr('', '8000', False, False)], False, True, set(['somename', 'another.alias', 'alias']), - [], [9, 1, 12]) + [], [10, 1, 12]) vhost3 = obj.VirtualHost(nparser.abs_path('sites-enabled/example.com'), [obj.Addr('69.50.225.155', '9000', False, False), @@ -186,7 +186,7 @@ class NginxParserTest(util.NginxTest): None, None, None, set(['localhost', r'~^(www\.)?(example|bar)\.']), - None, [9, 1, 9]) + None, [10, 1, 9]) nparser.add_server_directives(mock_vhost, [['foo', 'bar'], ['\n ', 'ssl_certificate', ' ', '/etc/ssl/cert.pem']], diff --git a/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/nginx.conf b/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/nginx.conf index 0af503e6b..ccce4dc1b 100644 --- a/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/nginx.conf +++ b/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/nginx.conf @@ -14,6 +14,9 @@ events { worker_connections 1024; } +empty { +} + include foo.conf; http { From 72fa27514e1b212fa59635ecc05acedffaa631fb Mon Sep 17 00:00:00 2001 From: yomna Date: Thu, 27 Apr 2017 10:46:33 -0700 Subject: [PATCH 19/24] fix for issue 4132: increasing server_names_hash_bucket_size if necessary (#4496) * increases server_names_hash_bucket_size if it's too low in your nginx conf * switching from k,v pairwise indices -> inner_line * simply using bucket_directive --- certbot-nginx/certbot_nginx/tls_sni_01.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/certbot-nginx/certbot_nginx/tls_sni_01.py b/certbot-nginx/certbot_nginx/tls_sni_01.py index 2e8125911..347d9f21f 100644 --- a/certbot-nginx/certbot_nginx/tls_sni_01.py +++ b/certbot-nginx/certbot_nginx/tls_sni_01.py @@ -100,9 +100,13 @@ class NginxTlsSni01(common.TLSSNI01): if line[0] == ['http']: body = line[1] found_bucket = False + posn = 0 for inner_line in body: if inner_line[0] == bucket_directive[1]: + if int(inner_line[1]) < int(bucket_directive[3]): + body[posn] = bucket_directive found_bucket = True + posn += 1 if not found_bucket: body.insert(0, bucket_directive) if include_directive not in body: From 89af460792fcdfb23c7dc4f9fcdec1bfa07a2656 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Fri, 28 Apr 2017 15:03:50 -0700 Subject: [PATCH 20/24] Reuse dynamic install_requires. (#4554) * Revert "Make argparse dependency unconditional. (#2249)" This reverts commit 8f101034960ffc1e47879314585898efda234e60. * Update comment about environment markers --- acme/setup.py | 5 ++++- setup.py | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/acme/setup.py b/acme/setup.py index a640ae6bb..1c887e5d3 100644 --- a/acme/setup.py +++ b/acme/setup.py @@ -8,7 +8,6 @@ version = '0.14.0.dev0' # Please update tox.ini when modifying dependency version requirements install_requires = [ - 'argparse', # load_pem_private/public_key (>=0.6) # rsa_recover_prime_factors (>=0.8) 'cryptography>=0.8', @@ -28,6 +27,10 @@ install_requires = [ 'six', ] +# env markers cause problems with older pip and setuptools +if sys.version_info < (2, 7): + install_requires.append('argparse') + dev_extras = [ 'nose', 'tox', diff --git a/setup.py b/setup.py index 0e8d19a22..d2c092a05 100644 --- a/setup.py +++ b/setup.py @@ -36,7 +36,6 @@ version = meta['version'] # https://github.com/pypa/pip/issues/988 for more info. install_requires = [ 'acme=={0}'.format(version), - 'argparse', # 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. @@ -56,6 +55,10 @@ install_requires = [ 'zope.interface', ] +# env markers cause problems with older pip and setuptools +if sys.version_info < (2, 7): + install_requires.append('argparse') + dev_extras = [ # Pin astroid==1.3.5, pylint==1.4.2 as a workaround for #289 'astroid==1.3.5', From 8fa12bef8e7c687400367cf45a022b43e8c92754 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Fri, 28 Apr 2017 16:06:45 -0700 Subject: [PATCH 21/24] Tell the world we're Python 3 compatible (#4568) * Mention python 3 support in setup.py * Build universal (py2 and py3 compatible) wheels * Mention Python 3.3+ support in docs * we work on python 3.6 too --- acme/setup.py | 1 + certbot-apache/setup.cfg | 2 ++ certbot-apache/setup.py | 5 +++++ certbot-compatibility-test/setup.cfg | 2 ++ certbot-compatibility-test/setup.py | 5 +++++ certbot-nginx/setup.cfg | 2 ++ certbot-nginx/setup.py | 5 +++++ docs/install.rst | 4 ++-- letshelp-certbot/setup.cfg | 2 ++ letshelp-certbot/setup.py | 5 +++++ setup.cfg | 3 +++ setup.py | 5 +++++ 12 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 certbot-apache/setup.cfg create mode 100644 certbot-compatibility-test/setup.cfg create mode 100644 certbot-nginx/setup.cfg create mode 100644 letshelp-certbot/setup.cfg diff --git a/acme/setup.py b/acme/setup.py index 1c887e5d3..cb12df51f 100644 --- a/acme/setup.py +++ b/acme/setup.py @@ -62,6 +62,7 @@ setup( 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', 'Topic :: Internet :: WWW/HTTP', 'Topic :: Security', ], diff --git a/certbot-apache/setup.cfg b/certbot-apache/setup.cfg new file mode 100644 index 000000000..2a9acf13d --- /dev/null +++ b/certbot-apache/setup.cfg @@ -0,0 +1,2 @@ +[bdist_wheel] +universal = 1 diff --git a/certbot-apache/setup.py b/certbot-apache/setup.py index 9a473c584..19f0c74f8 100644 --- a/certbot-apache/setup.py +++ b/certbot-apache/setup.py @@ -42,6 +42,11 @@ setup( 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.3', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', 'Topic :: Internet :: WWW/HTTP', 'Topic :: Security', 'Topic :: System :: Installation/Setup', diff --git a/certbot-compatibility-test/setup.cfg b/certbot-compatibility-test/setup.cfg new file mode 100644 index 000000000..2a9acf13d --- /dev/null +++ b/certbot-compatibility-test/setup.cfg @@ -0,0 +1,2 @@ +[bdist_wheel] +universal = 1 diff --git a/certbot-compatibility-test/setup.py b/certbot-compatibility-test/setup.py index aecae329f..dfd05bfd9 100644 --- a/certbot-compatibility-test/setup.py +++ b/certbot-compatibility-test/setup.py @@ -42,6 +42,11 @@ setup( 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.3', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', 'Topic :: Internet :: WWW/HTTP', 'Topic :: Security', ], diff --git a/certbot-nginx/setup.cfg b/certbot-nginx/setup.cfg new file mode 100644 index 000000000..2a9acf13d --- /dev/null +++ b/certbot-nginx/setup.cfg @@ -0,0 +1,2 @@ +[bdist_wheel] +universal = 1 diff --git a/certbot-nginx/setup.py b/certbot-nginx/setup.py index 786b5a1a1..63b6f16af 100644 --- a/certbot-nginx/setup.py +++ b/certbot-nginx/setup.py @@ -42,6 +42,11 @@ setup( 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.3', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', 'Topic :: Internet :: WWW/HTTP', 'Topic :: Security', 'Topic :: System :: Installation/Setup', diff --git a/docs/install.rst b/docs/install.rst index 6c56584be..a1e91c010 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -22,8 +22,8 @@ your system. System Requirements =================== -Certbot currently requires Python 2.6 or 2.7. By default, it requires root -access in order to write to ``/etc/letsencrypt``, +Certbot currently requires Python 2.6, 2.7, or 3.3+. By default, it requires +root access in order to write to ``/etc/letsencrypt``, ``/var/log/letsencrypt``, ``/var/lib/letsencrypt``; to bind to ports 80 and 443 (if you use the ``standalone`` plugin) and to read and modify webserver configurations (if you use the ``apache`` or ``nginx`` plugins). If none of diff --git a/letshelp-certbot/setup.cfg b/letshelp-certbot/setup.cfg new file mode 100644 index 000000000..2a9acf13d --- /dev/null +++ b/letshelp-certbot/setup.cfg @@ -0,0 +1,2 @@ +[bdist_wheel] +universal = 1 diff --git a/letshelp-certbot/setup.py b/letshelp-certbot/setup.py index b26ab41fe..3ce442b3e 100644 --- a/letshelp-certbot/setup.py +++ b/letshelp-certbot/setup.py @@ -33,6 +33,11 @@ setup( 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.3', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', 'Topic :: Internet :: WWW/HTTP', 'Topic :: Security', 'Topic :: System :: Installation/Setup', diff --git a/setup.cfg b/setup.cfg index 8d68bac30..3b4dbaf87 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,3 +1,6 @@ +[bdist_wheel] +universal = 1 + [easy_install] zip_ok = false diff --git a/setup.py b/setup.py index d2c092a05..b0cf57810 100644 --- a/setup.py +++ b/setup.py @@ -97,6 +97,11 @@ setup( 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.3', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', 'Topic :: Internet :: WWW/HTTP', 'Topic :: Security', 'Topic :: System :: Installation/Setup', From 0a4ee306a9de6a74d49964921204699e51809d7f Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Fri, 28 Apr 2017 17:56:10 -0700 Subject: [PATCH 22/24] Fix UA flag setting (and set more of them) --- certbot/cli.py | 4 +++- certbot/client.py | 45 ++++++++++++++++++++++++++++++++++----------- 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/certbot/cli.py b/certbot/cli.py index 14e3adf6d..82c9622f0 100644 --- a/certbot/cli.py +++ b/certbot/cli.py @@ -1108,7 +1108,9 @@ def _create_subparsers(helpful): "plugin and use case, and to know when to deprecate support for past Python " "versions. If you wish to hide this information from the Let's " 'Encrypt server, set this to "". ' - '(default: {0})'.format(sample_user_agent())) + '(default: {0}). The flags encoded in the user agent are: ' + '--duplicate, --force-renew, --allow-subset-of-names, -n, and ' + 'whether any hooks are set.'.format(sample_user_agent())) helpful.add("certonly", "--csr", type=read_file, help="Path to a Certificate Signing Request (CSR) in DER or PEM format." diff --git a/certbot/client.py b/certbot/client.py index c450d19e7..ad7323e62 100644 --- a/certbot/client.py +++ b/certbot/client.py @@ -58,27 +58,50 @@ def determine_user_agent(config): auto = cli.cli_command else: auto = "other" - flags = "dup" if config.duplicate else "" - flags += "force" if config.renew_by_default else "" ua = ("CertbotACMEClient/{0} ({1}; {2}) Authenticator/{3} Installer/{4} " "({5}; flags: {6}) Py/{7}") ua = ua.format(certbot.__version__, auto, util.get_os_info_ua(), - config.authenticator, config.installer, config.verb, flags, - platform.python_version()) + config.authenticator, config.installer, config.verb, + ua_flags(config), platform.python_version()) else: ua = config.user_agent return ua +def ua_flags(config): + "Turn some very important CLI flags into clues in the user agent." + if isinstance(config, DummyConfig): + return "FLAGS" + flags = [] + if config.duplicate: + flags.append("dup") + if config.renew_by_default: + flags.append("frn") + if config.allow_subset_of_names: + flags.append("asn") + if config.noninteractive_mode: + flags.append("n") + hook_names = ("pre", "post", "renew", "manual_auth", "manual_cleanup") + hooks = [getattr(config, h + "_hook") for h in hook_names] + if any(hooks): + flags.append("hook") + return " ".join(flags) + +class DummyConfig(object): + "Shim for computing a sample user agent." + def __init__(self): + self.authenticator = "XXX" + self.installer = "YYY" + self.user_agent = None + self.verb = "SUBCOMMAND" + + def __getattr__(self, name): + "Any config properties we might have are None." + return None + def sample_user_agent(): "Document what this Certbot's user agent string will be like." - class DummyConfig(object): - "Shim for computing a sample user agent." - def __init__(self): - self.authenticator = "XXX" - self.installer = "YYY" - self.user_agent = None - self.verb = "SUBCOMMAND" + return determine_user_agent(DummyConfig()) From f6c02728e4348a88fbca2acbacb2a8b55315b772 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Fri, 28 Apr 2017 18:37:58 -0700 Subject: [PATCH 23/24] Address review comments --- certbot/cli.py | 3 +-- certbot/client.py | 7 +------ 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/certbot/cli.py b/certbot/cli.py index 82c9622f0..9e8778ceb 100644 --- a/certbot/cli.py +++ b/certbot/cli.py @@ -532,7 +532,6 @@ class HelpfulArgumentParser(object): parsed_args = self.parser.parse_args(self.args) parsed_args.func = self.VERBS[self.verb] parsed_args.verb = self.verb - parsed_args.renewing = "unknown" # used for the User Agent; not known until later if self.detect_defaults: return parsed_args @@ -1106,7 +1105,7 @@ def _create_subparsers(helpful): help="Set a custom user agent string for the client. User agent strings allow " "the CA to collect high level statistics about success rates by OS, " "plugin and use case, and to know when to deprecate support for past Python " - "versions. If you wish to hide this information from the Let's " + "versions and flags. If you wish to hide this information from the Let's " 'Encrypt server, set this to "". ' '(default: {0}). The flags encoded in the user agent are: ' '--duplicate, --force-renew, --allow-subset-of-names, -n, and ' diff --git a/certbot/client.py b/certbot/client.py index ad7323e62..dca6d8618 100644 --- a/certbot/client.py +++ b/certbot/client.py @@ -54,14 +54,9 @@ def determine_user_agent(config): """ if config.user_agent is None: - if cli.cli_command in ["certbot", "letsencrypt", "certbot-auto", "letsencrypt-auto"]: - auto = cli.cli_command - else: - auto = "other" - ua = ("CertbotACMEClient/{0} ({1}; {2}) Authenticator/{3} Installer/{4} " "({5}; flags: {6}) Py/{7}") - ua = ua.format(certbot.__version__, auto, util.get_os_info_ua(), + ua = ua.format(certbot.__version__, cli.cli_command, util.get_os_info_ua(), config.authenticator, config.installer, config.verb, ua_flags(config), platform.python_version()) else: From 72b6179e0eb2519b04d0c58dea1a1005d8ac5889 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Fri, 28 Apr 2017 18:45:42 -0700 Subject: [PATCH 24/24] Remove vestigial thingy --- certbot/main.py | 1 - 1 file changed, 1 deletion(-) diff --git a/certbot/main.py b/certbot/main.py index e9bf3b9ed..256503d4e 100644 --- a/certbot/main.py +++ b/certbot/main.py @@ -690,7 +690,6 @@ def certonly(config, plugins): def renew(config, unused_plugins): """Renew previously-obtained certificates.""" try: - config.renewing = "yes" renewal.handle_renewal_request(config) finally: hooks.run_saved_post_hooks()