From 31fef196c0ed57a5cc6b1c4d409ff8097afbc716 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Sun, 27 Sep 2015 01:15:35 +0000 Subject: [PATCH 01/78] --help is effectively a verb for CLI purposes... --- letsencrypt/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 7cb4a0458..711ac0048 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -679,7 +679,7 @@ def create_parser(plugins, args): # For now unfortunately this constant just needs to match the code below; # there isn't an elegant way to autogenerate it in time. VERBS = ["run", "auth", "install", "revoke", "rollback", "config_changes", - "plugins"] + "plugins", "--help"] def _create_subparsers(helpful): From 001d37f9650d0c5f7521673f9fd075a7bd662b0c Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Sun, 27 Sep 2015 02:41:55 +0000 Subject: [PATCH 02/78] "-h" is also a ver. --- letsencrypt/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index a5968ec9c..2c996cd3e 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -679,7 +679,7 @@ def create_parser(plugins, args): # For now unfortunately this constant just needs to match the code below; # there isn't an elegant way to autogenerate it in time. VERBS = ["run", "auth", "install", "revoke", "rollback", "config_changes", - "plugins", "--help"] + "plugins", "--help", "-h"] def _create_subparsers(helpful): From f3c2a096b54950e5368907b698c488a458180961 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Sun, 27 Sep 2015 02:48:44 +0000 Subject: [PATCH 03/78] Move the verb/subcommand to the end of the argparse line --- letsencrypt/cli.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 2c996cd3e..8afbf023f 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -495,13 +495,14 @@ class HelpfulArgumentParser(object): def preprocess_args(self, args): """Work around some limitations in argparse. - Currently, add the default verb "run" as a default. + Currently: add the default verb "run" as a default, and ensure that the + subcommand / verb comes last. """ - - for token in args: + for i,token in enumerate(args): if token in VERBS: - return args - return ["run"] + args + reordered = args[:i] + args[i+1:] + [args[i]] + return reordered + return args + ["run"] def prescan_for_flag(self, flag, possible_arguments): """Checks cli input for flags. From 746016be6cfd17b8c5f19ab45eed7d2e1c11ac22 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sat, 26 Sep 2015 22:27:04 +0000 Subject: [PATCH 04/78] Sync version strings to 0.1.0.dev0, pin same repo deps. --- acme/setup.py | 3 +++ letsencrypt-apache/setup.py | 7 +++++-- letsencrypt-compatibility-test/setup.py | 9 ++++++--- letsencrypt-nginx/setup.py | 7 +++++-- letsencrypt/__init__.py | 2 +- letshelp-letsencrypt/setup.py | 7 +++++-- setup.py | 5 +++-- 7 files changed, 28 insertions(+), 12 deletions(-) diff --git a/acme/setup.py b/acme/setup.py index 60f97844b..bec7feb25 100644 --- a/acme/setup.py +++ b/acme/setup.py @@ -4,6 +4,8 @@ from setuptools import setup from setuptools import find_packages +version = '0.1.0.dev0' + install_requires = [ # load_pem_private/public_key (>=0.6) # rsa_recover_prime_factors (>=0.8) @@ -34,6 +36,7 @@ testing_extras = [ setup( name='acme', + version=version, packages=find_packages(), install_requires=install_requires, extras_require={ diff --git a/letsencrypt-apache/setup.py b/letsencrypt-apache/setup.py index 57d2f6b47..5ac27f4fe 100644 --- a/letsencrypt-apache/setup.py +++ b/letsencrypt-apache/setup.py @@ -2,9 +2,11 @@ from setuptools import setup from setuptools import find_packages +version = '0.1.0.dev0' + install_requires = [ - 'acme', - 'letsencrypt', + 'acme=={0}'.format(version), + 'letsencrypt=={0}'.format(version), 'mock<1.1.0', # py26 'python-augeas', 'setuptools', # pkg_resources @@ -14,6 +16,7 @@ install_requires = [ setup( name='letsencrypt-apache', + version=version, packages=find_packages(), install_requires=install_requires, entry_points={ diff --git a/letsencrypt-compatibility-test/setup.py b/letsencrypt-compatibility-test/setup.py index f02041e55..8d4bbda30 100644 --- a/letsencrypt-compatibility-test/setup.py +++ b/letsencrypt-compatibility-test/setup.py @@ -2,10 +2,12 @@ from setuptools import setup from setuptools import find_packages +version = '0.1.0.dev0' + install_requires = [ - 'letsencrypt', - 'letsencrypt-apache', - 'letsencrypt-nginx', + 'letsencrypt=={0}'.format(version), + 'letsencrypt-apache=={0}'.format(version), + 'letsencrypt-nginx=={0}'.format(version), 'docker-py', 'mock<1.1.0', # py26 'zope.interface', @@ -13,6 +15,7 @@ install_requires = [ setup( name='letsencrypt-compatibility-test', + version=version, packages=find_packages(), install_requires=install_requires, entry_points={ diff --git a/letsencrypt-nginx/setup.py b/letsencrypt-nginx/setup.py index b4ef69505..0131f26cd 100644 --- a/letsencrypt-nginx/setup.py +++ b/letsencrypt-nginx/setup.py @@ -2,9 +2,11 @@ from setuptools import setup from setuptools import find_packages +version = '0.1.0.dev0' + install_requires = [ - 'acme', - 'letsencrypt', + 'acme=={0}'.format(version), + 'letsencrypt=={0}'.format(version), 'mock<1.1.0', # py26 'PyOpenSSL', 'pyparsing>=1.5.5', # Python3 support; perhaps unnecessary? @@ -14,6 +16,7 @@ install_requires = [ setup( name='letsencrypt-nginx', + version=version, packages=find_packages(), install_requires=install_requires, entry_points={ diff --git a/letsencrypt/__init__.py b/letsencrypt/__init__.py index 560191bf1..e59dedeac 100644 --- a/letsencrypt/__init__.py +++ b/letsencrypt/__init__.py @@ -1,4 +1,4 @@ """Let's Encrypt client.""" # version number like 1.2.3a0, must have at least 2 parts, like 1.2 -__version__ = "0.1" +__version__ = "0.1.0.dev0" diff --git a/letshelp-letsencrypt/setup.py b/letshelp-letsencrypt/setup.py index 5e7542411..a228fcf09 100644 --- a/letshelp-letsencrypt/setup.py +++ b/letshelp-letsencrypt/setup.py @@ -4,8 +4,10 @@ from setuptools import setup from setuptools import find_packages +version = "0.1.0.dev0" + install_requires = [ - 'setuptools', # pkg_resources + "setuptools", # pkg_resources ] if sys.version_info < (2, 7): install_requires.append("mock<1.1.0") @@ -14,10 +16,11 @@ else: setup( name="letshelp-letsencrypt", + version=version, packages=find_packages(), install_requires=install_requires, entry_points={ - 'console_scripts': [ + "console_scripts": [ "letshelp-letsencrypt-apache = letshelp_letsencrypt.apache:main", ], }, diff --git a/setup.py b/setup.py index c568d2872..b753a8253 100644 --- a/setup.py +++ b/setup.py @@ -28,9 +28,10 @@ meta = dict(re.findall(r"""__([a-z]+)__ = "([^"]+)""", read_file(init_fn))) readme = read_file(os.path.join(here, 'README.rst')) changes = read_file(os.path.join(here, 'CHANGES.rst')) +version = meta['version'] install_requires = [ - 'acme', + 'acme=={0}'.format(version), 'ConfigArgParse', 'configobj', 'cryptography>=0.7', # load_pem_x509_certificate @@ -75,7 +76,7 @@ testing_extras = [ setup( name='letsencrypt', - version=meta['version'], + version=version, description="Let's Encrypt", long_description=readme, # later: + '\n\n' + changes author="Let's Encrypt Project", From 20131de9fb63eeb7197c0067c4598219834e9bd6 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sat, 26 Sep 2015 22:37:50 +0000 Subject: [PATCH 05/78] Add licences to all subpackages. --- MANIFEST.in | 1 + acme/LICENSE.txt | 190 +++++++++++++++++++++ acme/MANIFEST.in | 1 + acme/setup.py | 1 + letsencrypt-apache/LICENSE.txt | 190 +++++++++++++++++++++ letsencrypt-apache/MANIFEST.in | 1 + letsencrypt-apache/setup.py | 1 + letsencrypt-compatibility-test/LICENSE.txt | 190 +++++++++++++++++++++ letsencrypt-compatibility-test/MANIFEST.in | 1 + letsencrypt-compatibility-test/setup.py | 1 + letsencrypt-nginx/LICENSE.txt | 190 +++++++++++++++++++++ letsencrypt-nginx/MANIFEST.in | 1 + letsencrypt-nginx/setup.py | 1 + letshelp-letsencrypt/LICENSE.txt | 190 +++++++++++++++++++++ letshelp-letsencrypt/MANIFEST.in | 1 + letshelp-letsencrypt/setup.py | 1 + 16 files changed, 961 insertions(+) create mode 100644 acme/LICENSE.txt create mode 100644 letsencrypt-apache/LICENSE.txt create mode 100644 letsencrypt-compatibility-test/LICENSE.txt create mode 100644 letsencrypt-nginx/LICENSE.txt create mode 100644 letshelp-letsencrypt/LICENSE.txt diff --git a/MANIFEST.in b/MANIFEST.in index 530044212..80fd8777e 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -2,6 +2,7 @@ include requirements.txt include README.rst include CHANGES.rst include CONTRIBUTING.md +include LICENSE.txt include linter_plugin.py include letsencrypt/EULA recursive-include letsencrypt/tests/testdata * diff --git a/acme/LICENSE.txt b/acme/LICENSE.txt new file mode 100644 index 000000000..7c13afb9d --- /dev/null +++ b/acme/LICENSE.txt @@ -0,0 +1,190 @@ + Copyright 2015 Internet Security Research Group + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/acme/MANIFEST.in b/acme/MANIFEST.in index f3444f746..4a68ae096 100644 --- a/acme/MANIFEST.in +++ b/acme/MANIFEST.in @@ -1 +1,2 @@ +include LICENSE.txt recursive-include acme/testdata * diff --git a/acme/setup.py b/acme/setup.py index bec7feb25..80f8c2387 100644 --- a/acme/setup.py +++ b/acme/setup.py @@ -37,6 +37,7 @@ testing_extras = [ setup( name='acme', version=version, + license='Apache License 2.0', packages=find_packages(), install_requires=install_requires, extras_require={ diff --git a/letsencrypt-apache/LICENSE.txt b/letsencrypt-apache/LICENSE.txt new file mode 100644 index 000000000..7c13afb9d --- /dev/null +++ b/letsencrypt-apache/LICENSE.txt @@ -0,0 +1,190 @@ + Copyright 2015 Internet Security Research Group + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/letsencrypt-apache/MANIFEST.in b/letsencrypt-apache/MANIFEST.in index aac2bfb36..0a2a07efd 100644 --- a/letsencrypt-apache/MANIFEST.in +++ b/letsencrypt-apache/MANIFEST.in @@ -1,2 +1,3 @@ +include LICENSE.txt recursive-include letsencrypt_apache/tests/testdata * include letsencrypt_apache/options-ssl-apache.conf diff --git a/letsencrypt-apache/setup.py b/letsencrypt-apache/setup.py index 5ac27f4fe..a3dd6989d 100644 --- a/letsencrypt-apache/setup.py +++ b/letsencrypt-apache/setup.py @@ -17,6 +17,7 @@ install_requires = [ setup( name='letsencrypt-apache', version=version, + license='Apache License 2.0', packages=find_packages(), install_requires=install_requires, entry_points={ diff --git a/letsencrypt-compatibility-test/LICENSE.txt b/letsencrypt-compatibility-test/LICENSE.txt new file mode 100644 index 000000000..7c13afb9d --- /dev/null +++ b/letsencrypt-compatibility-test/LICENSE.txt @@ -0,0 +1,190 @@ + Copyright 2015 Internet Security Research Group + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/letsencrypt-compatibility-test/MANIFEST.in b/letsencrypt-compatibility-test/MANIFEST.in index a6aa14443..50a02378f 100644 --- a/letsencrypt-compatibility-test/MANIFEST.in +++ b/letsencrypt-compatibility-test/MANIFEST.in @@ -1 +1,2 @@ +include LICENSE.txt recursive-include letsencrypt_compatibility_test/testdata * diff --git a/letsencrypt-compatibility-test/setup.py b/letsencrypt-compatibility-test/setup.py index 8d4bbda30..e46a05a07 100644 --- a/letsencrypt-compatibility-test/setup.py +++ b/letsencrypt-compatibility-test/setup.py @@ -16,6 +16,7 @@ install_requires = [ setup( name='letsencrypt-compatibility-test', version=version, + license='Apache License 2.0', packages=find_packages(), install_requires=install_requires, entry_points={ diff --git a/letsencrypt-nginx/LICENSE.txt b/letsencrypt-nginx/LICENSE.txt new file mode 100644 index 000000000..7c13afb9d --- /dev/null +++ b/letsencrypt-nginx/LICENSE.txt @@ -0,0 +1,190 @@ + Copyright 2015 Internet Security Research Group + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/letsencrypt-nginx/MANIFEST.in b/letsencrypt-nginx/MANIFEST.in index 94f85e40f..03457a674 100644 --- a/letsencrypt-nginx/MANIFEST.in +++ b/letsencrypt-nginx/MANIFEST.in @@ -1,2 +1,3 @@ +include LICENSE.txt recursive-include letsencrypt_nginx/tests/testdata * include letsencrypt_nginx/options-ssl-nginx.conf diff --git a/letsencrypt-nginx/setup.py b/letsencrypt-nginx/setup.py index 0131f26cd..9d7d246b7 100644 --- a/letsencrypt-nginx/setup.py +++ b/letsencrypt-nginx/setup.py @@ -17,6 +17,7 @@ install_requires = [ setup( name='letsencrypt-nginx', version=version, + license='Apache License 2.0', packages=find_packages(), install_requires=install_requires, entry_points={ diff --git a/letshelp-letsencrypt/LICENSE.txt b/letshelp-letsencrypt/LICENSE.txt new file mode 100644 index 000000000..7c13afb9d --- /dev/null +++ b/letshelp-letsencrypt/LICENSE.txt @@ -0,0 +1,190 @@ + Copyright 2015 Internet Security Research Group + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/letshelp-letsencrypt/MANIFEST.in b/letshelp-letsencrypt/MANIFEST.in index 61a3d3150..9173a6b57 100644 --- a/letshelp-letsencrypt/MANIFEST.in +++ b/letshelp-letsencrypt/MANIFEST.in @@ -1 +1,2 @@ +include LICENSE.txt recursive-include letshelp-letsencrypt/testdata * diff --git a/letshelp-letsencrypt/setup.py b/letshelp-letsencrypt/setup.py index a228fcf09..6582ba59c 100644 --- a/letshelp-letsencrypt/setup.py +++ b/letshelp-letsencrypt/setup.py @@ -17,6 +17,7 @@ else: setup( name="letshelp-letsencrypt", version=version, + license="Apache License 2.0", packages=find_packages(), install_requires=install_requires, entry_points={ From b5036e36ad928522e263db3b4398109167e30ddd Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sat, 26 Sep 2015 22:49:46 +0000 Subject: [PATCH 06/78] Unify setup.py: description/url/author/author_email. --- acme/setup.py | 5 +++++ letsencrypt-apache/setup.py | 5 +++++ letsencrypt-compatibility-test/setup.py | 5 +++++ letsencrypt-nginx/setup.py | 5 +++++ letshelp-letsencrypt/setup.py | 5 +++++ setup.py | 5 +++-- 6 files changed, 28 insertions(+), 2 deletions(-) diff --git a/acme/setup.py b/acme/setup.py index 80f8c2387..f3afe97c3 100644 --- a/acme/setup.py +++ b/acme/setup.py @@ -37,7 +37,12 @@ testing_extras = [ setup( name='acme', version=version, + description='ACME protocol implementation', + url='https://github.com/letsencrypt/letsencrypt', + author="Let's Encrypt Project", + author_email='client-dev@letsencrypt.org', license='Apache License 2.0', + packages=find_packages(), install_requires=install_requires, extras_require={ diff --git a/letsencrypt-apache/setup.py b/letsencrypt-apache/setup.py index a3dd6989d..035a10f4c 100644 --- a/letsencrypt-apache/setup.py +++ b/letsencrypt-apache/setup.py @@ -17,7 +17,12 @@ install_requires = [ setup( name='letsencrypt-apache', version=version, + description="Apache plugin for Let's Encrypt client", + url='https://github.com/letsencrypt/letsencrypt', + author="Let's Encrypt Project", + author_email='client-dev@letsencrypt.org', license='Apache License 2.0', + packages=find_packages(), install_requires=install_requires, entry_points={ diff --git a/letsencrypt-compatibility-test/setup.py b/letsencrypt-compatibility-test/setup.py index e46a05a07..485752e64 100644 --- a/letsencrypt-compatibility-test/setup.py +++ b/letsencrypt-compatibility-test/setup.py @@ -16,7 +16,12 @@ install_requires = [ setup( name='letsencrypt-compatibility-test', version=version, + description="Compatibility tests for Let's Encrypt client", + url='https://github.com/letsencrypt/letsencrypt', + author="Let's Encrypt Project", + author_email='client-dev@letsencrypt.org', license='Apache License 2.0', + packages=find_packages(), install_requires=install_requires, entry_points={ diff --git a/letsencrypt-nginx/setup.py b/letsencrypt-nginx/setup.py index 9d7d246b7..0a6d9646e 100644 --- a/letsencrypt-nginx/setup.py +++ b/letsencrypt-nginx/setup.py @@ -17,7 +17,12 @@ install_requires = [ setup( name='letsencrypt-nginx', version=version, + description="Nginx plugin for Let's Encrypt client", + url='https://github.com/letsencrypt/letsencrypt', + author="Let's Encrypt Project", + author_email='client-dev@letsencrypt.org', license='Apache License 2.0', + packages=find_packages(), install_requires=install_requires, entry_points={ diff --git a/letshelp-letsencrypt/setup.py b/letshelp-letsencrypt/setup.py index 6582ba59c..c2981132d 100644 --- a/letshelp-letsencrypt/setup.py +++ b/letshelp-letsencrypt/setup.py @@ -17,7 +17,12 @@ else: setup( name="letshelp-letsencrypt", version=version, + description="Let's help Let's Encrypt client", + url='https://github.com/letsencrypt/letsencrypt', + author="Let's Encrypt Project", + author_email='client-dev@letsencrypt.org', license="Apache License 2.0", + packages=find_packages(), install_requires=install_requires, entry_points={ diff --git a/setup.py b/setup.py index b753a8253..846c609e5 100644 --- a/setup.py +++ b/setup.py @@ -77,11 +77,12 @@ testing_extras = [ setup( name='letsencrypt', version=version, - description="Let's Encrypt", + description="Let's Encrypt client", long_description=readme, # later: + '\n\n' + changes + url='https://github.com/letsencrypt/letsencrypt', author="Let's Encrypt Project", + author_email='client-dev@letsencrypt.org', license='Apache License 2.0', - url='https://letsencrypt.org', classifiers=[ 'Environment :: Console', 'Environment :: Console :: Curses', From b6819ad05bb57d98fb78e5f1bd00841da7183d06 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sat, 26 Sep 2015 23:00:25 +0000 Subject: [PATCH 07/78] Add general classifiers to all setup.py scripts. --- acme/setup.py | 9 +++++++++ letsencrypt-apache/setup.py | 15 +++++++++++++++ letsencrypt-compatibility-test/setup.py | 9 +++++++++ letsencrypt-nginx/setup.py | 15 +++++++++++++++ letshelp-letsencrypt/setup.py | 14 ++++++++++++++ 5 files changed, 62 insertions(+) diff --git a/acme/setup.py b/acme/setup.py index f3afe97c3..4651ef5c2 100644 --- a/acme/setup.py +++ b/acme/setup.py @@ -42,6 +42,15 @@ setup( author="Let's Encrypt Project", author_email='client-dev@letsencrypt.org', license='Apache License 2.0', + classifiers=[ + 'Intended Audience :: Developers', + 'License :: OSI Approved :: Apache Software License', + 'Programming Language :: Python', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.7', + 'Topic :: Internet :: WWW/HTTP', + 'Topic :: Security', + ], packages=find_packages(), install_requires=install_requires, diff --git a/letsencrypt-apache/setup.py b/letsencrypt-apache/setup.py index 035a10f4c..8d12b8ef2 100644 --- a/letsencrypt-apache/setup.py +++ b/letsencrypt-apache/setup.py @@ -22,6 +22,21 @@ setup( author="Let's Encrypt Project", author_email='client-dev@letsencrypt.org', license='Apache License 2.0', + classifiers=[ + 'Environment :: Plugins', + 'Intended Audience :: System Administrators', + 'License :: OSI Approved :: Apache Software License', + 'Operating System :: POSIX :: Linux', + 'Programming Language :: Python', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.7', + 'Topic :: Internet :: WWW/HTTP', + 'Topic :: Security', + 'Topic :: System :: Installation/Setup', + 'Topic :: System :: Networking', + 'Topic :: System :: Systems Administration', + 'Topic :: Utilities', + ], packages=find_packages(), install_requires=install_requires, diff --git a/letsencrypt-compatibility-test/setup.py b/letsencrypt-compatibility-test/setup.py index 485752e64..e69c67575 100644 --- a/letsencrypt-compatibility-test/setup.py +++ b/letsencrypt-compatibility-test/setup.py @@ -21,6 +21,15 @@ setup( author="Let's Encrypt Project", author_email='client-dev@letsencrypt.org', license='Apache License 2.0', + classifiers=[ + 'Intended Audience :: Developers', + 'License :: OSI Approved :: Apache Software License', + 'Programming Language :: Python', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.7', + 'Topic :: Internet :: WWW/HTTP', + 'Topic :: Security', + ], packages=find_packages(), install_requires=install_requires, diff --git a/letsencrypt-nginx/setup.py b/letsencrypt-nginx/setup.py index 0a6d9646e..8798c9eb9 100644 --- a/letsencrypt-nginx/setup.py +++ b/letsencrypt-nginx/setup.py @@ -22,6 +22,21 @@ setup( author="Let's Encrypt Project", author_email='client-dev@letsencrypt.org', license='Apache License 2.0', + classifiers=[ + 'Environment :: Plugins', + 'Intended Audience :: System Administrators', + 'License :: OSI Approved :: Apache Software License', + 'Operating System :: POSIX :: Linux', + 'Programming Language :: Python', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.7', + 'Topic :: Internet :: WWW/HTTP', + 'Topic :: Security', + 'Topic :: System :: Installation/Setup', + 'Topic :: System :: Networking', + 'Topic :: System :: Systems Administration', + 'Topic :: Utilities', + ], packages=find_packages(), install_requires=install_requires, diff --git a/letshelp-letsencrypt/setup.py b/letshelp-letsencrypt/setup.py index c2981132d..f05ec0cd1 100644 --- a/letshelp-letsencrypt/setup.py +++ b/letshelp-letsencrypt/setup.py @@ -22,6 +22,20 @@ setup( author="Let's Encrypt Project", author_email='client-dev@letsencrypt.org', license="Apache License 2.0", + classifiers=[ + 'Intended Audience :: System Administrators', + 'License :: OSI Approved :: Apache Software License', + 'Operating System :: POSIX :: Linux', + 'Programming Language :: Python', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.7', + 'Topic :: Internet :: WWW/HTTP', + 'Topic :: Security', + 'Topic :: System :: Installation/Setup', + 'Topic :: System :: Networking', + 'Topic :: System :: Systems Administration', + 'Topic :: Utilities', + ], packages=find_packages(), install_requires=install_requires, From 033ed589cc5ab5d08310779ce96ce28b88ca1ebb Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sat, 26 Sep 2015 23:02:41 +0000 Subject: [PATCH 08/78] Development Status :: 3 - Alpha --- acme/setup.py | 1 + letsencrypt-apache/setup.py | 1 + letsencrypt-compatibility-test/setup.py | 1 + letsencrypt-nginx/setup.py | 1 + letshelp-letsencrypt/setup.py | 1 + setup.py | 1 + 6 files changed, 6 insertions(+) diff --git a/acme/setup.py b/acme/setup.py index 4651ef5c2..0bd03b050 100644 --- a/acme/setup.py +++ b/acme/setup.py @@ -43,6 +43,7 @@ setup( author_email='client-dev@letsencrypt.org', license='Apache License 2.0', classifiers=[ + 'Development Status :: 3 - Alpha', 'Intended Audience :: Developers', 'License :: OSI Approved :: Apache Software License', 'Programming Language :: Python', diff --git a/letsencrypt-apache/setup.py b/letsencrypt-apache/setup.py index 8d12b8ef2..4e3b441a0 100644 --- a/letsencrypt-apache/setup.py +++ b/letsencrypt-apache/setup.py @@ -23,6 +23,7 @@ setup( author_email='client-dev@letsencrypt.org', license='Apache License 2.0', classifiers=[ + 'Development Status :: 3 - Alpha', 'Environment :: Plugins', 'Intended Audience :: System Administrators', 'License :: OSI Approved :: Apache Software License', diff --git a/letsencrypt-compatibility-test/setup.py b/letsencrypt-compatibility-test/setup.py index e69c67575..701b85f3a 100644 --- a/letsencrypt-compatibility-test/setup.py +++ b/letsencrypt-compatibility-test/setup.py @@ -22,6 +22,7 @@ setup( author_email='client-dev@letsencrypt.org', license='Apache License 2.0', classifiers=[ + 'Development Status :: 3 - Alpha', 'Intended Audience :: Developers', 'License :: OSI Approved :: Apache Software License', 'Programming Language :: Python', diff --git a/letsencrypt-nginx/setup.py b/letsencrypt-nginx/setup.py index 8798c9eb9..bf200b07b 100644 --- a/letsencrypt-nginx/setup.py +++ b/letsencrypt-nginx/setup.py @@ -23,6 +23,7 @@ setup( author_email='client-dev@letsencrypt.org', license='Apache License 2.0', classifiers=[ + 'Development Status :: 3 - Alpha', 'Environment :: Plugins', 'Intended Audience :: System Administrators', 'License :: OSI Approved :: Apache Software License', diff --git a/letshelp-letsencrypt/setup.py b/letshelp-letsencrypt/setup.py index f05ec0cd1..e68813083 100644 --- a/letshelp-letsencrypt/setup.py +++ b/letshelp-letsencrypt/setup.py @@ -23,6 +23,7 @@ setup( author_email='client-dev@letsencrypt.org', license="Apache License 2.0", classifiers=[ + 'Development Status :: 3 - Alpha', 'Intended Audience :: System Administrators', 'License :: OSI Approved :: Apache Software License', 'Operating System :: POSIX :: Linux', diff --git a/setup.py b/setup.py index 846c609e5..3076d76b5 100644 --- a/setup.py +++ b/setup.py @@ -84,6 +84,7 @@ setup( author_email='client-dev@letsencrypt.org', license='Apache License 2.0', classifiers=[ + 'Development Status :: 3 - Alpha', 'Environment :: Console', 'Environment :: Console :: Curses', 'Intended Audience :: System Administrators', From 9883f8965d55ee6992816cf9e409244a5b2303a4 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sun, 27 Sep 2015 06:22:46 +0000 Subject: [PATCH 09/78] Add dummy README.rst files --- acme/MANIFEST.in | 1 + acme/README.rst | 1 + letsencrypt-apache/MANIFEST.in | 1 + letsencrypt-apache/README.rst | 1 + letsencrypt-compatibility-test/MANIFEST.in | 1 + letsencrypt-compatibility-test/README.rst | 1 + letsencrypt-nginx/MANIFEST.in | 1 + letsencrypt-nginx/README.rst | 1 + letshelp-letsencrypt/MANIFEST.in | 1 + letshelp-letsencrypt/README.rst | 1 + 10 files changed, 10 insertions(+) create mode 100644 acme/README.rst create mode 100644 letsencrypt-apache/README.rst create mode 100644 letsencrypt-compatibility-test/README.rst create mode 100644 letsencrypt-nginx/README.rst create mode 100644 letshelp-letsencrypt/README.rst diff --git a/acme/MANIFEST.in b/acme/MANIFEST.in index 4a68ae096..ec2b09e05 100644 --- a/acme/MANIFEST.in +++ b/acme/MANIFEST.in @@ -1,2 +1,3 @@ include LICENSE.txt +include README.rst recursive-include acme/testdata * diff --git a/acme/README.rst b/acme/README.rst new file mode 100644 index 000000000..e3ca8b738 --- /dev/null +++ b/acme/README.rst @@ -0,0 +1 @@ +ACME protocol implementation for Python diff --git a/letsencrypt-apache/MANIFEST.in b/letsencrypt-apache/MANIFEST.in index 0a2a07efd..ff99bf0d8 100644 --- a/letsencrypt-apache/MANIFEST.in +++ b/letsencrypt-apache/MANIFEST.in @@ -1,3 +1,4 @@ include LICENSE.txt +include README.rst recursive-include letsencrypt_apache/tests/testdata * include letsencrypt_apache/options-ssl-apache.conf diff --git a/letsencrypt-apache/README.rst b/letsencrypt-apache/README.rst new file mode 100644 index 000000000..3505fd594 --- /dev/null +++ b/letsencrypt-apache/README.rst @@ -0,0 +1 @@ +Apache plugin for Let's Encrypt client diff --git a/letsencrypt-compatibility-test/MANIFEST.in b/letsencrypt-compatibility-test/MANIFEST.in index 50a02378f..52bbb3c65 100644 --- a/letsencrypt-compatibility-test/MANIFEST.in +++ b/letsencrypt-compatibility-test/MANIFEST.in @@ -1,2 +1,3 @@ include LICENSE.txt +include README.rst recursive-include letsencrypt_compatibility_test/testdata * diff --git a/letsencrypt-compatibility-test/README.rst b/letsencrypt-compatibility-test/README.rst new file mode 100644 index 000000000..4afd999a8 --- /dev/null +++ b/letsencrypt-compatibility-test/README.rst @@ -0,0 +1 @@ +Compatibility tests for Let's Encrypt client diff --git a/letsencrypt-nginx/MANIFEST.in b/letsencrypt-nginx/MANIFEST.in index 03457a674..c4bd67735 100644 --- a/letsencrypt-nginx/MANIFEST.in +++ b/letsencrypt-nginx/MANIFEST.in @@ -1,3 +1,4 @@ include LICENSE.txt +include README.rst recursive-include letsencrypt_nginx/tests/testdata * include letsencrypt_nginx/options-ssl-nginx.conf diff --git a/letsencrypt-nginx/README.rst b/letsencrypt-nginx/README.rst new file mode 100644 index 000000000..ff6d50ce4 --- /dev/null +++ b/letsencrypt-nginx/README.rst @@ -0,0 +1 @@ +Nginx plugin for Let's Encrypt client diff --git a/letshelp-letsencrypt/MANIFEST.in b/letshelp-letsencrypt/MANIFEST.in index 9173a6b57..380b80fcb 100644 --- a/letshelp-letsencrypt/MANIFEST.in +++ b/letshelp-letsencrypt/MANIFEST.in @@ -1,2 +1,3 @@ include LICENSE.txt +include README.rst recursive-include letshelp-letsencrypt/testdata * diff --git a/letshelp-letsencrypt/README.rst b/letshelp-letsencrypt/README.rst new file mode 100644 index 000000000..159048d6d --- /dev/null +++ b/letshelp-letsencrypt/README.rst @@ -0,0 +1 @@ +Let's help Let's Encrypt client From 3d638caeb7daca282035b8b7b46c2761d5a1c94a Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sun, 27 Sep 2015 06:30:30 +0000 Subject: [PATCH 10/78] Unify quotes in setup.py scripts --- letsencrypt/__init__.py | 2 +- letshelp-letsencrypt/setup.py | 16 ++++++++-------- setup.py | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/letsencrypt/__init__.py b/letsencrypt/__init__.py index e59dedeac..1155a5b0c 100644 --- a/letsencrypt/__init__.py +++ b/letsencrypt/__init__.py @@ -1,4 +1,4 @@ """Let's Encrypt client.""" # version number like 1.2.3a0, must have at least 2 parts, like 1.2 -__version__ = "0.1.0.dev0" +__version__ = '0.1.0.dev0' diff --git a/letshelp-letsencrypt/setup.py b/letshelp-letsencrypt/setup.py index e68813083..9291b101d 100644 --- a/letshelp-letsencrypt/setup.py +++ b/letshelp-letsencrypt/setup.py @@ -4,24 +4,24 @@ from setuptools import setup from setuptools import find_packages -version = "0.1.0.dev0" +version = '0.1.0.dev0' install_requires = [ - "setuptools", # pkg_resources + 'setuptools', # pkg_resources ] if sys.version_info < (2, 7): - install_requires.append("mock<1.1.0") + install_requires.append('mock<1.1.0') else: - install_requires.append("mock") + install_requires.append('mock') setup( - name="letshelp-letsencrypt", + name='letshelp-letsencrypt', version=version, description="Let's help Let's Encrypt client", url='https://github.com/letsencrypt/letsencrypt', author="Let's Encrypt Project", author_email='client-dev@letsencrypt.org', - license="Apache License 2.0", + license='Apache License 2.0', classifiers=[ 'Development Status :: 3 - Alpha', 'Intended Audience :: System Administrators', @@ -41,8 +41,8 @@ setup( packages=find_packages(), install_requires=install_requires, entry_points={ - "console_scripts": [ - "letshelp-letsencrypt-apache = letshelp_letsencrypt.apache:main", + 'console_scripts': [ + 'letshelp-letsencrypt-apache = letshelp_letsencrypt.apache:main', ], }, include_package_data=True, diff --git a/setup.py b/setup.py index 3076d76b5..b1b0213b6 100644 --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ here = os.path.abspath(os.path.dirname(__file__)) # read version number (and other metadata) from package init init_fn = os.path.join(here, 'letsencrypt', '__init__.py') -meta = dict(re.findall(r"""__([a-z]+)__ = "([^"]+)""", read_file(init_fn))) +meta = dict(re.findall(r"""__([a-z]+)__ = '([^']+)""", read_file(init_fn))) readme = read_file(os.path.join(here, 'README.rst')) changes = read_file(os.path.join(here, 'CHANGES.rst')) From ddc04c755bf6a3e9da956ecf8782ee32b6464cc0 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Sun, 27 Sep 2015 07:56:38 +0000 Subject: [PATCH 11/78] work in progress --- letsencrypt/cli.py | 44 +++++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 6d3aa9d2c..53609009b 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -730,53 +730,55 @@ def _create_subparsers(helpful): # the order of add_subparser() calls is important: it defines the # order in which subparser names will be displayed in --help add_subparser("run", run) + parser_auth = add_subparser("auth", auth) + helpful.add_group("auth", "Options for modifying how a cert is obtained") parser_install = add_subparser("install", install) + helpful.add_group("install", "Options for modifying how a cert is deployed") parser_revoke = add_subparser("revoke", revoke) + helpful.add_group("revoke", "Options for revocation of certs") parser_rollback = add_subparser("rollback", rollback) + helpful.add_group("rollback", "Options for reverting config changes") add_subparser("config_changes", config_changes) parser_plugins = add_subparser("plugins", plugins_cmd) + helpful.add_group("plugins", "Plugin options") - parser_auth.add_argument( - "--csr", type=read_file, help="Path to a Certificate Signing " - "Request (CSR) in DER format.") - parser_auth.add_argument( + helpful.add("auth", + "--csr", type=read_file, help="Path to a Certificate Signing Request (CSR) in DER format.") + helpful.add("auth", "--cert-path", default=flag_default("auth_cert_path"), help="When using --csr this is where certificate is saved.") - parser_auth.add_argument( + helpful.add("auth", "--chain-path", default=flag_default("auth_chain_path"), help="When using --csr this is where certificate chain is saved.") - parser_install.add_argument( - "--cert-path", required=True, help="Path to a certificate that " - "is going to be installed.") - parser_install.add_argument( + helpful.add("install", + "--cert-path", required=True, help="Path to a certificate that is going to be installed.") + helpful.add("install", "--key-path", required=True, help="Accompanying private key") - parser_install.add_argument( + helpful.add("install", "--chain-path", help="Accompanying path to a certificate chain.") - parser_revoke.add_argument( - "--cert-path", type=read_file, help="Revoke a specific certificate.", - required=True) - parser_revoke.add_argument( + helpful.add("revoke", + "--cert-path", type=read_file, help="Revoke a specific certificate.", required=True) + helpful.add("revoke", "--key-path", type=read_file, - help="Revoke certificate using its accompanying key. Useful if " - "Account Key is lost.") + help="Revoke certificate using its accompanying key. Useful if Account Key is lost.") - parser_rollback.add_argument( + helpful.add("rollback", "--checkpoints", type=int, metavar="N", default=flag_default("rollback_checkpoints"), help="Revert configuration N number of checkpoints.") - parser_plugins.add_argument( + helpful.add("plugins", "--init", action="store_true", help="Initialize plugins.") - parser_plugins.add_argument( + helpful.add("plugins", "--prepare", action="store_true", help="Initialize and prepare plugins.") - parser_plugins.add_argument( + helpful.add("plugins", "--authenticators", action="append_const", dest="ifaces", const=interfaces.IAuthenticator, help="Limit to authenticator plugins only.") - parser_plugins.add_argument( + helpful.add("plugins", "--installers", action="append_const", dest="ifaces", const=interfaces.IInstaller, help="Limit to installer plugins only.") From 45a0cd2799a8e3f4b59164045ccf41bf54fec5f3 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sun, 27 Sep 2015 08:10:39 +0000 Subject: [PATCH 12/78] Fix include_package_data. --- acme/setup.py | 1 + letsencrypt-apache/setup.py | 2 +- letsencrypt-compatibility-test/setup.py | 1 + letsencrypt-nginx/setup.py | 2 +- letshelp-letsencrypt/setup.py | 2 +- setup.py | 5 ++--- 6 files changed, 7 insertions(+), 6 deletions(-) diff --git a/acme/setup.py b/acme/setup.py index 0bd03b050..2a3a123c5 100644 --- a/acme/setup.py +++ b/acme/setup.py @@ -54,6 +54,7 @@ setup( ], packages=find_packages(), + include_package_data=True, install_requires=install_requires, extras_require={ 'testing': testing_extras, diff --git a/letsencrypt-apache/setup.py b/letsencrypt-apache/setup.py index 4e3b441a0..ee1457131 100644 --- a/letsencrypt-apache/setup.py +++ b/letsencrypt-apache/setup.py @@ -40,11 +40,11 @@ setup( ], packages=find_packages(), + include_package_data=True, install_requires=install_requires, entry_points={ 'letsencrypt.plugins': [ 'apache = letsencrypt_apache.configurator:ApacheConfigurator', ], }, - include_package_data=True, ) diff --git a/letsencrypt-compatibility-test/setup.py b/letsencrypt-compatibility-test/setup.py index 701b85f3a..745b49bb5 100644 --- a/letsencrypt-compatibility-test/setup.py +++ b/letsencrypt-compatibility-test/setup.py @@ -33,6 +33,7 @@ setup( ], packages=find_packages(), + include_package_data=True, install_requires=install_requires, entry_points={ 'console_scripts': [ diff --git a/letsencrypt-nginx/setup.py b/letsencrypt-nginx/setup.py index bf200b07b..4e770c8cb 100644 --- a/letsencrypt-nginx/setup.py +++ b/letsencrypt-nginx/setup.py @@ -40,11 +40,11 @@ setup( ], packages=find_packages(), + include_package_data=True, install_requires=install_requires, entry_points={ 'letsencrypt.plugins': [ 'nginx = letsencrypt_nginx.configurator:NginxConfigurator', ], }, - include_package_data=True, ) diff --git a/letshelp-letsencrypt/setup.py b/letshelp-letsencrypt/setup.py index 9291b101d..a83fc8843 100644 --- a/letshelp-letsencrypt/setup.py +++ b/letshelp-letsencrypt/setup.py @@ -39,11 +39,11 @@ setup( ], packages=find_packages(), + include_package_data=True, install_requires=install_requires, entry_points={ 'console_scripts': [ 'letshelp-letsencrypt-apache = letshelp_letsencrypt.apache:main', ], }, - include_package_data=True, ) diff --git a/setup.py b/setup.py index b1b0213b6..0fed79354 100644 --- a/setup.py +++ b/setup.py @@ -102,6 +102,8 @@ setup( ], packages=find_packages(exclude=['docs', 'examples', 'tests', 'venv']), + include_package_data=True, + install_requires=install_requires, extras_require={ 'dev': dev_extras, @@ -127,7 +129,4 @@ setup( ':StandaloneAuthenticator', ], }, - - zip_safe=False, # letsencrypt/tests/test_util.py is a symlink! - include_package_data=True, ) From d621df3320fe5d291c1da4884365f11e659b2127 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sun, 27 Sep 2015 10:50:50 +0000 Subject: [PATCH 13/78] Make zipsafe --- acme/acme/test_util.py | 2 - letsencrypt/tests/test_util.py | 68 +++++++++++++++++++++++++++++++++- 2 files changed, 67 insertions(+), 3 deletions(-) mode change 120000 => 100644 letsencrypt/tests/test_util.py diff --git a/acme/acme/test_util.py b/acme/acme/test_util.py index c9c076d27..2b4c6e00c 100644 --- a/acme/acme/test_util.py +++ b/acme/acme/test_util.py @@ -1,5 +1,3 @@ -# Symlinked in letsencrypt/tests/test_util.py, causes duplicate-code -# warning that cannot be disabled locally. """Test utilities. .. warning:: This module is not part of the public API. diff --git a/letsencrypt/tests/test_util.py b/letsencrypt/tests/test_util.py deleted file mode 120000 index 80d26cbe8..000000000 --- a/letsencrypt/tests/test_util.py +++ /dev/null @@ -1 +0,0 @@ -../../acme/acme/test_util.py \ No newline at end of file diff --git a/letsencrypt/tests/test_util.py b/letsencrypt/tests/test_util.py new file mode 100644 index 000000000..2b4c6e00c --- /dev/null +++ b/letsencrypt/tests/test_util.py @@ -0,0 +1,67 @@ +"""Test utilities. + +.. warning:: This module is not part of the public API. + +""" +import os +import pkg_resources + +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import serialization +import OpenSSL + +from acme import jose + + +def vector_path(*names): + """Path to a test vector.""" + return pkg_resources.resource_filename( + __name__, os.path.join('testdata', *names)) + + +def load_vector(*names): + """Load contents of a test vector.""" + # luckily, resource_string opens file in binary mode + return pkg_resources.resource_string( + __name__, os.path.join('testdata', *names)) + + +def _guess_loader(filename, loader_pem, loader_der): + _, ext = os.path.splitext(filename) + if ext.lower() == '.pem': + return loader_pem + elif ext.lower() == '.der': + return loader_der + else: # pragma: no cover + raise ValueError("Loader could not be recognized based on extension") + + +def load_cert(*names): + """Load certificate.""" + loader = _guess_loader( + names[-1], OpenSSL.crypto.FILETYPE_PEM, OpenSSL.crypto.FILETYPE_ASN1) + return jose.ComparableX509(OpenSSL.crypto.load_certificate( + loader, load_vector(*names))) + + +def load_csr(*names): + """Load certificate request.""" + loader = _guess_loader( + names[-1], OpenSSL.crypto.FILETYPE_PEM, OpenSSL.crypto.FILETYPE_ASN1) + return jose.ComparableX509(OpenSSL.crypto.load_certificate_request( + loader, load_vector(*names))) + + +def load_rsa_private_key(*names): + """Load RSA private key.""" + loader = _guess_loader(names[-1], serialization.load_pem_private_key, + serialization.load_der_private_key) + return jose.ComparableRSAKey(loader( + load_vector(*names), password=None, backend=default_backend())) + + +def load_pyopenssl_private_key(*names): + """Load pyOpenSSL private key.""" + loader = _guess_loader( + names[-1], OpenSSL.crypto.FILETYPE_PEM, OpenSSL.crypto.FILETYPE_ASN1) + return OpenSSL.crypto.load_privatekey(loader, load_vector(*names)) From 7d3a49b9e73017aa07d0a8ea39ac5dad9b1febca Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sun, 27 Sep 2015 06:54:31 +0000 Subject: [PATCH 14/78] Add twine and wheel to [dev] --- setup.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/setup.py b/setup.py index 0fed79354..b1a5ada25 100644 --- a/setup.py +++ b/setup.py @@ -57,6 +57,8 @@ dev_extras = [ # Pin astroid==1.3.5, pylint==1.4.2 as a workaround for #289 'astroid==1.3.5', 'pylint==1.4.2', # upstream #248 + 'twine', + 'wheel', ] docs_extras = [ From 1d5e1ee37e8d3710658b66506f35994953d362f6 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sun, 27 Sep 2015 09:42:17 +0000 Subject: [PATCH 15/78] Fix letshelp_letsencrypt MANIFEST --- letshelp-letsencrypt/MANIFEST.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letshelp-letsencrypt/MANIFEST.in b/letshelp-letsencrypt/MANIFEST.in index 380b80fcb..96c1d7ba5 100644 --- a/letshelp-letsencrypt/MANIFEST.in +++ b/letshelp-letsencrypt/MANIFEST.in @@ -1,3 +1,3 @@ include LICENSE.txt include README.rst -recursive-include letshelp-letsencrypt/testdata * +recursive-include letshelp_letsencrypt/testdata * From 3c08b512c3b546528880a1353f6b8607307533a3 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sat, 26 Sep 2015 23:56:18 +0000 Subject: [PATCH 16/78] Simple dev release script --- .gitignore | 6 +-- tools/dev-release.sh | 96 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+), 3 deletions(-) create mode 100755 tools/dev-release.sh diff --git a/.gitignore b/.gitignore index 8afb61ffc..ba843d9cc 100644 --- a/.gitignore +++ b/.gitignore @@ -2,9 +2,9 @@ *.egg-info/ .eggs/ build/ -dist/ -/venv/ -/venv3/ +dist*/ +/venv*/ +/kgs/ /.tox/ letsencrypt.log diff --git a/tools/dev-release.sh b/tools/dev-release.sh new file mode 100755 index 000000000..06f49f0a5 --- /dev/null +++ b/tools/dev-release.sh @@ -0,0 +1,96 @@ +#!/bin/sh -xe +# Release dev packages to PyPI + +version="0.0.0.dev$(date +%Y%m%d)" +DEV_RELEASE_BRANCH="dev-release" +# TODO: create a real release key instead of using Kuba's personal one +RELEASE_GPG_KEY="${RELEASE_GPG_KEY:-148C30F6F7E429337A72D992B00B9CC82D7ADF2C}" + +# port for a local Python Package Index (used in testing) +PORT=${PORT:-1234} + +# subpackages to be released +SUBPKGS=${SUBPKGS:-"acme letsencrypt_apache letsencrypt_nginx letshelp_letsencrypt"} +subpkgs_dirs="$(echo $SUBPKGS | sed s/_/-/g)" +# letsencrypt_compatibility_test is not packaged because: +# - it is not meant to be used by anyone else than Let's Encrypt devs +# - it causes problems when running nosetests - the latter tries to +# run everything that matches test*, while there are no unittests +# there + +tag="v$version" +mv "dist.$version" "dist.$version.$(date +%s).bak" || true +git tag --delete "$tag" || true + +root="$(mktemp -d -t le.$version.XXX)" +echo "Cloning into fresh copy at $root" # clean repo = no artificats +git clone . $root +cd $root +git branch -f "$DEV_RELEASE_BRANCH" +git checkout "$DEV_RELEASE_BRANCH" + +for pkg_dir in $subpkgs_dirs +do + sed -i $x "s/^version.*/version = '$version'/" $pkg_dir/setup.py +done +sed -i "s/^__version.*/__version__ = '$version'/" letsencrypt/__init__.py + +git add -p # interactive user input +git -c commit.gpgsign=true commit -m "Release $version" +git tag --local-user "$RELEASE_GPG_KEY" \ + --sign --message "Release $version" "$tag" + +echo "Preparing sdists and wheels" +for pkg_dir in . $subpkgs_dirs +do + cd $pkg_dir + + python setup.py clean + rm -rf build dist + python setup.py sdist + python setup.py bdist_wheel + + echo "Signing ($pkg_dir)" + for x in dist/*.tar.gz dist/*.whl + do + gpg2 --detach-sign --armor --sign $x + done + + cd - +done + +mkdir "dist.$version" +mv dist "dist.$version/letsencrypt" +for pkg_dir in $subpkgs_dirs +do + mv $pkg_dir/dist "dist.$version/$pkg_dir/" +done + +echo "Testing packages" +cd "dist.$version" +# start local PyPI +python -m SimpleHTTPServer $PORT & +# cd .. is NOT done on purpose: we make sure that all subpacakges are +# installed from local PyPI rather than current directory (repo root) +virtualenv --no-site-packages ../venv +. ../venv/bin/activate +# Now, use our local PyPI. --pre allows installation of pre-release (incl. dev) +pip install \ + --pre \ + --extra-index-url http://localhost:$PORT \ + letsencrypt $SUBPKGS +# stop local PyPI +kill $! + +# freeze before installing anythin else, so that we know end-user KGS +mkdir kgs +kgs="kgs/$version" +pip freeze | tee $kgs +pip install nose +# TODO: letsencrypt_apache fails due to symlink, c.f. #838 +nosetests letsencrypt $SUBPKGS || true + +echo "New root: $root" +echo "KGS is at $root/$kgs" +echo "In order to upload packages run the following command:" +echo twine upload "$root/dist.$version/*/*" From cbfdae88fcde764b0a60190d12881c9945fe2437 Mon Sep 17 00:00:00 2001 From: Brandon Kreisel Date: Sun, 27 Sep 2015 14:44:00 -0400 Subject: [PATCH 17/78] Add Mac compatibility to boulder-start The version of sort that ships with OS X does not support the -V version flag. Emulate that functionality with some sed-fu --- tests/boulder-start.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/boulder-start.sh b/tests/boulder-start.sh index 7ce7dcba4..e8c50633f 100755 --- a/tests/boulder-start.sh +++ b/tests/boulder-start.sh @@ -4,11 +4,16 @@ # ugh, go version output is like: # go version go1.4.2 linux/amd64 -GOVER=`go version | cut -d" " -f3 | cut -do -f2` +GOVER=`go version | cut -d" " -f3 | cut -do -f2` # version comparison function verlte { + if [ `uname` == 'Darwin' ]; then + [ "$1" = "`echo -e \"$1\n$2\" | sed 's/\b\([0-9]\)\b/0\1/g' \ + | sort | sed 's/\b0\([0-9]\)/\1/g' | head -n1`" ] + else [ "$1" = "`echo -e "$1\n$2" | sort -V | head -n1`" ] + fi } if ! verlte 1.5 "$GOVER" ; then From 5d8e9a3d68b362634c9fb752e5a0bcb4fb12d021 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sun, 27 Sep 2015 21:07:40 +0000 Subject: [PATCH 18/78] Fix various doc generation issues --- acme/acme/challenges.py | 2 +- docs/api/display.rst | 6 ------ docs/api/recovery_token.rst | 5 ----- docs/api/revoker.rst | 5 ----- letsencrypt-apache/letsencrypt_apache/configurator.py | 2 +- 5 files changed, 2 insertions(+), 18 deletions(-) delete mode 100644 docs/api/recovery_token.rst delete mode 100644 docs/api/revoker.rst diff --git a/acme/acme/challenges.py b/acme/acme/challenges.py index 13186cc4f..81711e605 100644 --- a/acme/acme/challenges.py +++ b/acme/acme/challenges.py @@ -542,7 +542,7 @@ class DNS(DVChallenge): def check_validation(self, validation, account_public_key): """Check validation. - :param validation + :param JWS validation: :type account_public_key: `~cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey` or diff --git a/docs/api/display.rst b/docs/api/display.rst index b79ef25d7..117a91708 100644 --- a/docs/api/display.rst +++ b/docs/api/display.rst @@ -21,9 +21,3 @@ .. automodule:: letsencrypt.display.enhancements :members: - -:mod:`letsencrypt.display.revocation` -===================================== - -.. automodule:: letsencrypt.display.revocation - :members: diff --git a/docs/api/recovery_token.rst b/docs/api/recovery_token.rst deleted file mode 100644 index 774aa4b3c..000000000 --- a/docs/api/recovery_token.rst +++ /dev/null @@ -1,5 +0,0 @@ -:mod:`letsencrypt.recovery_token` --------------------------------------------------- - -.. automodule:: letsencrypt.recovery_token - :members: diff --git a/docs/api/revoker.rst b/docs/api/revoker.rst deleted file mode 100644 index a482a138e..000000000 --- a/docs/api/revoker.rst +++ /dev/null @@ -1,5 +0,0 @@ -:mod:`letsencrypt.revoker` --------------------------- - -.. automodule:: letsencrypt.revoker - :members: diff --git a/letsencrypt-apache/letsencrypt_apache/configurator.py b/letsencrypt-apache/letsencrypt_apache/configurator.py index f301de8b9..ad3c62d2c 100644 --- a/letsencrypt-apache/letsencrypt_apache/configurator.py +++ b/letsencrypt-apache/letsencrypt_apache/configurator.py @@ -1162,7 +1162,7 @@ def _get_mod_deps(mod_name): changes. .. warning:: If all deps are not included, it may cause incorrect parsing behavior, due to enable_mod's shortcut for updating the parser's - currently defined modules (:method:`.ApacheConfigurator._add_parser_mod`) + currently defined modules (`.ApacheConfigurator._add_parser_mod`) This would only present a major problem in extremely atypical configs that use ifmod for the missing deps. From 96a737bbbaf9aa76accdbd9421b19e38a0703e72 Mon Sep 17 00:00:00 2001 From: David Xia Date: Sun, 27 Sep 2015 16:51:20 -0400 Subject: [PATCH 19/78] Fix CLI --help for OS X OS X's signal module doesn't have SIGPWR. Don't try to use it. Fixes #841 --- letsencrypt/error_handler.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/letsencrypt/error_handler.py b/letsencrypt/error_handler.py index fedb66c0e..99f502ac2 100644 --- a/letsencrypt/error_handler.py +++ b/letsencrypt/error_handler.py @@ -2,6 +2,7 @@ import logging import os import signal +import sys import traceback @@ -13,9 +14,14 @@ logger = logging.getLogger(__name__) # potentially occur from inside Python. Signals such as SIGILL were not # included as they could be a sign of something devious and we should terminate # immediately. -_SIGNALS = ([signal.SIGTERM] if os.name == "nt" else - [signal.SIGTERM, signal.SIGHUP, signal.SIGQUIT, - signal.SIGXCPU, signal.SIGXFSZ, signal.SIGPWR]) +if os.name == "nt": + _SIGNALS = [signal.SIGTERM] +elif sys.platform == "darwin": + _SIGNALS = [signal.SIGTERM, signal.SIGHUP, signal.SIGQUIT, signal.SIGXCPU, + signal.SIGXFSZ] +else: + _SIGNALS = [signal.SIGTERM, signal.SIGHUP, signal.SIGQUIT, signal.SIGXCPU, + signal.SIGXFSZ, signal.SIGPWR] class ErrorHandler(object): From a7375eb5494df494d2604ee1e903467b093af30b Mon Sep 17 00:00:00 2001 From: Jacob Hoffman-Andrews Date: Sun, 27 Sep 2015 17:44:31 -0700 Subject: [PATCH 20/78] Emit error when simple_verify fails. When running the manual authenticator, if simple_verify fails, there is no output to indicate what went wrong, just "Incomplete authorizations." --- letsencrypt/plugins/manual.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/letsencrypt/plugins/manual.py b/letsencrypt/plugins/manual.py index 2014c8c0e..2fad4ac53 100644 --- a/letsencrypt/plugins/manual.py +++ b/letsencrypt/plugins/manual.py @@ -182,6 +182,8 @@ binary for temporary key/certificate generation.""".replace("\n", "") achall.account_key.public_key(), self.config.simple_http_port): return response else: + logger.error( + "Self-verify of challenge failed, authorization abandoned.\n") if self.conf("test-mode") and self._httpd.poll() is not None: # simply verify cause command failure... return False From 913a0a9e98b2559ab960b58dd533a932cdde8150 Mon Sep 17 00:00:00 2001 From: Jadaw1n Date: Mon, 28 Sep 2015 17:34:43 +0200 Subject: [PATCH 21/78] Dockerfile: option --text doesn't exist --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 789e26af9..b9ea168de 100644 --- a/Dockerfile +++ b/Dockerfile @@ -62,5 +62,5 @@ RUN virtualenv --no-site-packages -p python2 /opt/letsencrypt/venv && \ # bash" and investigate, apply patches, etc. ENV PATH /opt/letsencrypt/venv/bin:$PATH -# TODO: is --text really necessary? -ENTRYPOINT [ "letsencrypt", "--text" ] + +ENTRYPOINT [ "letsencrypt" ] From 27268afdcc82a34e0d37d39bd6a14af5431ddb8c Mon Sep 17 00:00:00 2001 From: Jacob Hoffman-Andrews Date: Mon, 28 Sep 2015 11:58:12 -0700 Subject: [PATCH 22/78] Remove extra newline. --- letsencrypt/plugins/manual.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt/plugins/manual.py b/letsencrypt/plugins/manual.py index 2fad4ac53..3f7276725 100644 --- a/letsencrypt/plugins/manual.py +++ b/letsencrypt/plugins/manual.py @@ -183,7 +183,7 @@ binary for temporary key/certificate generation.""".replace("\n", "") return response else: logger.error( - "Self-verify of challenge failed, authorization abandoned.\n") + "Self-verify of challenge failed, authorization abandoned.") if self.conf("test-mode") and self._httpd.poll() is not None: # simply verify cause command failure... return False From 315b3577811fba3d3a540c22cc2f6bf772fb98af Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sun, 27 Sep 2015 21:27:36 +0000 Subject: [PATCH 23/78] Hide null installer (fixes #789). --- letsencrypt/cli.py | 2 +- letsencrypt/display/ops.py | 2 +- letsencrypt/plugins/disco.py | 9 +++++++++ letsencrypt/plugins/null.py | 1 + letsencrypt/tests/display/ops_test.py | 12 +++++++----- setup.py | 1 - 6 files changed, 19 insertions(+), 8 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 3317ae549..8bcbd8f02 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -420,7 +420,7 @@ def plugins_cmd(args, config, plugins): # TODO: Use IDisplay rather than print logger.debug("Expected interfaces: %s", args.ifaces) ifaces = [] if args.ifaces is None else args.ifaces - filtered = plugins.ifaces(ifaces) + filtered = plugins.visible().ifaces(ifaces) logger.debug("Filtered plugins: %r", filtered) if not args.init and not args.prepare: diff --git a/letsencrypt/display/ops.py b/letsencrypt/display/ops.py index 4ab3ec579..43705e309 100644 --- a/letsencrypt/display/ops.py +++ b/letsencrypt/display/ops.py @@ -65,7 +65,7 @@ def pick_plugin(config, default, plugins, question, ifaces): # throw more UX-friendly error if default not in plugins filtered = plugins.filter(lambda p_ep: p_ep.name == default) else: - filtered = plugins.ifaces(ifaces) + filtered = plugins.visible().ifaces(ifaces) filtered.init(config) verified = filtered.verify(ifaces) diff --git a/letsencrypt/plugins/disco.py b/letsencrypt/plugins/disco.py index b6cdb1f99..5a41fda88 100644 --- a/letsencrypt/plugins/disco.py +++ b/letsencrypt/plugins/disco.py @@ -50,6 +50,11 @@ class PluginEntryPoint(object): """Description with name. Handy for UI.""" return "{0} ({1})".format(self.description, self.name) + @property + def hidden(self): + """Should this plugin be hidden from UI?""" + return getattr(self.plugin_cls, "hidden", False) + def ifaces(self, *ifaces_groups): """Does plugin implements specified interface groups?""" return not ifaces_groups or any( @@ -183,6 +188,10 @@ class PluginsRegistry(collections.Mapping): return type(self)(dict((name, plugin_ep) for name, plugin_ep in self._plugins.iteritems() if pred(plugin_ep))) + def visible(self): + """Filter plugins based on visibility.""" + return self.filter(lambda plugin_ep: not plugin_ep.hidden) + def ifaces(self, *ifaces_groups): """Filter plugins based on interfaces.""" # pylint: disable=star-args diff --git a/letsencrypt/plugins/null.py b/letsencrypt/plugins/null.py index efe041cac..4ba6c9d64 100644 --- a/letsencrypt/plugins/null.py +++ b/letsencrypt/plugins/null.py @@ -17,6 +17,7 @@ class Installer(common.Plugin): zope.interface.classProvides(interfaces.IPluginFactory) description = "Null Installer" + hidden = True # pylint: disable=missing-docstring,no-self-use diff --git a/letsencrypt/tests/display/ops_test.py b/letsencrypt/tests/display/ops_test.py index 7420a62f0..9d4a3a933 100644 --- a/letsencrypt/tests/display/ops_test.py +++ b/letsencrypt/tests/display/ops_test.py @@ -84,7 +84,7 @@ class PickPluginTest(unittest.TestCase): def test_no_default(self): self._call() - self.assertEqual(1, self.reg.ifaces.call_count) + self.assertEqual(1, self.reg.visible().ifaces.call_count) def test_no_candidate(self): self.assertTrue(self._call() is None) @@ -94,7 +94,8 @@ class PickPluginTest(unittest.TestCase): plugin_ep.init.return_value = "foo" plugin_ep.misconfigured = False - self.reg.ifaces().verify().available.return_value = {"bar": plugin_ep} + self.reg.visible().ifaces().verify().available.return_value = { + "bar": plugin_ep} self.assertEqual("foo", self._call()) def test_single_misconfigured(self): @@ -102,13 +103,14 @@ class PickPluginTest(unittest.TestCase): plugin_ep.init.return_value = "foo" plugin_ep.misconfigured = True - self.reg.ifaces().verify().available.return_value = {"bar": plugin_ep} + self.reg.visible().ifaces().verify().available.return_value = { + "bar": plugin_ep} self.assertTrue(self._call() is None) def test_multiple(self): plugin_ep = mock.MagicMock() plugin_ep.init.return_value = "foo" - self.reg.ifaces().verify().available.return_value = { + self.reg.visible().ifaces().verify().available.return_value = { "bar": plugin_ep, "baz": plugin_ep, } @@ -119,7 +121,7 @@ class PickPluginTest(unittest.TestCase): [plugin_ep, plugin_ep], self.question) def test_choose_plugin_none(self): - self.reg.ifaces().verify().available.return_value = { + self.reg.visible().ifaces().verify().available.return_value = { "bar": None, "baz": None, } diff --git a/setup.py b/setup.py index c568d2872..8f75aff03 100644 --- a/setup.py +++ b/setup.py @@ -118,7 +118,6 @@ setup( ], 'letsencrypt.plugins': [ 'manual = letsencrypt.plugins.manual:Authenticator', - # TODO: null should probably not be presented to the user 'null = letsencrypt.plugins.null:Installer', 'standalone = letsencrypt.plugins.standalone.authenticator' ':StandaloneAuthenticator', From c1012f5f0082dd99d22fb5a49695dfbdfd433f19 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Mon, 28 Sep 2015 12:25:37 -0700 Subject: [PATCH 24/78] Removed SIGPWR entirely --- letsencrypt/error_handler.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/letsencrypt/error_handler.py b/letsencrypt/error_handler.py index 99f502ac2..1f979a6de 100644 --- a/letsencrypt/error_handler.py +++ b/letsencrypt/error_handler.py @@ -2,7 +2,6 @@ import logging import os import signal -import sys import traceback @@ -14,14 +13,9 @@ logger = logging.getLogger(__name__) # potentially occur from inside Python. Signals such as SIGILL were not # included as they could be a sign of something devious and we should terminate # immediately. -if os.name == "nt": - _SIGNALS = [signal.SIGTERM] -elif sys.platform == "darwin": - _SIGNALS = [signal.SIGTERM, signal.SIGHUP, signal.SIGQUIT, signal.SIGXCPU, - signal.SIGXFSZ] -else: - _SIGNALS = [signal.SIGTERM, signal.SIGHUP, signal.SIGQUIT, signal.SIGXCPU, - signal.SIGXFSZ, signal.SIGPWR] +_SIGNALS = ([signal.SIGTERM] if os.name == "nt" else + [signal.SIGTERM, signal.SIGHUP, signal.SIGQUIT, + signal.SIGXCPU, signal.SIGXFSZ]) class ErrorHandler(object): From ab98d5c39fc19cc90785a87f10cc4b53390e8b20 Mon Sep 17 00:00:00 2001 From: Richard Barnes Date: Mon, 28 Sep 2015 17:14:33 -0400 Subject: [PATCH 25/78] Ignore unknown challenge types --- acme/acme/messages.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/acme/acme/messages.py b/acme/acme/messages.py index 02ae24c8f..002c08767 100644 --- a/acme/acme/messages.py +++ b/acme/acme/messages.py @@ -373,7 +373,17 @@ class Authorization(ResourceBody): @challenges.decoder def challenges(value): # pylint: disable=missing-docstring,no-self-argument - return tuple(ChallengeBody.from_json(chall) for chall in value) + # The from_json method raises errors.UnrecognizedTypeError when a + # challenge of unknown type is encountered. We want to ignore this + # case. This forces us to do an explicit iteration, since list + # comprehensions can't handle exceptions. + challenges = [] + for chall in value: + try: + challenges.append(ChallengeBody.from_json(chall)) + except errors.UnknownTypeError: + continue + return tuple(challenges) @property def resolved_combinations(self): From b6bbc9e0a29a7b64ecc03b1ffbbccf67cac37238 Mon Sep 17 00:00:00 2001 From: Brandon Kreisel Date: Mon, 28 Sep 2015 17:39:01 -0400 Subject: [PATCH 26/78] Add inline Mac comment --- tests/boulder-start.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/boulder-start.sh b/tests/boulder-start.sh index e8c50633f..530f9c598 100755 --- a/tests/boulder-start.sh +++ b/tests/boulder-start.sh @@ -8,6 +8,7 @@ GOVER=`go version | cut -d" " -f3 | cut -do -f2` # version comparison function verlte { + #OS X doesn't support version sorting; emulate with sed if [ `uname` == 'Darwin' ]; then [ "$1" = "`echo -e \"$1\n$2\" | sed 's/\b\([0-9]\)\b/0\1/g' \ | sort | sed 's/\b0\([0-9]\)/\1/g' | head -n1`" ] From 3279aefefbd409aae2f1bb954cd67d266240e973 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Mon, 28 Sep 2015 15:15:44 -0700 Subject: [PATCH 27/78] Made PEP8 happy --- letsencrypt/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 8bcbd8f02..dccfb9289 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -518,7 +518,7 @@ class HelpfulArgumentParser(object): help2 = self.prescan_for_flag("--help", self.help_topics) assert max(True, "a") == "a", "Gravity changed direction" help_arg = max(help1, help2) - if help_arg == True: + if help_arg is True: # just --help with no topic; avoid argparse altogether print USAGE sys.exit(0) From fa992faf52be93309506ae728eb64340fd388706 Mon Sep 17 00:00:00 2001 From: James Kasten Date: Mon, 28 Sep 2015 15:24:51 -0700 Subject: [PATCH 28/78] Fix pylint and add test --- acme/acme/messages.py | 11 ++++++----- acme/acme/messages_test.py | 21 +++++++++++++++++++++ 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/acme/acme/messages.py b/acme/acme/messages.py index 002c08767..594b3d5c7 100644 --- a/acme/acme/messages.py +++ b/acme/acme/messages.py @@ -2,6 +2,7 @@ import collections from acme import challenges +from acme import errors from acme import fields from acme import jose from acme import util @@ -373,17 +374,17 @@ class Authorization(ResourceBody): @challenges.decoder def challenges(value): # pylint: disable=missing-docstring,no-self-argument - # The from_json method raises errors.UnrecognizedTypeError when a + # The from_json method raises errors.UnrecognizedTypeError when a # challenge of unknown type is encountered. We want to ignore this # case. This forces us to do an explicit iteration, since list # comprehensions can't handle exceptions. - challenges = [] + challs = [] for chall in value: try: - challenges.append(ChallengeBody.from_json(chall)) - except errors.UnknownTypeError: + challs.append(ChallengeBody.from_json(chall)) + except jose.UnrecognizedTypeError: continue - return tuple(challenges) + return tuple(challs) @property def resolved_combinations(self): diff --git a/acme/acme/messages_test.py b/acme/acme/messages_test.py index 25f07018c..ac722909c 100644 --- a/acme/acme/messages_test.py +++ b/acme/acme/messages_test.py @@ -274,6 +274,9 @@ class AuthorizationTest(unittest.TestCase): def setUp(self): from acme.messages import ChallengeBody from acme.messages import STATUS_VALID + + unknown_chall = mock.MagicMock() + unknown_chall.to_json.side_effect = side_effect=jose.UnrecognizedTypeError self.challbs = ( ChallengeBody( uri='http://challb1', status=STATUS_VALID, @@ -300,6 +303,19 @@ class AuthorizationTest(unittest.TestCase): 'combinations': combinations, } + # For unknown challenge types + self.jmsg_unknown_chall = { + 'resource': 'challenge', + 'uri': 'random_uri', + 'type': 'unknown', + 'tls': True, + } + + self.jobj_from_unknown = { + 'identifier': identifier.to_json(), + 'challenges': [self.jmsg_unknown_chall], + } + def test_from_json(self): from acme.messages import Authorization Authorization.from_json(self.jobj_from) @@ -314,6 +330,11 @@ class AuthorizationTest(unittest.TestCase): (self.challbs[1], self.challbs[2]), )) + def test_unknown_chall_type(self): + """Just make sure an error isn't thrown.""" + from acme.messages import Authorization + Authorization.from_json(self.jobj_from_unknown) + class AuthorizationResourceTest(unittest.TestCase): """Tests for acme.messages.AuthorizationResource.""" From 4da0e17255a15d0e9589795410b25c05a6b87cc2 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Mon, 28 Sep 2015 15:45:31 -0700 Subject: [PATCH 29/78] Added message and changed reporter interface --- letsencrypt/account.py | 4 ++-- letsencrypt/auth_handler.py | 2 +- letsencrypt/cli.py | 11 +++++++++++ letsencrypt/client.py | 2 +- letsencrypt/interfaces.py | 2 +- letsencrypt/reporter.py | 2 +- letsencrypt/tests/reporter_test.py | 6 +++--- 7 files changed, 20 insertions(+), 9 deletions(-) diff --git a/letsencrypt/account.py b/letsencrypt/account.py index 8bee22102..c97e4f6fe 100644 --- a/letsencrypt/account.py +++ b/letsencrypt/account.py @@ -92,13 +92,13 @@ def report_new_account(acc, config): "contain certificates and private keys obtained by Let's Encrypt " "so making regular backups of this folder is ideal.".format( config.config_dir), - reporter.MEDIUM_PRIORITY, True) + reporter.MEDIUM_PRIORITY) if acc.regr.body.emails: recovery_msg = ("If you lose your account credentials, you can " "recover through e-mails sent to {0}.".format( ", ".join(acc.regr.body.emails))) - reporter.add_message(recovery_msg, reporter.HIGH_PRIORITY, True) + reporter.add_message(recovery_msg, reporter.HIGH_PRIORITY) class AccountMemoryStorage(interfaces.AccountStorage): diff --git a/letsencrypt/auth_handler.py b/letsencrypt/auth_handler.py index 68aed510a..b27a569f6 100644 --- a/letsencrypt/auth_handler.py +++ b/letsencrypt/auth_handler.py @@ -531,7 +531,7 @@ def _report_failed_challs(failed_achalls): reporter = zope.component.getUtility(interfaces.IReporter) for achalls in problems.itervalues(): reporter.add_message( - _generate_failed_chall_msg(achalls), reporter.MEDIUM_PRIORITY, True) + _generate_failed_chall_msg(achalls), reporter.MEDIUM_PRIORITY) def _generate_failed_chall_msg(failed_achalls): diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index dccfb9289..bd49d110b 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -267,6 +267,14 @@ def _treat_as_renewal(config, domains): return None +def _report_new_cert(cert_path): + """Reports the creation of a new certificate to the user.""" + reporter_util = zope.component.getUtility(interfaces.IReporter) + reporter_util.add_message("Congratulations! Your certificate has been " + "saved at {0}.".format(cert_path), + reporter.MEDIUM_PRIORITY) + + def _auth_from_domains(le_client, config, domains, plugins): """Authenticate and enroll certificate.""" # Note: This can raise errors... caught above us though. @@ -292,6 +300,8 @@ def _auth_from_domains(le_client, config, domains, plugins): if not lineage: raise errors.Error("Certificate could not be obtained") + _report_new_cert(lineage.cert) + return lineage @@ -365,6 +375,7 @@ def auth(args, config, plugins): file=args.csr[0], data=args.csr[1], form="der")) le_client.save_certificate( certr, chain, args.cert_path, args.chain_path) + _report_new_cert(args.cert_path) else: domains = _find_domains(args, installer) _auth_from_domains(le_client, config, domains, plugins) diff --git a/letsencrypt/client.py b/letsencrypt/client.py index e9decae47..c82131af3 100644 --- a/letsencrypt/client.py +++ b/letsencrypt/client.py @@ -286,7 +286,7 @@ class Client(object): "configured in the directories under {0}.").format( cert.cli_config.renewal_configs_dir) reporter = zope.component.getUtility(interfaces.IReporter) - reporter.add_message(msg, reporter.LOW_PRIORITY, True) + reporter.add_message(msg, reporter.LOW_PRIORITY) def save_certificate(self, certr, chain_cert, cert_path, chain_path): # pylint: disable=no-self-use diff --git a/letsencrypt/interfaces.py b/letsencrypt/interfaces.py index 1ba8afe45..1f51645ab 100644 --- a/letsencrypt/interfaces.py +++ b/letsencrypt/interfaces.py @@ -478,7 +478,7 @@ class IReporter(zope.interface.Interface): LOW_PRIORITY = zope.interface.Attribute( "Used to denote low priority messages") - def add_message(self, msg, priority, on_crash=False): + def add_message(self, msg, priority, on_crash=True): """Adds msg to the list of messages to be printed. :param str msg: Message to be displayed to the user. diff --git a/letsencrypt/reporter.py b/letsencrypt/reporter.py index 482305838..0905dfa54 100644 --- a/letsencrypt/reporter.py +++ b/letsencrypt/reporter.py @@ -36,7 +36,7 @@ class Reporter(object): def __init__(self): self.messages = Queue.PriorityQueue() - def add_message(self, msg, priority, on_crash=False): + def add_message(self, msg, priority, on_crash=True): """Adds msg to the list of messages to be printed. :param str msg: Message to be displayed to the user. diff --git a/letsencrypt/tests/reporter_test.py b/letsencrypt/tests/reporter_test.py index c43511208..89bd9dfc7 100644 --- a/letsencrypt/tests/reporter_test.py +++ b/letsencrypt/tests/reporter_test.py @@ -78,13 +78,13 @@ class ReporterTest(unittest.TestCase): output = sys.stdout.getvalue() self.assertTrue("IMPORTANT NOTES:" in output) self.assertTrue("High" in output) - self.assertTrue("Med" not in output) + self.assertTrue("Med" in output) self.assertTrue("Low" not in output) def _add_messages(self): - self.reporter.add_message("High", self.reporter.HIGH_PRIORITY, True) + self.reporter.add_message("High", self.reporter.HIGH_PRIORITY) self.reporter.add_message("Med", self.reporter.MEDIUM_PRIORITY) - self.reporter.add_message("Low", self.reporter.LOW_PRIORITY) + self.reporter.add_message("Low", self.reporter.LOW_PRIORITY, False) if __name__ == "__main__": From 243c9e9021cd1183742a516aed0a432a9cc65b73 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Mon, 28 Sep 2015 15:52:09 -0700 Subject: [PATCH 30/78] Made cover and lint happy --- letsencrypt/cli.py | 2 +- letsencrypt/tests/reporter_test.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index bd49d110b..0b7d17909 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -272,7 +272,7 @@ def _report_new_cert(cert_path): reporter_util = zope.component.getUtility(interfaces.IReporter) reporter_util.add_message("Congratulations! Your certificate has been " "saved at {0}.".format(cert_path), - reporter.MEDIUM_PRIORITY) + reporter_util.MEDIUM_PRIORITY) def _auth_from_domains(le_client, config, domains, plugins): diff --git a/letsencrypt/tests/reporter_test.py b/letsencrypt/tests/reporter_test.py index 89bd9dfc7..ddf345c4c 100644 --- a/letsencrypt/tests/reporter_test.py +++ b/letsencrypt/tests/reporter_test.py @@ -78,12 +78,12 @@ class ReporterTest(unittest.TestCase): output = sys.stdout.getvalue() self.assertTrue("IMPORTANT NOTES:" in output) self.assertTrue("High" in output) - self.assertTrue("Med" in output) + self.assertTrue("Med" not in output) self.assertTrue("Low" not in output) def _add_messages(self): self.reporter.add_message("High", self.reporter.HIGH_PRIORITY) - self.reporter.add_message("Med", self.reporter.MEDIUM_PRIORITY) + self.reporter.add_message("Med", self.reporter.MEDIUM_PRIORITY, False) self.reporter.add_message("Low", self.reporter.LOW_PRIORITY, False) From 67ec4d09eef289b979f18b869c760cc997ef2f44 Mon Sep 17 00:00:00 2001 From: James Kasten Date: Mon, 28 Sep 2015 15:53:42 -0700 Subject: [PATCH 31/78] Put in dummy challenge --- acme/acme/challenges.py | 5 +++++ acme/acme/messages.py | 5 +++-- acme/acme/messages_test.py | 2 -- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/acme/acme/challenges.py b/acme/acme/challenges.py index 81711e605..1ffc6cc99 100644 --- a/acme/acme/challenges.py +++ b/acme/acme/challenges.py @@ -34,6 +34,11 @@ class DVChallenge(Challenge): # pylint: disable=abstract-method """Domain validation challenges.""" +class UnrecognizedChallenge(DVChallenge): + """Unrecognized challenge.""" + typ = "unknown" + + class ChallengeResponse(jose.TypedJSONObjectWithFields): # _fields_to_partial_json | pylint: disable=abstract-method """ACME challenge response.""" diff --git a/acme/acme/messages.py b/acme/acme/messages.py index 594b3d5c7..d6e9952c3 100644 --- a/acme/acme/messages.py +++ b/acme/acme/messages.py @@ -2,7 +2,6 @@ import collections from acme import challenges -from acme import errors from acme import fields from acme import jose from acme import util @@ -383,7 +382,9 @@ class Authorization(ResourceBody): try: challs.append(ChallengeBody.from_json(chall)) except jose.UnrecognizedTypeError: - continue + challs.append(ChallengeBody( + uri="UNKNOWN", chall=challenges.UnrecognizedChallenge, + status=STATUS_UNKNOWN)) return tuple(challs) @property diff --git a/acme/acme/messages_test.py b/acme/acme/messages_test.py index ac722909c..d7bbdb0e4 100644 --- a/acme/acme/messages_test.py +++ b/acme/acme/messages_test.py @@ -275,8 +275,6 @@ class AuthorizationTest(unittest.TestCase): from acme.messages import ChallengeBody from acme.messages import STATUS_VALID - unknown_chall = mock.MagicMock() - unknown_chall.to_json.side_effect = side_effect=jose.UnrecognizedTypeError self.challbs = ( ChallengeBody( uri='http://challb1', status=STATUS_VALID, From 5238f530924de2bf335b958a102b31306cf4a79d Mon Sep 17 00:00:00 2001 From: James Kasten Date: Mon, 28 Sep 2015 16:03:03 -0700 Subject: [PATCH 32/78] DVChallenge -> Challenge --- acme/acme/challenges.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme/acme/challenges.py b/acme/acme/challenges.py index 1ffc6cc99..fbb2e7418 100644 --- a/acme/acme/challenges.py +++ b/acme/acme/challenges.py @@ -34,7 +34,7 @@ class DVChallenge(Challenge): # pylint: disable=abstract-method """Domain validation challenges.""" -class UnrecognizedChallenge(DVChallenge): +class UnrecognizedChallenge(Challenge): """Unrecognized challenge.""" typ = "unknown" From ed7977fb039d74455d088a4bb11cbf2eaf91373b Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Mon, 28 Sep 2015 18:45:12 -0700 Subject: [PATCH 33/78] Added cli tests --- letsencrypt/tests/cli_test.py | 140 ++++++++++++++++++++++++++-------- 1 file changed, 107 insertions(+), 33 deletions(-) diff --git a/letsencrypt/tests/cli_test.py b/letsencrypt/tests/cli_test.py index 2e9f3330c..31cef584b 100644 --- a/letsencrypt/tests/cli_test.py +++ b/letsencrypt/tests/cli_test.py @@ -16,6 +16,9 @@ from letsencrypt.tests import renewer_test from letsencrypt.tests import test_util +CSR = test_util.vector_path('csr.der') + + class CLITest(unittest.TestCase): """Tests for different commands.""" @@ -65,40 +68,111 @@ class CLITest(unittest.TestCase): for r in xrange(len(flags)))): self._call(['plugins'] + list(args)) - @mock.patch("letsencrypt.cli.sys") + def test_auth_bad_args(self): + ret, _, _, _ = self._call(['-d', 'foo.bar', 'auth', '--csr', CSR]) + self.assertEqual(ret, '--domains and --csr are mutually exclusive') + + ret, _, _, _ = self._call(['-a', 'bad_auth', 'auth']) + self.assertEqual(ret, 'Authenticator could not be determined') + + @mock.patch('letsencrypt.cli.zope.component.getUtility') + def test_auth_new_request_success(self, mock_get_utility): + cert_path = '/etc/letsencrypt/live/foo.bar' + mock_lineage = mock.MagicMock(cert=cert_path) + mock_client = mock.MagicMock() + mock_client.obtain_and_enroll_certificate.return_value = mock_lineage + self._auth_new_request_common(mock_client) + self.assertEqual( + mock_client.obtain_and_enroll_certificate.call_count, 1) + self.assertTrue( + cert_path in mock_get_utility().add_message.call_args[0][0]) + + def test_auth_new_request_failure(self): + mock_client = mock.MagicMock() + mock_client.obtain_and_enroll_certificate.return_value = False + self.assertRaises(errors.Error, + self._auth_new_request_common, mock_client) + + def _auth_new_request_common(self, mock_client): + with mock.patch('letsencrypt.cli._treat_as_renewal') as mock_renewal: + mock_renewal.return_value = None + with mock.patch('letsencrypt.cli._init_le_client') as mock_init: + mock_init.return_value = mock_client + self._call(['-d', 'foo.bar', '-a', + 'standalone', '-i', 'bad', 'auth']) + + @mock.patch('letsencrypt.cli.zope.component.getUtility') + @mock.patch('letsencrypt.cli._treat_as_renewal') + @mock.patch('letsencrypt.cli._init_le_client') + def test_auth_renewal(self, mock_init, mock_renewal, mock_get_utility): + cert_path = '/etc/letsencrypt/live/foo.bar' + mock_lineage = mock.MagicMock(cert=cert_path) + mock_cert = mock.MagicMock(body='body') + mock_key = mock.MagicMock(pem='pem_key') + mock_renewal.return_value = mock_lineage + mock_client = mock.MagicMock() + mock_client.obtain_certificate.return_value = (mock_cert, 'chain', + mock_key, 'csr') + mock_init.return_value = mock_client + with mock.patch('letsencrypt.cli.OpenSSL'): + with mock.patch('letsencrypt.cli.crypto_util'): + self._call(['-d', 'foo.bar', '-a', 'standalone', 'auth']) + mock_client.obtain_certificate.assert_called_once_with(['foo.bar']) + self.assertEqual(mock_lineage.save_successor.call_count, 1) + mock_lineage.update_all_links_to.assert_called_once_with( + mock_lineage.latest_common_version()) + self.assertTrue( + cert_path in mock_get_utility().add_message.call_args[0][0]) + + @mock.patch('letsencrypt.cli.zope.component.getUtility') + @mock.patch('letsencrypt.cli._init_le_client') + def test_auth_csr(self, mock_init, mock_get_utility): + cert_path = '/etc/letsencrypt/live/foo.bar' + mock_client = mock.MagicMock() + mock_client.obtain_certificate_from_csr.return_value = ('certr', + 'chain') + mock_init.return_value = mock_client + self._call(['-a', 'standalone', 'auth', '--csr', CSR, + '--cert-path', cert_path, '--chain-path', '/']) + mock_client.save_certificate.assert_called_once_with( + 'certr', 'chain', cert_path, '/') + self.assertTrue( + cert_path in mock_get_utility().add_message.call_args[0][0]) + + @mock.patch('letsencrypt.cli.sys') def test_handle_exception(self, mock_sys): # pylint: disable=protected-access from letsencrypt import cli mock_open = mock.mock_open() - with mock.patch("letsencrypt.cli.open", mock_open, create=True): - exception = Exception("detail") + with mock.patch('letsencrypt.cli.open', mock_open, create=True): + exception = Exception('detail') cli._handle_exception( Exception, exc_value=exception, trace=None, args=None) - mock_open().write.assert_called_once_with("".join( + mock_open().write.assert_called_once_with(''.join( traceback.format_exception_only(Exception, exception))) error_msg = mock_sys.exit.call_args_list[0][0][0] - self.assertTrue("unexpected error" in error_msg) + self.assertTrue('unexpected error' in error_msg) - with mock.patch("letsencrypt.cli.open", mock_open, create=True): + with mock.patch('letsencrypt.cli.open', mock_open, create=True): mock_open.side_effect = [KeyboardInterrupt] - error = errors.Error("detail") + error = errors.Error('detail') cli._handle_exception( errors.Error, exc_value=error, trace=None, args=None) # assert_any_call used because sys.exit doesn't exit in cli.py - mock_sys.exit.assert_any_call("".join( + mock_sys.exit.assert_any_call(''.join( traceback.format_exception_only(errors.Error, error))) args = mock.MagicMock(debug=False) cli._handle_exception( - Exception, exc_value=Exception("detail"), trace=None, args=args) + Exception, exc_value=Exception('detail'), trace=None, args=args) error_msg = mock_sys.exit.call_args_list[-1][0][0] - self.assertTrue("unexpected error" in error_msg) + self.assertTrue('unexpected error' in error_msg) - interrupt = KeyboardInterrupt("detail") + interrupt = KeyboardInterrupt('detail') cli._handle_exception( KeyboardInterrupt, exc_value=interrupt, trace=None, args=None) - mock_sys.exit.assert_called_with("".join( + mock_sys.exit.assert_called_with(''.join( traceback.format_exception_only(KeyboardInterrupt, interrupt))) @@ -108,13 +182,13 @@ class DetermineAccountTest(unittest.TestCase): def setUp(self): self.args = mock.MagicMock(account=None, email=None) self.config = configuration.NamespaceConfig(self.args) - self.accs = [mock.MagicMock(id="x"), mock.MagicMock(id="y")] + self.accs = [mock.MagicMock(id='x'), mock.MagicMock(id='y')] self.account_storage = account.AccountMemoryStorage() def _call(self): # pylint: disable=protected-access from letsencrypt.cli import _determine_account - with mock.patch("letsencrypt.cli.account.AccountFileStorage") as mock_storage: + with mock.patch('letsencrypt.cli.account.AccountFileStorage') as mock_storage: mock_storage.return_value = self.account_storage return _determine_account(self.args, self.config) @@ -131,7 +205,7 @@ class DetermineAccountTest(unittest.TestCase): self.assertEqual(self.accs[0].id, self.args.account) self.assertTrue(self.args.email is None) - @mock.patch("letsencrypt.client.display_ops.choose_account") + @mock.patch('letsencrypt.client.display_ops.choose_account') def test_multiple_accounts(self, mock_choose_accounts): for acc in self.accs: self.account_storage.save(acc) @@ -142,11 +216,11 @@ class DetermineAccountTest(unittest.TestCase): self.assertEqual(self.accs[1].id, self.args.account) self.assertTrue(self.args.email is None) - @mock.patch("letsencrypt.client.display_ops.get_email") + @mock.patch('letsencrypt.client.display_ops.get_email') def test_no_accounts_no_email(self, mock_get_email): - mock_get_email.return_value = "foo@bar.baz" + mock_get_email.return_value = 'foo@bar.baz' - with mock.patch("letsencrypt.cli.client") as client: + with mock.patch('letsencrypt.cli.client') as client: client.register.return_value = ( self.accs[0], mock.sentinel.acme) self.assertEqual((self.accs[0], mock.sentinel.acme), self._call()) @@ -154,15 +228,15 @@ class DetermineAccountTest(unittest.TestCase): self.config, self.account_storage, tos_cb=mock.ANY) self.assertEqual(self.accs[0].id, self.args.account) - self.assertEqual("foo@bar.baz", self.args.email) + self.assertEqual('foo@bar.baz', self.args.email) def test_no_accounts_email(self): - self.args.email = "other email" - with mock.patch("letsencrypt.cli.client") as client: + self.args.email = 'other email' + with mock.patch('letsencrypt.cli.client') as client: client.register.return_value = (self.accs[1], mock.sentinel.acme) self._call() self.assertEqual(self.accs[1].id, self.args.account) - self.assertEqual("other email", self.args.email) + self.assertEqual('other email', self.args.email) class DuplicativeCertsTest(renewer_test.BaseRenewableCertTest): @@ -176,36 +250,36 @@ class DuplicativeCertsTest(renewer_test.BaseRenewableCertTest): def tearDown(self): shutil.rmtree(self.tempdir) - @mock.patch("letsencrypt.le_util.make_or_verify_dir") + @mock.patch('letsencrypt.le_util.make_or_verify_dir') def test_find_duplicative_names(self, unused_makedir): from letsencrypt.cli import _find_duplicative_certs - test_cert = test_util.load_vector("cert-san.pem") - with open(self.test_rc.cert, "w") as f: + test_cert = test_util.load_vector('cert-san.pem') + with open(self.test_rc.cert, 'w') as f: f.write(test_cert) # No overlap at all - result = _find_duplicative_certs(["wow.net", "hooray.org"], + result = _find_duplicative_certs(['wow.net', 'hooray.org'], self.config, self.cli_config) self.assertEqual(result, (None, None)) # Totally identical - result = _find_duplicative_certs(["example.com", "www.example.com"], + result = _find_duplicative_certs(['example.com', 'www.example.com'], self.config, self.cli_config) - self.assertTrue(result[0].configfile.filename.endswith("example.org.conf")) + self.assertTrue(result[0].configfile.filename.endswith('example.org.conf')) self.assertEqual(result[1], None) # Superset - result = _find_duplicative_certs(["example.com", "www.example.com", - "something.new"], self.config, + result = _find_duplicative_certs(['example.com', 'www.example.com', + 'something.new'], self.config, self.cli_config) self.assertEqual(result[0], None) - self.assertTrue(result[1].configfile.filename.endswith("example.org.conf")) + self.assertTrue(result[1].configfile.filename.endswith('example.org.conf')) # Partial overlap doesn't count - result = _find_duplicative_certs(["example.com", "something.new"], + result = _find_duplicative_certs(['example.com', 'something.new'], self.config, self.cli_config) self.assertEqual(result, (None, None)) -if __name__ == "__main__": +if __name__ == '__main__': unittest.main() # pragma: no cover From dc0b26c2781132a1c3f0622c40c93f4e64bf1f53 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Mon, 28 Sep 2015 18:47:15 -0700 Subject: [PATCH 34/78] Raised cover percentage --- tox.cover.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.cover.sh b/tox.cover.sh index aa5e3ed88..edfd9b81a 100755 --- a/tox.cover.sh +++ b/tox.cover.sh @@ -16,7 +16,7 @@ fi cover () { if [ "$1" = "letsencrypt" ]; then - min=96 + min=97 elif [ "$1" = "acme" ]; then min=100 elif [ "$1" = "letsencrypt_apache" ]; then From ad1fce03f77feddcbf0ef96d1ff63ed40e44576f Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Tue, 29 Sep 2015 06:47:15 +0000 Subject: [PATCH 35/78] UnrecognizedChallenge (fixes #855). Overrides quick fix from #856. --- acme/acme/challenges.py | 37 ++++++++++++++++++++++++++++++++----- acme/acme/messages.py | 14 +------------- 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/acme/acme/challenges.py b/acme/acme/challenges.py index fbb2e7418..4731c043f 100644 --- a/acme/acme/challenges.py +++ b/acme/acme/challenges.py @@ -25,6 +25,14 @@ class Challenge(jose.TypedJSONObjectWithFields): """ACME challenge.""" TYPES = {} + @classmethod + def from_json(cls, jobj): + try: + return super(Challenge, cls).from_json(jobj) + except jose.UnrecognizedTypeError as error: + logger.debug(error) + return UnrecognizedChallenge.from_json(jobj) + class ContinuityChallenge(Challenge): # pylint: disable=abstract-method """Client validation challenges.""" @@ -34,11 +42,6 @@ class DVChallenge(Challenge): # pylint: disable=abstract-method """Domain validation challenges.""" -class UnrecognizedChallenge(Challenge): - """Unrecognized challenge.""" - typ = "unknown" - - class ChallengeResponse(jose.TypedJSONObjectWithFields): # _fields_to_partial_json | pylint: disable=abstract-method """ACME challenge response.""" @@ -47,6 +50,30 @@ class ChallengeResponse(jose.TypedJSONObjectWithFields): resource = fields.Resource(resource_type) +class UnrecognizedChallenge(Challenge): + """Unrecognized challenge. + + ACME specification defines a generic framework for challenges and + defines some standard challenges that are implemented in this + module. However, other implementations (including peers) might + define additional challenge types, which should be ignored if + unrecognized. + + :ivar jobj: Original JSON decoded object. + + """ + + def __init__(self, jobj): + object.__setattr__(self, "jobj", jobj) + + def to_partial_json(self): + return self.jobj + + @classmethod + def from_json(cls, jobj): + return cls(jobj) + + @Challenge.register class SimpleHTTP(DVChallenge): """ACME "simpleHttp" challenge. diff --git a/acme/acme/messages.py b/acme/acme/messages.py index d6e9952c3..02ae24c8f 100644 --- a/acme/acme/messages.py +++ b/acme/acme/messages.py @@ -373,19 +373,7 @@ class Authorization(ResourceBody): @challenges.decoder def challenges(value): # pylint: disable=missing-docstring,no-self-argument - # The from_json method raises errors.UnrecognizedTypeError when a - # challenge of unknown type is encountered. We want to ignore this - # case. This forces us to do an explicit iteration, since list - # comprehensions can't handle exceptions. - challs = [] - for chall in value: - try: - challs.append(ChallengeBody.from_json(chall)) - except jose.UnrecognizedTypeError: - challs.append(ChallengeBody( - uri="UNKNOWN", chall=challenges.UnrecognizedChallenge, - status=STATUS_UNKNOWN)) - return tuple(challs) + return tuple(ChallengeBody.from_json(chall) for chall in value) @property def resolved_combinations(self): From 0ffef20a20522cf060c8c75f84ad6ab9a77470d2 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Tue, 29 Sep 2015 07:02:33 +0000 Subject: [PATCH 36/78] UnrecognizedChallenge: fix tests and lint. --- acme/acme/challenges.py | 2 ++ acme/acme/challenges_test.py | 26 ++++++++++++++++++++++++++ acme/acme/messages_test.py | 18 ------------------ 3 files changed, 28 insertions(+), 18 deletions(-) diff --git a/acme/acme/challenges.py b/acme/acme/challenges.py index 4731c043f..d81e77f83 100644 --- a/acme/acme/challenges.py +++ b/acme/acme/challenges.py @@ -64,9 +64,11 @@ class UnrecognizedChallenge(Challenge): """ def __init__(self, jobj): + super(UnrecognizedChallenge, self).__init__() object.__setattr__(self, "jobj", jobj) def to_partial_json(self): + # pylint: disable=no-member return self.jobj @classmethod diff --git a/acme/acme/challenges_test.py b/acme/acme/challenges_test.py index c82d95e19..ed44d4c45 100644 --- a/acme/acme/challenges_test.py +++ b/acme/acme/challenges_test.py @@ -17,6 +17,32 @@ CERT = test_util.load_cert('cert.pem') KEY = test_util.load_rsa_private_key('rsa512_key.pem') +class ChallengeTest(unittest.TestCase): + + def test_from_json_unrecognized(self): + from acme.challenges import Challenge + from acme.challenges import UnrecognizedChallenge + chall = UnrecognizedChallenge({"type": "foo"}) + # pylint: disable=no-member + self.assertEqual(chall, Challenge.from_json(chall.jobj)) + + +class UnrecognizedChallengeTest(unittest.TestCase): + + def setUp(self): + from acme.challenges import UnrecognizedChallenge + self.jobj = {"type": "foo"} + self.chall = UnrecognizedChallenge(self.jobj) + + def test_to_partial_json(self): + self.assertEqual(self.jobj, self.chall.to_partial_json()) + + def test_from_json(self): + from acme.challenges import UnrecognizedChallenge + self.assertEqual( + self.chall, UnrecognizedChallenge.from_json(self.jobj)) + + class SimpleHTTPTest(unittest.TestCase): def setUp(self): diff --git a/acme/acme/messages_test.py b/acme/acme/messages_test.py index d7bbdb0e4..d2d859bc5 100644 --- a/acme/acme/messages_test.py +++ b/acme/acme/messages_test.py @@ -301,19 +301,6 @@ class AuthorizationTest(unittest.TestCase): 'combinations': combinations, } - # For unknown challenge types - self.jmsg_unknown_chall = { - 'resource': 'challenge', - 'uri': 'random_uri', - 'type': 'unknown', - 'tls': True, - } - - self.jobj_from_unknown = { - 'identifier': identifier.to_json(), - 'challenges': [self.jmsg_unknown_chall], - } - def test_from_json(self): from acme.messages import Authorization Authorization.from_json(self.jobj_from) @@ -328,11 +315,6 @@ class AuthorizationTest(unittest.TestCase): (self.challbs[1], self.challbs[2]), )) - def test_unknown_chall_type(self): - """Just make sure an error isn't thrown.""" - from acme.messages import Authorization - Authorization.from_json(self.jobj_from_unknown) - class AuthorizationResourceTest(unittest.TestCase): """Tests for acme.messages.AuthorizationResource.""" From dcd274ed93182caaf225e33d8efbb50666bb49fa Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Tue, 29 Sep 2015 11:06:02 -0700 Subject: [PATCH 37/78] Marked Nginx as Alpha --- letsencrypt-nginx/letsencrypt_nginx/configurator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt-nginx/letsencrypt_nginx/configurator.py b/letsencrypt-nginx/letsencrypt_nginx/configurator.py index 2899e1f76..3f6d6f327 100644 --- a/letsencrypt-nginx/letsencrypt_nginx/configurator.py +++ b/letsencrypt-nginx/letsencrypt_nginx/configurator.py @@ -56,7 +56,7 @@ class NginxConfigurator(common.Plugin): zope.interface.implements(interfaces.IAuthenticator, interfaces.IInstaller) zope.interface.classProvides(interfaces.IPluginFactory) - description = "Nginx Web Server" + description = "Nginx Web Server - Alpha" @classmethod def add_parser_arguments(cls, add): From 312057b1b817254914256972dc326af3dbdece48 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Tue, 29 Sep 2015 12:54:52 -0700 Subject: [PATCH 38/78] changes += kuba_feedback --- letsencrypt/tests/cli_test.py | 13 ++++++++----- letsencrypt/tests/reporter_test.py | 6 ++++-- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/letsencrypt/tests/cli_test.py b/letsencrypt/tests/cli_test.py index 31cef584b..a59bc414e 100644 --- a/letsencrypt/tests/cli_test.py +++ b/letsencrypt/tests/cli_test.py @@ -98,8 +98,7 @@ class CLITest(unittest.TestCase): mock_renewal.return_value = None with mock.patch('letsencrypt.cli._init_le_client') as mock_init: mock_init.return_value = mock_client - self._call(['-d', 'foo.bar', '-a', - 'standalone', '-i', 'bad', 'auth']) + self._call(['-d', 'foo.bar', '-a', 'standalone', 'auth']) @mock.patch('letsencrypt.cli.zope.component.getUtility') @mock.patch('letsencrypt.cli._treat_as_renewal') @@ -124,16 +123,20 @@ class CLITest(unittest.TestCase): self.assertTrue( cert_path in mock_get_utility().add_message.call_args[0][0]) + @mock.patch('letsencrypt.cli.display_ops.pick_installer') @mock.patch('letsencrypt.cli.zope.component.getUtility') @mock.patch('letsencrypt.cli._init_le_client') - def test_auth_csr(self, mock_init, mock_get_utility): + def test_auth_csr(self, mock_init, mock_get_utility, mock_pick_installer): cert_path = '/etc/letsencrypt/live/foo.bar' mock_client = mock.MagicMock() mock_client.obtain_certificate_from_csr.return_value = ('certr', 'chain') mock_init.return_value = mock_client - self._call(['-a', 'standalone', 'auth', '--csr', CSR, - '--cert-path', cert_path, '--chain-path', '/']) + installer = 'installer' + self._call( + ['-a', 'standalone', '-i', installer, 'auth', '--csr', CSR, + '--cert-path', cert_path, '--chain-path', '/']) + self.assertEqual(mock_pick_installer.call_args[0][1], installer) mock_client.save_certificate.assert_called_once_with( 'certr', 'chain', cert_path, '/') self.assertTrue( diff --git a/letsencrypt/tests/reporter_test.py b/letsencrypt/tests/reporter_test.py index ddf345c4c..c848b1cab 100644 --- a/letsencrypt/tests/reporter_test.py +++ b/letsencrypt/tests/reporter_test.py @@ -83,8 +83,10 @@ class ReporterTest(unittest.TestCase): def _add_messages(self): self.reporter.add_message("High", self.reporter.HIGH_PRIORITY) - self.reporter.add_message("Med", self.reporter.MEDIUM_PRIORITY, False) - self.reporter.add_message("Low", self.reporter.LOW_PRIORITY, False) + self.reporter.add_message( + "Med", self.reporter.MEDIUM_PRIORITY, on_crash=False) + self.reporter.add_message( + "Low", self.reporter.LOW_PRIORITY, on_crash=False) if __name__ == "__main__": From 2e0fd36c2831db4fcdaefdd5c43fac41ee7fbac6 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Tue, 29 Sep 2015 21:01:02 +0000 Subject: [PATCH 39/78] Improve flag and help processing * letsencrypt --help $SUBCOMMAND now works. Fixes #787 #819 * subcommand arguments are now actually argument groups, so that all flags can be placed before or after subcommand verbs as the user wishes Fixes: #820 A limitation: * args like --cert-path were previously present for multiple verbs (auth/install/revoke) with separate docs; they are now in the "paths" topic. That's fine, though it would be good to *also* list them when the user types letsencrypt --help install. --- letsencrypt/cli.py | 64 ++++++++++++++++++++++------------------------ 1 file changed, 30 insertions(+), 34 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 53609009b..ac2c55551 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -489,8 +489,6 @@ class SilentParser(object): # pylint: disable=too-few-public-methods self.parser.add_argument(*args, **kwargs) -HELP_TOPICS = ["all", "security", "paths", "automation", "testing", "plugins"] - class HelpfulArgumentParser(object): """Argparse Wrapper. @@ -529,12 +527,17 @@ class HelpfulArgumentParser(object): def preprocess_args(self, args): """Work around some limitations in argparse. - Currently: add the default verb "run" as a default, and ensure that the + Currently: add the default verb "run" as a default, and ensure that the subcommand / verb comes last. """ + + if "-h" in args or "--help" in args: + # all verbs double as help arguments; don't get them confused + return args + for i,token in enumerate(args): if token in VERBS: - reordered = args[:i] + args[i+1:] + [args[i]] + reordered = args[:i] + args[i+1:] + [args[i]] return reordered return args + ["run"] @@ -717,6 +720,9 @@ def create_parser(plugins, args): VERBS = ["run", "auth", "install", "revoke", "rollback", "config_changes", "plugins", "--help", "-h"] +HELP_TOPICS = (["all", "security", "paths", "automation", "testing", "apache", "nginx"] + + [v for v in VERBS if "-" not in v]) + def _create_subparsers(helpful): subparsers = helpful.parser.add_subparsers(metavar="SUBCOMMAND") @@ -732,53 +738,34 @@ def _create_subparsers(helpful): add_subparser("run", run) parser_auth = add_subparser("auth", auth) - helpful.add_group("auth", "Options for modifying how a cert is obtained") + helpful.add_group("auth", description="Options for modifying how a cert is obtained") parser_install = add_subparser("install", install) - helpful.add_group("install", "Options for modifying how a cert is deployed") + helpful.add_group("install", description="Options for modifying how a cert is deployed") parser_revoke = add_subparser("revoke", revoke) - helpful.add_group("revoke", "Options for revocation of certs") + helpful.add_group("revoke", description="Options for revocation of certs") parser_rollback = add_subparser("rollback", rollback) - helpful.add_group("rollback", "Options for reverting config changes") + helpful.add_group("rollback", description="Options for reverting config changes") add_subparser("config_changes", config_changes) parser_plugins = add_subparser("plugins", plugins_cmd) - helpful.add_group("plugins", "Plugin options") + helpful.add_group("plugins", description="Plugin options") - helpful.add("auth", - "--csr", type=read_file, help="Path to a Certificate Signing Request (CSR) in DER format.") - helpful.add("auth", - "--cert-path", default=flag_default("auth_cert_path"), - help="When using --csr this is where certificate is saved.") helpful.add("auth", - "--chain-path", default=flag_default("auth_chain_path"), - help="When using --csr this is where certificate chain is saved.") - - helpful.add("install", - "--cert-path", required=True, help="Path to a certificate that is going to be installed.") - helpful.add("install", - "--key-path", required=True, help="Accompanying private key") - helpful.add("install", - "--chain-path", help="Accompanying path to a certificate chain.") - helpful.add("revoke", - "--cert-path", type=read_file, help="Revoke a specific certificate.", required=True) - helpful.add("revoke", - "--key-path", type=read_file, - help="Revoke certificate using its accompanying key. Useful if Account Key is lost.") - - helpful.add("rollback", + "--csr", type=read_file, help="Path to a Certificate Signing Request (CSR) in DER format.") + helpful.add("rollback", "--checkpoints", type=int, metavar="N", default=flag_default("rollback_checkpoints"), help="Revert configuration N number of checkpoints.") - helpful.add("plugins", + helpful.add("plugins", "--init", action="store_true", help="Initialize plugins.") - helpful.add("plugins", + helpful.add("plugins", "--prepare", action="store_true", help="Initialize and prepare plugins.") - helpful.add("plugins", + helpful.add("plugins", "--authenticators", action="append_const", dest="ifaces", const=interfaces.IAuthenticator, help="Limit to authenticator plugins only.") - helpful.add("plugins", + helpful.add("plugins", "--installers", action="append_const", dest="ifaces", const=interfaces.IInstaller, help="Limit to installer plugins only.") @@ -787,6 +774,15 @@ def _paths_parser(helpful): add = helpful.add helpful.add_group( "paths", description="Arguments changing execution paths & servers") + helpful.add("paths", + "--cert-path", default=flag_default("auth_cert_path"), + help="Path to where certificate is saved (with auth), " + "installed (with install --csr) or revoked.") + helpful.add("paths", + "--key-path", required=True, + help="Path to private key for cert creation or revocation (if account key is missing)") + helpful.add("paths", + "--chain-path", help="Accompanying path to a certificate chain.") add("paths", "--config-dir", default=flag_default("config_dir"), help=config_help("config_dir")) add("paths", "--work-dir", default=flag_default("work_dir"), From a0af023b1436e27f5c1a7626aeeab374d927cf3b Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Tue, 29 Sep 2015 14:48:26 -0700 Subject: [PATCH 40/78] --key-path is mandatory for install, optional for revoke --- letsencrypt/cli.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index ac2c55551..8042173e8 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -530,7 +530,6 @@ class HelpfulArgumentParser(object): Currently: add the default verb "run" as a default, and ensure that the subcommand / verb comes last. """ - if "-h" in args or "--help" in args: # all verbs double as help arguments; don't get them confused return args @@ -779,7 +778,7 @@ def _paths_parser(helpful): help="Path to where certificate is saved (with auth), " "installed (with install --csr) or revoked.") helpful.add("paths", - "--key-path", required=True, + "--key-path", required=("install" in helpful.args), help="Path to private key for cert creation or revocation (if account key is missing)") helpful.add("paths", "--chain-path", help="Accompanying path to a certificate chain.") From 05d439a33937c4c46d2ee949dc70c9126f463efd Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Tue, 29 Sep 2015 14:48:40 -0700 Subject: [PATCH 41/78] Update cli tests We don't expect to error out if called with no args --- letsencrypt/tests/cli_test.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/letsencrypt/tests/cli_test.py b/letsencrypt/tests/cli_test.py index 2e9f3330c..992b254a7 100644 --- a/letsencrypt/tests/cli_test.py +++ b/letsencrypt/tests/cli_test.py @@ -40,7 +40,9 @@ class CLITest(unittest.TestCase): return ret, stdout, stderr, client def test_no_flags(self): - self.assertRaises(SystemExit, self._call, []) + with mock.patch('letsencrypt.cli.run') as mock_run: + self._call([]) + self.assertEqual(1, mock_run.call_count) def test_help(self): self.assertRaises(SystemExit, self._call, ['--help']) From 2297349b95f1451d10caae24297ba3b84dd7d6ce Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Tue, 29 Sep 2015 16:56:36 -0700 Subject: [PATCH 42/78] lintian --- letsencrypt/cli.py | 24 ++++++++++++------------ letsencrypt/tests/cli_test.py | 4 ++-- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 0d72a3eb5..b7efa041a 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -500,7 +500,6 @@ class SilentParser(object): # pylint: disable=too-few-public-methods self.parser.add_argument(*args, **kwargs) - class HelpfulArgumentParser(object): """Argparse Wrapper. @@ -545,7 +544,7 @@ class HelpfulArgumentParser(object): # all verbs double as help arguments; don't get them confused return args - for i,token in enumerate(args): + for i, token in enumerate(args): if token in VERBS: reordered = args[:i] + args[i+1:] + [args[i]] return reordered @@ -745,18 +744,21 @@ def _create_subparsers(helpful): # the order of add_subparser() calls is important: it defines the # order in which subparser names will be displayed in --help - add_subparser("run", run) + # these add_subparser objects return objects to which arguments could be + # attached, but they have annoying arg ordering constrains so we use + # groups instead: https://github.com/letsencrypt/letsencrypt/issues/820 - parser_auth = add_subparser("auth", auth) + add_subparser("run", run) + add_subparser("auth", auth) helpful.add_group("auth", description="Options for modifying how a cert is obtained") - parser_install = add_subparser("install", install) + add_subparser("install", install) helpful.add_group("install", description="Options for modifying how a cert is deployed") - parser_revoke = add_subparser("revoke", revoke) + add_subparser("revoke", revoke) helpful.add_group("revoke", description="Options for revocation of certs") - parser_rollback = add_subparser("rollback", rollback) + add_subparser("rollback", rollback) helpful.add_group("rollback", description="Options for reverting config changes") add_subparser("config_changes", config_changes) - parser_plugins = add_subparser("plugins", plugins_cmd) + add_subparser("plugins", plugins_cmd) helpful.add_group("plugins", description="Plugin options") helpful.add("auth", @@ -769,12 +771,10 @@ def _create_subparsers(helpful): helpful.add("plugins", "--init", action="store_true", help="Initialize plugins.") helpful.add("plugins", - "--prepare", action="store_true", - help="Initialize and prepare plugins.") + "--prepare", action="store_true", help="Initialize and prepare plugins.") helpful.add("plugins", "--authenticators", action="append_const", dest="ifaces", - const=interfaces.IAuthenticator, - help="Limit to authenticator plugins only.") + const=interfaces.IAuthenticator, help="Limit to authenticator plugins only.") helpful.add("plugins", "--installers", action="append_const", dest="ifaces", const=interfaces.IInstaller, help="Limit to installer plugins only.") diff --git a/letsencrypt/tests/cli_test.py b/letsencrypt/tests/cli_test.py index ce32b8f78..9a99a74cc 100644 --- a/letsencrypt/tests/cli_test.py +++ b/letsencrypt/tests/cli_test.py @@ -44,8 +44,8 @@ class CLITest(unittest.TestCase): def test_no_flags(self): with mock.patch('letsencrypt.cli.run') as mock_run: - self._call([]) - self.assertEqual(1, mock_run.call_count) + self._call([]) + self.assertEqual(1, mock_run.call_count) def test_help(self): self.assertRaises(SystemExit, self._call, ['--help']) From 6b6bc038827e359173039a5cb229104ef257e127 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Tue, 29 Sep 2015 17:12:38 -0700 Subject: [PATCH 43/78] --cert-path was required for install and revoke Oops --- letsencrypt/cli.py | 1 + 1 file changed, 1 insertion(+) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index b7efa041a..82bd57ec8 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -786,6 +786,7 @@ def _paths_parser(helpful): "paths", description="Arguments changing execution paths & servers") helpful.add("paths", "--cert-path", default=flag_default("auth_cert_path"), + required=("install" in helpful.args or "revoke" in helpful.args), help="Path to where certificate is saved (with auth), " "installed (with install --csr) or revoked.") helpful.add("paths", From 18dacc528df67e703336bd778518a25f4850b345 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Tue, 29 Sep 2015 18:08:58 -0700 Subject: [PATCH 44/78] Preserve all argparse parameters Try to restore all variants that applied to the different subcomannds --- letsencrypt/cli.py | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 82bd57ec8..ac79ab93c 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -521,6 +521,7 @@ class HelpfulArgumentParser(object): self.parser._add_config_file_help = False # pylint: disable=protected-access self.silent_parser = SilentParser(self.parser) + self.verb = None self.args = self.preprocess_args(args) help1 = self.prescan_for_flag("-h", self.help_topics) help2 = self.prescan_for_flag("--help", self.help_topics) @@ -542,12 +543,16 @@ class HelpfulArgumentParser(object): """ if "-h" in args or "--help" in args: # all verbs double as help arguments; don't get them confused + self.verb = "help" return args for i, token in enumerate(args): if token in VERBS: reordered = args[:i] + args[i+1:] + [args[i]] + self.verb = token return reordered + + self.verb = "run" return args + ["run"] def prescan_for_flag(self, flag, possible_arguments): @@ -782,18 +787,28 @@ def _create_subparsers(helpful): def _paths_parser(helpful): add = helpful.add + verb = helpful.verb helpful.add_group( "paths", description="Arguments changing execution paths & servers") - helpful.add("paths", - "--cert-path", default=flag_default("auth_cert_path"), - required=("install" in helpful.args or "revoke" in helpful.args), - help="Path to where certificate is saved (with auth), " - "installed (with install --csr) or revoked.") - helpful.add("paths", - "--key-path", required=("install" in helpful.args), + + cph = "Path to where cert is saved (with auth), installed (with install --csr) or revoked." + if verb == "auth": + add("paths", "--cert-path", default=flag_default("auth_cert_path"), help=cph) + elif verb == "revoke": + add("paths", "--cert-path", type=read_file, required=True, help=cph) + else: + add("paths", "--cert-path", help=cph, required=(verb == "install")) + + # revoke --key-path reads a file, install --key-path takes a string + add("paths", "--key-path", type=((verb == "revoke" and read_file) or str), + required=(verb == "install"), help="Path to private key for cert creation or revocation (if account key is missing)") - helpful.add("paths", - "--chain-path", help="Accompanying path to a certificate chain.") + + default_cp = None + if verb == "auth": + default_cp = flag_default("auth_chain_path") + add("paths", "--chain-path", default=default_cp, + help="Accompanying path to a certificate chain.") add("paths", "--config-dir", default=flag_default("config_dir"), help=config_help("config_dir")) add("paths", "--work-dir", default=flag_default("work_dir"), From 627fca37b4e45d300fdb3a023943b11c6bbae593 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Tue, 29 Sep 2015 18:18:18 -0700 Subject: [PATCH 45/78] We didn't actually need to define --help as a verb --- letsencrypt/cli.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index ac79ab93c..efc0f9f70 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -732,10 +732,10 @@ def create_parser(plugins, args): # For now unfortunately this constant just needs to match the code below; # there isn't an elegant way to autogenerate it in time. VERBS = ["run", "auth", "install", "revoke", "rollback", "config_changes", - "plugins", "--help", "-h"] + "plugins"] HELP_TOPICS = (["all", "security", "paths", "automation", "testing", "apache", "nginx"] + - [v for v in VERBS if "-" not in v]) + [v for v in VERBS]) def _create_subparsers(helpful): From 1e3c92c714bb382298343d5c14f14aa896e765ab Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 30 Sep 2015 11:49:46 -0700 Subject: [PATCH 46/78] Cleanup the verb -> subparser mapping --- letsencrypt/cli.py | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index efc0f9f70..fd9d8cbb6 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -9,6 +9,7 @@ import os import pkg_resources import sys import time +import types import traceback import configargparse @@ -731,17 +732,16 @@ def create_parser(plugins, args): # For now unfortunately this constant just needs to match the code below; # there isn't an elegant way to autogenerate it in time. -VERBS = ["run", "auth", "install", "revoke", "rollback", "config_changes", - "plugins"] - -HELP_TOPICS = (["all", "security", "paths", "automation", "testing", "apache", "nginx"] + - [v for v in VERBS]) - +VERBS = ["run", "auth", "install", "revoke", "rollback", "config_changes", "plugins"] +HELP_TOPICS = (["all", "security", "paths", "automation", "testing", "apache", "nginx"] + VERBS) def _create_subparsers(helpful): subparsers = helpful.parser.add_subparsers(metavar="SUBCOMMAND") - def add_subparser(name, func): # pylint: disable=missing-docstring + def add_subparser(name): # pylint: disable=missing-docstring + # Each subcommand is implemented by a function of the same name + func = eval(name) # pylint: disable=eval-used + assert isinstance(func, types.FunctionType), "squirrels in namespace" subparser = subparsers.add_parser( name, help=func.__doc__.splitlines()[0], description=func.__doc__) subparser.set_defaults(func=func) @@ -752,18 +752,13 @@ def _create_subparsers(helpful): # these add_subparser objects return objects to which arguments could be # attached, but they have annoying arg ordering constrains so we use # groups instead: https://github.com/letsencrypt/letsencrypt/issues/820 + for v in VERBS: + add_subparser(v) - add_subparser("run", run) - add_subparser("auth", auth) helpful.add_group("auth", description="Options for modifying how a cert is obtained") - add_subparser("install", install) helpful.add_group("install", description="Options for modifying how a cert is deployed") - add_subparser("revoke", revoke) helpful.add_group("revoke", description="Options for revocation of certs") - add_subparser("rollback", rollback) helpful.add_group("rollback", description="Options for reverting config changes") - add_subparser("config_changes", config_changes) - add_subparser("plugins", plugins_cmd) helpful.add_group("plugins", description="Plugin options") helpful.add("auth", From 2a3a111d628711e131ea202511a1383c10dfe378 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 30 Sep 2015 12:09:38 -0700 Subject: [PATCH 47/78] Disable pylint invalid-name It's clearly making our code harder to read and write --- .pylintrc | 2 +- acme/acme/challenges.py | 4 ++-- acme/acme/challenges_test.py | 2 +- acme/acme/jose/interfaces_test.py | 4 ++-- acme/acme/jose/json_util_test.py | 6 +++--- acme/acme/jose/jwa_test.py | 2 +- acme/acme/jose/jwk.py | 2 +- acme/acme/jose/util.py | 4 ++-- acme/acme/jose/util_test.py | 4 ++-- acme/acme/messages_test.py | 2 +- letsencrypt/account.py | 2 +- letsencrypt/display/enhancements.py | 2 +- letsencrypt/display/ops.py | 2 +- letsencrypt/log.py | 2 +- letsencrypt/plugins/common.py | 6 +++--- letsencrypt/tests/auth_handler_test.py | 2 +- letsencrypt/tests/log_test.py | 2 +- letsencrypt/tests/reverter_test.py | 8 ++++---- 18 files changed, 29 insertions(+), 29 deletions(-) diff --git a/.pylintrc b/.pylintrc index bf318704a..268d61ec6 100644 --- a/.pylintrc +++ b/.pylintrc @@ -38,7 +38,7 @@ load-plugins=linter_plugin # --enable=similarities". If you want to run only the classes checker, but have # no Warning level messages displayed, use"--disable=all --enable=classes # --disable=W" -disable=fixme,locally-disabled,abstract-class-not-used,bad-continuation,too-few-public-methods,no-self-use +disable=fixme,locally-disabled,abstract-class-not-used,bad-continuation,too-few-public-methods,no-self-use,invalid-name # abstract-class-not-used cannot be disabled locally (at least in pylint 1.4.1) diff --git a/acme/acme/challenges.py b/acme/acme/challenges.py index d81e77f83..f5763adc4 100644 --- a/acme/acme/challenges.py +++ b/acme/acme/challenges.py @@ -315,7 +315,7 @@ class DVSNIResponse(ChallengeResponse): validation = jose.Field("validation", decoder=jose.JWS.from_json) @property - def z(self): # pylint: disable=invalid-name + def z(self): """The ``z`` parameter. :rtype: bytes @@ -333,7 +333,7 @@ class DVSNIResponse(ChallengeResponse): :rtype: bytes """ - z = self.z # pylint: disable=invalid-name + z = self.z return z[:32] + b'.' + z[32:] + self.DOMAIN_SUFFIX @property diff --git a/acme/acme/challenges_test.py b/acme/acme/challenges_test.py index ed44d4c45..b3f48cdf2 100644 --- a/acme/acme/challenges_test.py +++ b/acme/acme/challenges_test.py @@ -269,7 +269,7 @@ class DVSNIResponseTest(unittest.TestCase): 'validation': self.validation.to_json(), } - # pylint: disable=invalid-name + label1 = b'e2df3498860637c667fedadc5a8494ec' label2 = b'09dcc75553c9b3bd73662b50e71b1e42' self.z = label1 + label2 diff --git a/acme/acme/jose/interfaces_test.py b/acme/acme/jose/interfaces_test.py index 380c3a2a5..a3ee124ff 100644 --- a/acme/acme/jose/interfaces_test.py +++ b/acme/acme/jose/interfaces_test.py @@ -8,7 +8,7 @@ class JSONDeSerializableTest(unittest.TestCase): def setUp(self): from acme.jose.interfaces import JSONDeSerializable - # pylint: disable=missing-docstring,invalid-name + # pylint: disable=missing-docstring class Basic(JSONDeSerializable): def __init__(self, v): @@ -53,7 +53,7 @@ class JSONDeSerializableTest(unittest.TestCase): self.nested = Basic([[self.basic1]]) self.tuple = Basic(('foo',)) - # pylint: disable=invalid-name + self.Basic = Basic self.Sequence = Sequence self.Mapping = Mapping diff --git a/acme/acme/jose/json_util_test.py b/acme/acme/jose/json_util_test.py index a055f3bf7..f751382e0 100644 --- a/acme/acme/jose/json_util_test.py +++ b/acme/acme/jose/json_util_test.py @@ -92,7 +92,7 @@ class JSONObjectWithFieldsMetaTest(unittest.TestCase): from acme.jose.json_util import JSONObjectWithFieldsMeta self.field = Field('Baz') self.field2 = Field('Baz2') - # pylint: disable=invalid-name,missing-docstring,too-few-public-methods + # pylint: disable=missing-docstring,too-few-public-methods # pylint: disable=blacklisted-name @six.add_metaclass(JSONObjectWithFieldsMeta) @@ -138,7 +138,7 @@ class JSONObjectWithFieldsTest(unittest.TestCase): from acme.jose.json_util import Field class MockJSONObjectWithFields(JSONObjectWithFields): - # pylint: disable=invalid-name,missing-docstring,no-self-argument + # pylint: disable=missing-docstring,no-self-argument # pylint: disable=too-few-public-methods x = Field('x', omitempty=True, encoder=(lambda x: x * 2), @@ -158,7 +158,7 @@ class JSONObjectWithFieldsTest(unittest.TestCase): raise errors.DeserializationError() return value - # pylint: disable=invalid-name + self.MockJSONObjectWithFields = MockJSONObjectWithFields self.mock = MockJSONObjectWithFields(x=None, y=2, z=3) diff --git a/acme/acme/jose/jwa_test.py b/acme/acme/jose/jwa_test.py index 3328d083a..8ca512043 100644 --- a/acme/acme/jose/jwa_test.py +++ b/acme/acme/jose/jwa_test.py @@ -26,7 +26,7 @@ class JWASignatureTest(unittest.TestCase): def verify(self, key, msg, sig): raise NotImplementedError() # pragma: no cover - # pylint: disable=invalid-name + self.Sig1 = MockSig('Sig1') self.Sig2 = MockSig('Sig2') diff --git a/acme/acme/jose/jwk.py b/acme/acme/jose/jwk.py index 7a976f189..67f243347 100644 --- a/acme/acme/jose/jwk.py +++ b/acme/acme/jose/jwk.py @@ -186,7 +186,7 @@ class JWKRSA(JWK): @classmethod def fields_from_json(cls, jobj): - # pylint: disable=invalid-name + n, e = (cls._decode_param(jobj[x]) for x in ('n', 'e')) public_numbers = rsa.RSAPublicNumbers(e=e, n=n) if 'd' not in jobj: # public key diff --git a/acme/acme/jose/util.py b/acme/acme/jose/util.py index ab3606efc..46c43bf35 100644 --- a/acme/acme/jose/util.py +++ b/acme/acme/jose/util.py @@ -7,7 +7,7 @@ import six class abstractclassmethod(classmethod): - # pylint: disable=invalid-name,too-few-public-methods + # pylint: disable=too-few-public-methods """Descriptor for an abstract classmethod. It augments the :mod:`abc` framework with an abstract @@ -172,7 +172,7 @@ class ImmutableMap(collections.Mapping, collections.Hashable): class frozendict(collections.Mapping, collections.Hashable): - # pylint: disable=invalid-name,too-few-public-methods + # pylint: disable=too-few-public-methods """Frozen dictionary.""" __slots__ = ('_items', '_keys') diff --git a/acme/acme/jose/util_test.py b/acme/acme/jose/util_test.py index 4cdd9127f..295c70fee 100644 --- a/acme/acme/jose/util_test.py +++ b/acme/acme/jose/util_test.py @@ -92,7 +92,7 @@ class ImmutableMapTest(unittest.TestCase): """Tests for acme.jose.util.ImmutableMap.""" def setUp(self): - # pylint: disable=invalid-name,too-few-public-methods + # pylint: disable=too-few-public-methods # pylint: disable=missing-docstring from acme.jose.util import ImmutableMap @@ -156,7 +156,7 @@ class ImmutableMapTest(unittest.TestCase): self.assertEqual("B(x='foo', y='bar')", repr(self.B(x='foo', y='bar'))) -class frozendictTest(unittest.TestCase): # pylint: disable=invalid-name +class frozendictTest(unittest.TestCase): """Tests for acme.jose.util.frozendict.""" def setUp(self): diff --git a/acme/acme/messages_test.py b/acme/acme/messages_test.py index d2d859bc5..718a936dd 100644 --- a/acme/acme/messages_test.py +++ b/acme/acme/messages_test.py @@ -64,7 +64,7 @@ class ConstantTest(unittest.TestCase): class MockConstant(_Constant): # pylint: disable=missing-docstring POSSIBLE_NAMES = {} - self.MockConstant = MockConstant # pylint: disable=invalid-name + self.MockConstant = MockConstant self.const_a = MockConstant('a') self.const_b = MockConstant('b') diff --git a/letsencrypt/account.py b/letsencrypt/account.py index c97e4f6fe..81d31b831 100644 --- a/letsencrypt/account.py +++ b/letsencrypt/account.py @@ -54,7 +54,7 @@ class Account(object): # pylint: disable=too-few-public-methods tz=pytz.UTC).replace(microsecond=0), creation_host=socket.getfqdn()) if meta is None else meta - self.id = hashlib.md5( # pylint: disable=invalid-name + self.id = hashlib.md5( self.key.key.public_key().public_bytes( encoding=serialization.Encoding.DER, format=serialization.PublicFormat.SubjectPublicKeyInfo) diff --git a/letsencrypt/display/enhancements.py b/letsencrypt/display/enhancements.py index 8edc72ba0..c56198161 100644 --- a/letsencrypt/display/enhancements.py +++ b/letsencrypt/display/enhancements.py @@ -11,7 +11,7 @@ from letsencrypt.display import util as display_util logger = logging.getLogger(__name__) # Define a helper function to avoid verbose code -util = zope.component.getUtility # pylint: disable=invalid-name +util = zope.component.getUtility def ask(enhancement): diff --git a/letsencrypt/display/ops.py b/letsencrypt/display/ops.py index 43705e309..cb424a81b 100644 --- a/letsencrypt/display/ops.py +++ b/letsencrypt/display/ops.py @@ -12,7 +12,7 @@ from letsencrypt.display import util as display_util logger = logging.getLogger(__name__) # Define a helper function to avoid verbose code -util = zope.component.getUtility # pylint: disable=invalid-name +util = zope.component.getUtility def choose_plugin(prepared, question): diff --git a/letsencrypt/log.py b/letsencrypt/log.py index e800d37c9..6436f6fc2 100644 --- a/letsencrypt/log.py +++ b/letsencrypt/log.py @@ -25,7 +25,7 @@ class DialogHandler(logging.Handler): # pylint: disable=too-few-public-methods logging.Handler.__init__(self, level) self.height = height self.width = width - # "dialog" collides with module name... pylint: disable=invalid-name + # "dialog" collides with module name... self.d = dialog.Dialog() if d is None else d self.lines = [] diff --git a/letsencrypt/plugins/common.py b/letsencrypt/plugins/common.py index 59598a35e..95ad56a0a 100644 --- a/letsencrypt/plugins/common.py +++ b/letsencrypt/plugins/common.py @@ -23,10 +23,10 @@ def dest_namespace(name): """ArgumentParser dest namespace (prefix of all destinations).""" return name.replace("-", "_") + "_" -private_ips_regex = re.compile( # pylint: disable=invalid-name +private_ips_regex = re.compile( r"(^127\.0\.0\.1)|(^10\.)|(^172\.1[6-9]\.)|" r"(^172\.2[0-9]\.)|(^172\.3[0-1]\.)|(^192\.168\.)") -hostname_regex = re.compile( # pylint: disable=invalid-name +hostname_regex = re.compile( r"^(([a-z0-9]|[a-z0-9][a-z0-9\-]*[a-z0-9])\.)*[a-z]+$", re.IGNORECASE) @@ -173,7 +173,7 @@ class Dvsni(object): achall.chall.encode("token") + '.pem') def _setup_challenge_cert(self, achall, s=None): - # pylint: disable=invalid-name + """Generate and write out challenge certificate.""" cert_path = self.get_cert_path(achall) key_path = self.get_key_path(achall) diff --git a/letsencrypt/tests/auth_handler_test.py b/letsencrypt/tests/auth_handler_test.py index ed29ead25..18ee56081 100644 --- a/letsencrypt/tests/auth_handler_test.py +++ b/letsencrypt/tests/auth_handler_test.py @@ -355,7 +355,7 @@ class GenChallengePathTest(unittest.TestCase): class MutuallyExclusiveTest(unittest.TestCase): """Tests for letsencrypt.auth_handler.mutually_exclusive.""" - # pylint: disable=invalid-name,missing-docstring,too-few-public-methods + # pylint: disable=missing-docstring,too-few-public-methods class A(object): pass diff --git a/letsencrypt/tests/log_test.py b/letsencrypt/tests/log_test.py index 50d0712e7..c1afd2c8a 100644 --- a/letsencrypt/tests/log_test.py +++ b/letsencrypt/tests/log_test.py @@ -8,7 +8,7 @@ import mock class DialogHandlerTest(unittest.TestCase): def setUp(self): - self.d = mock.MagicMock() # pylint: disable=invalid-name + self.d = mock.MagicMock() from letsencrypt.log import DialogHandler self.handler = DialogHandler(height=2, width=6, d=self.d) diff --git a/letsencrypt/tests/reverter_test.py b/letsencrypt/tests/reverter_test.py index 62c47f8d6..d31b6f2cc 100644 --- a/letsencrypt/tests/reverter_test.py +++ b/letsencrypt/tests/reverter_test.py @@ -85,7 +85,7 @@ class ReverterCheckpointLocalTest(unittest.TestCase): self.assertEqual(read_in(self.config1), "directive-dir1") def test_multiple_registration_fail_and_revert(self): - # pylint: disable=invalid-name + config3 = os.path.join(self.dir1, "config3.txt") update_file(config3, "Config3") config4 = os.path.join(self.dir2, "config4.txt") @@ -173,7 +173,7 @@ class ReverterCheckpointLocalTest(unittest.TestCase): self.assertRaises(errors.ReverterError, self.reverter.recovery_routine) def test_recover_checkpoint_revert_temp_failures(self): - # pylint: disable=invalid-name + mock_recover = mock.MagicMock( side_effect=errors.ReverterError("e")) @@ -291,7 +291,7 @@ class TestFullCheckpointsReverter(unittest.TestCase): errors.ReverterError, self.reverter.rollback_checkpoints, "one") def test_rollback_finalize_checkpoint_valid_inputs(self): - # pylint: disable=invalid-name + config3 = self._setup_three_checkpoints() # Check resulting backup directory @@ -334,7 +334,7 @@ class TestFullCheckpointsReverter(unittest.TestCase): @mock.patch("letsencrypt.reverter.os.rename") def test_finalize_checkpoint_no_rename_directory(self, mock_rename): - # pylint: disable=invalid-name + self.reverter.add_to_checkpoint(self.sets[0], "perm save") mock_rename.side_effect = OSError From 2d578468bde4cbba2138216633de44bfdd46cf04 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 30 Sep 2015 12:32:44 -0700 Subject: [PATCH 48/78] Use a verb -> function table instead of eval() - plugins_cmd() not plugins() broke the more minimalist eval() approach - more wrangling was required to mock out calls via the VERBS table --- letsencrypt/cli.py | 25 +++++++++++++++---------- letsencrypt/tests/cli_test.py | 2 ++ 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index fd9d8cbb6..66f991063 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -9,7 +9,6 @@ import os import pkg_resources import sys import time -import types import traceback import configargparse @@ -731,17 +730,23 @@ def create_parser(plugins, args): return helpful.parser, helpful.args # For now unfortunately this constant just needs to match the code below; -# there isn't an elegant way to autogenerate it in time. -VERBS = ["run", "auth", "install", "revoke", "rollback", "config_changes", "plugins"] -HELP_TOPICS = (["all", "security", "paths", "automation", "testing", "apache", "nginx"] + VERBS) +# there isn't an elegant way to autogenerate it in time. pylint: disable=bad-whitespace +VERBS = { + "run" : run, + "auth" : auth, + "install" : install, + "revoke" : revoke, + "rollback" : rollback, + "config_changes" : config_changes, + "plugins" : plugins_cmd +} +HELP_TOPICS = (["all", "security", "paths", "automation", "testing", "apache", "nginx"] + + VERBS.keys()) def _create_subparsers(helpful): subparsers = helpful.parser.add_subparsers(metavar="SUBCOMMAND") - def add_subparser(name): # pylint: disable=missing-docstring - # Each subcommand is implemented by a function of the same name - func = eval(name) # pylint: disable=eval-used - assert isinstance(func, types.FunctionType), "squirrels in namespace" + def add_subparser(name, func): # pylint: disable=missing-docstring subparser = subparsers.add_parser( name, help=func.__doc__.splitlines()[0], description=func.__doc__) subparser.set_defaults(func=func) @@ -752,8 +757,8 @@ def _create_subparsers(helpful): # these add_subparser objects return objects to which arguments could be # attached, but they have annoying arg ordering constrains so we use # groups instead: https://github.com/letsencrypt/letsencrypt/issues/820 - for v in VERBS: - add_subparser(v) + for v, func in VERBS.items(): + add_subparser(v, func) helpful.add_group("auth", description="Options for modifying how a cert is obtained") helpful.add_group("install", description="Options for modifying how a cert is deployed") diff --git a/letsencrypt/tests/cli_test.py b/letsencrypt/tests/cli_test.py index 9a99a74cc..f5613ee58 100644 --- a/letsencrypt/tests/cli_test.py +++ b/letsencrypt/tests/cli_test.py @@ -44,6 +44,8 @@ class CLITest(unittest.TestCase): def test_no_flags(self): with mock.patch('letsencrypt.cli.run') as mock_run: + from letsencrypt import cli + cli.VERBS["run"] = mock_run self._call([]) self.assertEqual(1, mock_run.call_count) From bb167743f32f6b1d84a25295505d255aea331d5c Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Wed, 30 Sep 2015 13:00:10 -0700 Subject: [PATCH 49/78] Don't call_registered() on SystemExit --- letsencrypt/error_handler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt/error_handler.py b/letsencrypt/error_handler.py index 1f979a6de..1292f2bc5 100644 --- a/letsencrypt/error_handler.py +++ b/letsencrypt/error_handler.py @@ -50,7 +50,7 @@ class ErrorHandler(object): self.set_signal_handlers() def __exit__(self, exec_type, exec_value, trace): - if exec_value is not None: + if exec_type not in (None, SystemExit): logger.debug("Encountered exception:\n%s", "".join( traceback.format_exception(exec_type, exec_value, trace))) self.call_registered() From d85f42d71f5aba91cb96af6f9959c778fb047b9f Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 30 Sep 2015 15:29:29 -0700 Subject: [PATCH 50/78] Plugins don't need to be in HELP_TOPICS They're already added as topics automatically, though they do need to be in the hand-written top level help. --- letsencrypt/cli.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 66f991063..1ad57b738 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -80,8 +80,8 @@ More detailed help: -h, --help [topic] print this message, or detailed help on a topic; the available topics are: - all, apache, automation, nginx, paths, security, testing, or any of the - subcommands + all, apache, automation, manual, nginx, paths, security, testing, or any of + the subcommands """ @@ -740,8 +740,7 @@ VERBS = { "config_changes" : config_changes, "plugins" : plugins_cmd } -HELP_TOPICS = (["all", "security", "paths", "automation", "testing", "apache", "nginx"] - + VERBS.keys()) +HELP_TOPICS = ["all", "security", "paths", "automation", "testing"] + VERBS.keys() def _create_subparsers(helpful): subparsers = helpful.parser.add_subparsers(metavar="SUBCOMMAND") From 5ca1a27200fb17ac04104ba65c05d810bb20b906 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 30 Sep 2015 15:31:32 -0700 Subject: [PATCH 51/78] Keep the acme/ subtree compatible with strict pylinting --- acme/acme/challenges.py | 4 ++-- acme/acme/challenges_test.py | 2 +- acme/acme/jose/interfaces_test.py | 4 ++-- acme/acme/jose/json_util_test.py | 6 +++--- acme/acme/jose/jwa_test.py | 2 +- acme/acme/jose/jwk.py | 2 +- acme/acme/jose/util.py | 4 ++-- acme/acme/jose/util_test.py | 4 ++-- acme/acme/messages_test.py | 2 +- 9 files changed, 15 insertions(+), 15 deletions(-) diff --git a/acme/acme/challenges.py b/acme/acme/challenges.py index f5763adc4..d81e77f83 100644 --- a/acme/acme/challenges.py +++ b/acme/acme/challenges.py @@ -315,7 +315,7 @@ class DVSNIResponse(ChallengeResponse): validation = jose.Field("validation", decoder=jose.JWS.from_json) @property - def z(self): + def z(self): # pylint: disable=invalid-name """The ``z`` parameter. :rtype: bytes @@ -333,7 +333,7 @@ class DVSNIResponse(ChallengeResponse): :rtype: bytes """ - z = self.z + z = self.z # pylint: disable=invalid-name return z[:32] + b'.' + z[32:] + self.DOMAIN_SUFFIX @property diff --git a/acme/acme/challenges_test.py b/acme/acme/challenges_test.py index b3f48cdf2..ed44d4c45 100644 --- a/acme/acme/challenges_test.py +++ b/acme/acme/challenges_test.py @@ -269,7 +269,7 @@ class DVSNIResponseTest(unittest.TestCase): 'validation': self.validation.to_json(), } - + # pylint: disable=invalid-name label1 = b'e2df3498860637c667fedadc5a8494ec' label2 = b'09dcc75553c9b3bd73662b50e71b1e42' self.z = label1 + label2 diff --git a/acme/acme/jose/interfaces_test.py b/acme/acme/jose/interfaces_test.py index a3ee124ff..380c3a2a5 100644 --- a/acme/acme/jose/interfaces_test.py +++ b/acme/acme/jose/interfaces_test.py @@ -8,7 +8,7 @@ class JSONDeSerializableTest(unittest.TestCase): def setUp(self): from acme.jose.interfaces import JSONDeSerializable - # pylint: disable=missing-docstring + # pylint: disable=missing-docstring,invalid-name class Basic(JSONDeSerializable): def __init__(self, v): @@ -53,7 +53,7 @@ class JSONDeSerializableTest(unittest.TestCase): self.nested = Basic([[self.basic1]]) self.tuple = Basic(('foo',)) - + # pylint: disable=invalid-name self.Basic = Basic self.Sequence = Sequence self.Mapping = Mapping diff --git a/acme/acme/jose/json_util_test.py b/acme/acme/jose/json_util_test.py index f751382e0..a055f3bf7 100644 --- a/acme/acme/jose/json_util_test.py +++ b/acme/acme/jose/json_util_test.py @@ -92,7 +92,7 @@ class JSONObjectWithFieldsMetaTest(unittest.TestCase): from acme.jose.json_util import JSONObjectWithFieldsMeta self.field = Field('Baz') self.field2 = Field('Baz2') - # pylint: disable=missing-docstring,too-few-public-methods + # pylint: disable=invalid-name,missing-docstring,too-few-public-methods # pylint: disable=blacklisted-name @six.add_metaclass(JSONObjectWithFieldsMeta) @@ -138,7 +138,7 @@ class JSONObjectWithFieldsTest(unittest.TestCase): from acme.jose.json_util import Field class MockJSONObjectWithFields(JSONObjectWithFields): - # pylint: disable=missing-docstring,no-self-argument + # pylint: disable=invalid-name,missing-docstring,no-self-argument # pylint: disable=too-few-public-methods x = Field('x', omitempty=True, encoder=(lambda x: x * 2), @@ -158,7 +158,7 @@ class JSONObjectWithFieldsTest(unittest.TestCase): raise errors.DeserializationError() return value - + # pylint: disable=invalid-name self.MockJSONObjectWithFields = MockJSONObjectWithFields self.mock = MockJSONObjectWithFields(x=None, y=2, z=3) diff --git a/acme/acme/jose/jwa_test.py b/acme/acme/jose/jwa_test.py index 8ca512043..3328d083a 100644 --- a/acme/acme/jose/jwa_test.py +++ b/acme/acme/jose/jwa_test.py @@ -26,7 +26,7 @@ class JWASignatureTest(unittest.TestCase): def verify(self, key, msg, sig): raise NotImplementedError() # pragma: no cover - + # pylint: disable=invalid-name self.Sig1 = MockSig('Sig1') self.Sig2 = MockSig('Sig2') diff --git a/acme/acme/jose/jwk.py b/acme/acme/jose/jwk.py index 67f243347..7a976f189 100644 --- a/acme/acme/jose/jwk.py +++ b/acme/acme/jose/jwk.py @@ -186,7 +186,7 @@ class JWKRSA(JWK): @classmethod def fields_from_json(cls, jobj): - + # pylint: disable=invalid-name n, e = (cls._decode_param(jobj[x]) for x in ('n', 'e')) public_numbers = rsa.RSAPublicNumbers(e=e, n=n) if 'd' not in jobj: # public key diff --git a/acme/acme/jose/util.py b/acme/acme/jose/util.py index 46c43bf35..ab3606efc 100644 --- a/acme/acme/jose/util.py +++ b/acme/acme/jose/util.py @@ -7,7 +7,7 @@ import six class abstractclassmethod(classmethod): - # pylint: disable=too-few-public-methods + # pylint: disable=invalid-name,too-few-public-methods """Descriptor for an abstract classmethod. It augments the :mod:`abc` framework with an abstract @@ -172,7 +172,7 @@ class ImmutableMap(collections.Mapping, collections.Hashable): class frozendict(collections.Mapping, collections.Hashable): - # pylint: disable=too-few-public-methods + # pylint: disable=invalid-name,too-few-public-methods """Frozen dictionary.""" __slots__ = ('_items', '_keys') diff --git a/acme/acme/jose/util_test.py b/acme/acme/jose/util_test.py index 295c70fee..4cdd9127f 100644 --- a/acme/acme/jose/util_test.py +++ b/acme/acme/jose/util_test.py @@ -92,7 +92,7 @@ class ImmutableMapTest(unittest.TestCase): """Tests for acme.jose.util.ImmutableMap.""" def setUp(self): - # pylint: disable=too-few-public-methods + # pylint: disable=invalid-name,too-few-public-methods # pylint: disable=missing-docstring from acme.jose.util import ImmutableMap @@ -156,7 +156,7 @@ class ImmutableMapTest(unittest.TestCase): self.assertEqual("B(x='foo', y='bar')", repr(self.B(x='foo', y='bar'))) -class frozendictTest(unittest.TestCase): +class frozendictTest(unittest.TestCase): # pylint: disable=invalid-name """Tests for acme.jose.util.frozendict.""" def setUp(self): diff --git a/acme/acme/messages_test.py b/acme/acme/messages_test.py index 718a936dd..d2d859bc5 100644 --- a/acme/acme/messages_test.py +++ b/acme/acme/messages_test.py @@ -64,7 +64,7 @@ class ConstantTest(unittest.TestCase): class MockConstant(_Constant): # pylint: disable=missing-docstring POSSIBLE_NAMES = {} - self.MockConstant = MockConstant + self.MockConstant = MockConstant # pylint: disable=invalid-name self.const_a = MockConstant('a') self.const_b = MockConstant('b') From 2406fc0486d8f74ad9979b1973e9e24d9d453df7 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 30 Sep 2015 15:56:58 -0700 Subject: [PATCH 52/78] Go back to VERBS as a list The dictionary was destroying the ordering, which was important. --- letsencrypt/cli.py | 28 ++++++++++++---------------- letsencrypt/tests/cli_test.py | 2 -- 2 files changed, 12 insertions(+), 18 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 1ad57b738..73dd24bdb 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -730,24 +730,20 @@ def create_parser(plugins, args): return helpful.parser, helpful.args # For now unfortunately this constant just needs to match the code below; -# there isn't an elegant way to autogenerate it in time. pylint: disable=bad-whitespace -VERBS = { - "run" : run, - "auth" : auth, - "install" : install, - "revoke" : revoke, - "rollback" : rollback, - "config_changes" : config_changes, - "plugins" : plugins_cmd -} -HELP_TOPICS = ["all", "security", "paths", "automation", "testing"] + VERBS.keys() +# there isn't an elegant way to autogenerate it in time. +VERBS = ["run", "auth", "install", "revoke", "rollback", "config_changes", "plugins"] +HELP_TOPICS = ["all", "security", "paths", "automation", "testing"] + VERBS def _create_subparsers(helpful): subparsers = helpful.parser.add_subparsers(metavar="SUBCOMMAND") - def add_subparser(name, func): # pylint: disable=missing-docstring - subparser = subparsers.add_parser( - name, help=func.__doc__.splitlines()[0], description=func.__doc__) + def add_subparser(name): # pylint: disable=missing-docstring + if name == "plugins": + func = plugins_cmd + else: + func = eval(name) # pylint: disable=eval-used + h = func.__doc__.splitlines()[0] + subparser = subparsers.add_parser(name, help=h, description=func.__doc__) subparser.set_defaults(func=func) return subparser @@ -756,8 +752,8 @@ def _create_subparsers(helpful): # these add_subparser objects return objects to which arguments could be # attached, but they have annoying arg ordering constrains so we use # groups instead: https://github.com/letsencrypt/letsencrypt/issues/820 - for v, func in VERBS.items(): - add_subparser(v, func) + for v in VERBS: + add_subparser(v) helpful.add_group("auth", description="Options for modifying how a cert is obtained") helpful.add_group("install", description="Options for modifying how a cert is deployed") diff --git a/letsencrypt/tests/cli_test.py b/letsencrypt/tests/cli_test.py index f5613ee58..9a99a74cc 100644 --- a/letsencrypt/tests/cli_test.py +++ b/letsencrypt/tests/cli_test.py @@ -44,8 +44,6 @@ class CLITest(unittest.TestCase): def test_no_flags(self): with mock.patch('letsencrypt.cli.run') as mock_run: - from letsencrypt import cli - cli.VERBS["run"] = mock_run self._call([]) self.assertEqual(1, mock_run.call_count) From 95c4b55da09aee285bd823bf993d43089907456a Mon Sep 17 00:00:00 2001 From: Jacob Hoffman-Andrews Date: Wed, 30 Sep 2015 16:49:03 -0700 Subject: [PATCH 53/78] Mark Nginx as non-working. --- letsencrypt-nginx/letsencrypt_nginx/configurator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt-nginx/letsencrypt_nginx/configurator.py b/letsencrypt-nginx/letsencrypt_nginx/configurator.py index 3f6d6f327..a88607e58 100644 --- a/letsencrypt-nginx/letsencrypt_nginx/configurator.py +++ b/letsencrypt-nginx/letsencrypt_nginx/configurator.py @@ -56,7 +56,7 @@ class NginxConfigurator(common.Plugin): zope.interface.implements(interfaces.IAuthenticator, interfaces.IInstaller) zope.interface.classProvides(interfaces.IPluginFactory) - description = "Nginx Web Server - Alpha" + description = "Nginx Web Server - currently doesn't work" @classmethod def add_parser_arguments(cls, add): From 11ca1108c2536adb0d735e76829f178f06a08715 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 30 Sep 2015 16:53:08 -0700 Subject: [PATCH 54/78] Test cases for command line help --- letsencrypt/tests/cli_test.py | 37 ++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/letsencrypt/tests/cli_test.py b/letsencrypt/tests/cli_test.py index 9a99a74cc..75eec1978 100644 --- a/letsencrypt/tests/cli_test.py +++ b/letsencrypt/tests/cli_test.py @@ -2,6 +2,7 @@ import itertools import os import shutil +import StringIO import traceback import tempfile import unittest @@ -42,6 +43,21 @@ class CLITest(unittest.TestCase): ret = cli.main(args) return ret, stdout, stderr, client + def _call_stdout(self, args): + """ + Variant of _call that preserves stdout so that it can be mocked by the + caller. + """ + from letsencrypt import cli + args = ['--text', '--config-dir', self.config_dir, + '--work-dir', self.work_dir, '--logs-dir', self.logs_dir, + '--agree-eula'] + args + with mock.patch('letsencrypt.cli.sys.stderr') as stderr: + with mock.patch('letsencrypt.cli.client') as client: + ret = cli.main(args) + return ret, None, stderr, client + + def test_no_flags(self): with mock.patch('letsencrypt.cli.run') as mock_run: self._call([]) @@ -49,7 +65,26 @@ class CLITest(unittest.TestCase): def test_help(self): self.assertRaises(SystemExit, self._call, ['--help']) - self.assertRaises(SystemExit, self._call, ['--help all']) + self.assertRaises(SystemExit, self._call, ['--help', 'all']) + output = StringIO.StringIO() + with mock.patch('letsencrypt.cli.sys.stdout', new=output): + self.assertRaises(SystemExit, self._call_stdout, ['--help', 'all']) + out = output.getvalue() + self.assertTrue("--configurator" in out) + self.assertTrue("how a cert is deployed" in out) + self.assertTrue("--manual-test-mode" in out) + output.truncate(0) + self.assertRaises(SystemExit, self._call_stdout, ['-h', 'nginx']) + out = output.getvalue() + self.assertTrue("--nginx-ctl" in out) + self.assertTrue("--manual-test-mode" not in out) + self.assertTrue("--checkpoints" not in out) + output.truncate(0) + self.assertRaises(SystemExit, self._call_stdout, ['--help', 'plugins']) + out = output.getvalue() + self.assertTrue("--manual-test-mode" not in out) + self.assertTrue("--prepare" in out) + self.assertTrue("Plugin options" in out) def test_rollback(self): _, _, _, client = self._call(['rollback']) From 43cb36807a001562726bceaaaae00d708fcc5ed2 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 30 Sep 2015 17:00:09 -0700 Subject: [PATCH 55/78] Also test top level help --- letsencrypt/tests/cli_test.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/letsencrypt/tests/cli_test.py b/letsencrypt/tests/cli_test.py index 75eec1978..0a92aba62 100644 --- a/letsencrypt/tests/cli_test.py +++ b/letsencrypt/tests/cli_test.py @@ -85,6 +85,12 @@ class CLITest(unittest.TestCase): self.assertTrue("--manual-test-mode" not in out) self.assertTrue("--prepare" in out) self.assertTrue("Plugin options" in out) + output.truncate(0) + self.assertRaises(SystemExit, self._call_stdout, ['-h']) + out = output.getvalue() + from letsencrypt import cli + self.assertTrue(cli.USAGE in out) + def test_rollback(self): _, _, _, client = self._call(['rollback']) From 9cf2ea8a5742d8868f1f1c47377626a741464bc7 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Wed, 30 Sep 2015 17:16:27 -0700 Subject: [PATCH 56/78] Report Apache correctly when uninstalled --- .../letsencrypt_apache/configurator.py | 6 +++ .../tests/configurator_test.py | 10 ++++- .../letsencrypt_apache/tests/util.py | 45 ++++++++++--------- 3 files changed, 39 insertions(+), 22 deletions(-) diff --git a/letsencrypt-apache/letsencrypt_apache/configurator.py b/letsencrypt-apache/letsencrypt_apache/configurator.py index ad3c62d2c..f3d2b5f9a 100644 --- a/letsencrypt-apache/letsencrypt_apache/configurator.py +++ b/letsencrypt-apache/letsencrypt_apache/configurator.py @@ -137,6 +137,12 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): :raises .errors.PluginError: If there is any other error """ + # Verify Apache is installed + for exe in (self.conf("ctl"), self.conf("enmod"), + self.conf("dismod"), self.conf("init-script")): + if not le_util.exe_exists(exe): + raise errors.NoInstallationError + # Make sure configuration is valid self.config_test() diff --git a/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py b/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py index 026594a8f..7c2137c45 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py +++ b/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py @@ -37,8 +37,16 @@ class TwoVhost80Test(util.ApacheTest): shutil.rmtree(self.config_dir) shutil.rmtree(self.work_dir) + @mock.patch("letsencrypt_apache.configurator.le_util.exe_exists") + def test_prepare_no_install(self, mock_exe_exists): + mock_exe_exists.return_value = False + self.assertRaises( + errors.NoInstallationError, self.config.prepare) + @mock.patch("letsencrypt_apache.parser.ApacheParser") - def test_prepare_version(self, _): + @mock.patch("letsencrypt_apache.configurator.le_util.exe_exists") + def test_prepare_version(self, mock_exe_exists, _): + mock_exe_exists.return_value = True self.config.version = None self.config.config_test = mock.Mock() self.config.get_version = mock.Mock(return_value=(1, 1)) diff --git a/letsencrypt-apache/letsencrypt_apache/tests/util.py b/letsencrypt-apache/letsencrypt_apache/tests/util.py index b544e06ee..2594ba773 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/util.py +++ b/letsencrypt-apache/letsencrypt_apache/tests/util.py @@ -66,31 +66,34 @@ def get_apache_configurator( """ backups = os.path.join(work_dir, "backups") + mock_le_config = mock.MagicMock( + apache_server_root=config_path, + apache_le_vhost_ext=constants.CLI_DEFAULTS["le_vhost_ext"], + backup_dir=backups, + config_dir=config_dir, + temp_checkpoint_dir=os.path.join(work_dir, "temp_checkpoints"), + in_progress_dir=os.path.join(backups, "IN_PROGRESS"), + work_dir=work_dir) with mock.patch("letsencrypt_apache.configurator." "subprocess.Popen") as mock_popen: - with mock.patch("letsencrypt_apache.parser.ApacheParser." - "update_runtime_variables"): - # This indicates config_test passes - mock_popen().communicate.return_value = ("Fine output", "No problems") - mock_popen().returncode = 0 + # This indicates config_test passes + mock_popen().communicate.return_value = ("Fine output", "No problems") + mock_popen().returncode = 0 + with mock.patch("letsencrypt_apache.configurator.le_util." + "exe_exists") as mock_exe_exists: + mock_exe_exists.return_value = True + with mock.patch("letsencrypt_apache.parser.ApacheParser." + "update_runtime_variables"): + config = configurator.ApacheConfigurator( + config=mock_le_config, + name="apache", + version=version) + # This allows testing scripts to set it a bit more quickly + if conf is not None: + config.conf = conf # pragma: no cover - config = configurator.ApacheConfigurator( - config=mock.MagicMock( - apache_server_root=config_path, - apache_le_vhost_ext=constants.CLI_DEFAULTS["le_vhost_ext"], - backup_dir=backups, - config_dir=config_dir, - temp_checkpoint_dir=os.path.join(work_dir, "temp_checkpoints"), - in_progress_dir=os.path.join(backups, "IN_PROGRESS"), - work_dir=work_dir), - name="apache", - version=version) - # This allows testing scripts to set it a bit more quickly - if conf is not None: - config.conf = conf # pragma: no cover - - config.prepare() + config.prepare() return config From 8041b35f9988d1193528df0d36b14eca35babc3a Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 30 Sep 2015 19:06:16 -0700 Subject: [PATCH 57/78] Make sure the LICENSE file is accurate for first pre-relase - In general copyrights remain with their respective authors or authors' organizations, but with license granted by clause 5 of the Apache License. - Presently the plurality of the copyright in the client is held by EFF as a result of work-for-hire by jdkasten, bmw, schoen, pde, rolandshoemaker and jsha; or by Jakub Warmuz or his employer, Google. --- LICENSE.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/LICENSE.txt b/LICENSE.txt index 5a9f6fa55..2ed752521 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,5 +1,5 @@ -Let's Encrypt: -Copyright (c) Internet Security Research Group +Let's Encrypt Python Client +Copyright (c) Electronic Frontier Foundation and others Licensed Apache Version 2.0 Incorporating code from nginxparser From 268368b3e928e669420beeefd5d82a8af4de4a1f Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Thu, 1 Oct 2015 10:12:38 -0700 Subject: [PATCH 58/78] Updated README to reflect state of Nginx plugin --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 23e4dad29..43ecd413c 100644 --- a/README.rst +++ b/README.rst @@ -79,7 +79,7 @@ Current Features * web servers supported: - apache/2.x (tested and working on Ubuntu Linux) - - nginx/0.8.48+ (tested and mostly working on Ubuntu Linux) + - nginx/0.8.48+ (under development) - standalone (runs its own webserver to prove you control the domain) * the private key is generated locally on your system From 6bde83c9835b1fba9a935f341e62a48b8393d189 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Thu, 1 Oct 2015 11:53:11 -0700 Subject: [PATCH 59/78] Fixed indentation in storage.py --- letsencrypt/storage.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt/storage.py b/letsencrypt/storage.py index 08dff25a1..be270a762 100644 --- a/letsencrypt/storage.py +++ b/letsencrypt/storage.py @@ -520,7 +520,7 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes remaining = expiry - now if remaining < autorenew_interval: return True - return False + return False @classmethod def new_lineage(cls, lineagename, cert, privkey, chain, From d7a16ecfcb76d50702375b3dbb66669e59818ddc Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Thu, 1 Oct 2015 15:39:55 -0700 Subject: [PATCH 60/78] Added tests and documentation --- letsencrypt/error_handler.py | 5 +++-- letsencrypt/tests/error_handler_test.py | 9 +++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/letsencrypt/error_handler.py b/letsencrypt/error_handler.py index 1292f2bc5..8b0eb7c8b 100644 --- a/letsencrypt/error_handler.py +++ b/letsencrypt/error_handler.py @@ -22,8 +22,8 @@ class ErrorHandler(object): """Registers functions to be called if an exception or signal occurs. This class allows you to register functions that will be called when - an exception or signal is encountered. The class works best as a - context manager. For example: + an exception (excluding SystemExit) or signal is encountered. The + class works best as a context manager. For example: with ErrorHandler(cleanup_func): do_something() @@ -50,6 +50,7 @@ class ErrorHandler(object): self.set_signal_handlers() def __exit__(self, exec_type, exec_value, trace): + # SystemExit is ignored to properly handle forks that don't exec if exec_type not in (None, SystemExit): logger.debug("Encountered exception:\n%s", "".join( traceback.format_exception(exec_type, exec_value, trace))) diff --git a/letsencrypt/tests/error_handler_test.py b/letsencrypt/tests/error_handler_test.py index 66acac930..c92f12435 100644 --- a/letsencrypt/tests/error_handler_test.py +++ b/letsencrypt/tests/error_handler_test.py @@ -1,5 +1,6 @@ """Tests for letsencrypt.error_handler.""" import signal +import sys import unittest import mock @@ -50,6 +51,14 @@ class ErrorHandlerTest(unittest.TestCase): self.init_func.assert_called_once_with() bad_func.assert_called_once_with() + def test_sysexit_ignored(self): + try: + with self.handler: + sys.exit(0) + except SystemExit: + pass + self.assertFalse(self.init_func.called) + if __name__ == "__main__": unittest.main() # pragma: no cover From 895faa7dc94b554881b4330f20354670c276df19 Mon Sep 17 00:00:00 2001 From: Brandon Kreisel Date: Fri, 2 Oct 2015 22:36:56 -0400 Subject: [PATCH 61/78] Add OS X bootstrap for integration enviornment Installs requirements and sets up environment to run boulder and integration tests --- tests/mac-bootstrap.sh | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100755 tests/mac-bootstrap.sh diff --git a/tests/mac-bootstrap.sh b/tests/mac-bootstrap.sh new file mode 100755 index 000000000..38db6a969 --- /dev/null +++ b/tests/mac-bootstrap.sh @@ -0,0 +1,26 @@ +#!/bin/sh + +#Check Homebrew +if ! hash brew 2>/dev/null; then + echo "Homebrew Not Installed\nDownloading..." + ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" +fi + +brew install libtool mariadb rabbitmq coreutils go + +mysql.server start + +rabbit_pid=`ps | grep rabbitmq | grep -v grep | awk '{ print $1}'` +if [ -n rabbit_pid ]; then + echo "RabbitMQ already running" +else + rabbitmq-server & +fi + +hosts_entry=`cat /etc/hosts | grep "127.0.0.1 le.wtf"` +if [ -z hosts_entry ]; then + echo "Adding hosts entry for le.wtf..." + sudo sh -c "echo 127.0.0.1 le.wtf >> /etc/hosts" +fi + +./tests/boulder-start.sh From 0868a5962f4ae812f9bcb1b39e4c3bace207074b Mon Sep 17 00:00:00 2001 From: Brandon Kreisel Date: Fri, 2 Oct 2015 22:37:22 -0400 Subject: [PATCH 62/78] Add documentation for OS X bootstrap script --- docs/contributing.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/contributing.rst b/docs/contributing.rst index c6443e3b2..7b0768efb 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -67,6 +67,8 @@ The following tools are there to help you: Integration ~~~~~~~~~~~ +Mac OS X users: Run `./tests/mac-integration.sh` to configure the +integration tests environment and start boulder First, install `Go`_ 1.5, libtool-ltdl, mariadb-server and rabbitmq-server and then start Boulder_, an ACME CA server:: From 8409c9c658b247421caef540d57410dc8a00ef41 Mon Sep 17 00:00:00 2001 From: Brandon Kreisel Date: Sat, 3 Oct 2015 11:27:39 -0400 Subject: [PATCH 63/78] Meddle with more documentation and learn how to bash flag --- docs/contributing.rst | 6 +++--- tests/mac-bootstrap.sh | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/contributing.rst b/docs/contributing.rst index 7b0768efb..c746c6ae7 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -67,10 +67,10 @@ The following tools are there to help you: Integration ~~~~~~~~~~~ -Mac OS X users: Run `./tests/mac-integration.sh` to configure the -integration tests environment and start boulder +Mac OS X users: Run `./tests/mac-bootstrap.sh` instead of `boulder-start.sh` to +install dependencies, configure the environment, and start boulder. -First, install `Go`_ 1.5, libtool-ltdl, mariadb-server and +Otherwise, install `Go`_ 1.5, libtool-ltdl, mariadb-server and rabbitmq-server and then start Boulder_, an ACME CA server:: ./tests/boulder-start.sh diff --git a/tests/mac-bootstrap.sh b/tests/mac-bootstrap.sh index 38db6a969..66036ce56 100755 --- a/tests/mac-bootstrap.sh +++ b/tests/mac-bootstrap.sh @@ -11,14 +11,14 @@ brew install libtool mariadb rabbitmq coreutils go mysql.server start rabbit_pid=`ps | grep rabbitmq | grep -v grep | awk '{ print $1}'` -if [ -n rabbit_pid ]; then +if [ -n "$rabbit_pid" ]; then echo "RabbitMQ already running" else rabbitmq-server & fi hosts_entry=`cat /etc/hosts | grep "127.0.0.1 le.wtf"` -if [ -z hosts_entry ]; then +if [ -z "$hosts_entry" ]; then echo "Adding hosts entry for le.wtf..." sudo sh -c "echo 127.0.0.1 le.wtf >> /etc/hosts" fi From b89bd4b5def4c2888e522caef6f0f039c6e8c601 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sun, 4 Oct 2015 06:24:24 +0000 Subject: [PATCH 64/78] Add API docs for letshelp_letsencrypt --- docs/pkgs/letshelp_letsencrypt.rst | 11 +++++++++++ readthedocs.org.requirements.txt | 1 + 2 files changed, 12 insertions(+) create mode 100644 docs/pkgs/letshelp_letsencrypt.rst diff --git a/docs/pkgs/letshelp_letsencrypt.rst b/docs/pkgs/letshelp_letsencrypt.rst new file mode 100644 index 000000000..8f6872eac --- /dev/null +++ b/docs/pkgs/letshelp_letsencrypt.rst @@ -0,0 +1,11 @@ +:mod:`letshelp_letsencrypt` +--------------------------- + +.. automodule:: letshelp_letsencrypt + :members: + +:mod:`letshelp_letsencrypt.apache` +================================== + +.. automodule:: letshelp_letsencrypt.apache + :members: diff --git a/readthedocs.org.requirements.txt b/readthedocs.org.requirements.txt index f686b00bf..d895f80a5 100644 --- a/readthedocs.org.requirements.txt +++ b/readthedocs.org.requirements.txt @@ -11,3 +11,4 @@ -e .[docs] -e letsencrypt-apache -e letsencrypt-nginx +-e letshelp-letsencrypt From 08afe48019639775bee4b1a30bfccabb18309bd6 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sun, 4 Oct 2015 06:37:35 +0000 Subject: [PATCH 65/78] Add API docs for letsencrypt_compatibility_test --- docs/pkgs/letsencrypt_compatibility_test.rst | 53 ++++++++++++++++++++ readthedocs.org.requirements.txt | 1 + 2 files changed, 54 insertions(+) create mode 100644 docs/pkgs/letsencrypt_compatibility_test.rst diff --git a/docs/pkgs/letsencrypt_compatibility_test.rst b/docs/pkgs/letsencrypt_compatibility_test.rst new file mode 100644 index 000000000..f792a2cc3 --- /dev/null +++ b/docs/pkgs/letsencrypt_compatibility_test.rst @@ -0,0 +1,53 @@ +:mod:`letsencrypt_compatibility_test` +------------------------------------- + +.. automodule:: letsencrypt_compatibility_test + :members: + +:mod:`letsencrypt_compatibility_test.errors` +============================================ + +.. automodule:: letsencrypt_compatibility_test.errors + :members: + +:mod:`letsencrypt_compatibility_test.interfaces` +================================================ + +.. automodule:: letsencrypt_compatibility_test.interfaces + :members: + +:mod:`letsencrypt_compatibility_test.test_driver` +================================================= + +.. automodule:: letsencrypt_compatibility_test.test_driver + :members: + +:mod:`letsencrypt_compatibility_test.util` +========================================== + +.. automodule:: letsencrypt_compatibility_test.util + :members: + +:mod:`letsencrypt_compatibility_test.configurators` +=================================================== + +.. automodule:: letsencrypt_compatibility_test.configurators + :members: + +:mod:`letsencrypt_compatibility_test.configurators.apache` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: letsencrypt_compatibility_test.configurators.apache + :members: + +:mod:`letsencrypt_compatibility_test.configurators.apache.apache24` +------------------------------------------------------------------- + +.. automodule:: letsencrypt_compatibility_test.configurators.apache.apache24 + :members: + +:mod:`letsencrypt_compatibility_test.configurators.apache.common` +------------------------------------------------------------------- + +.. automodule:: letsencrypt_compatibility_test.configurators.apache.common + :members: diff --git a/readthedocs.org.requirements.txt b/readthedocs.org.requirements.txt index d895f80a5..3c3a3c576 100644 --- a/readthedocs.org.requirements.txt +++ b/readthedocs.org.requirements.txt @@ -11,4 +11,5 @@ -e .[docs] -e letsencrypt-apache -e letsencrypt-nginx +-e letsencrypt-compatibility-test -e letshelp-letsencrypt From 7644613171e468e66eee6797dc2de3ea8ab9d8ac Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sun, 4 Oct 2015 10:10:41 +0000 Subject: [PATCH 66/78] Update Copyright notice in subpackages LICENSE. This corresponds to changes in #871. --- acme/LICENSE.txt | 2 +- letsencrypt-apache/LICENSE.txt | 2 +- letsencrypt-compatibility-test/LICENSE.txt | 2 +- letsencrypt-nginx/LICENSE.txt | 2 +- letshelp-letsencrypt/LICENSE.txt | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/acme/LICENSE.txt b/acme/LICENSE.txt index 7c13afb9d..981c46c9f 100644 --- a/acme/LICENSE.txt +++ b/acme/LICENSE.txt @@ -1,4 +1,4 @@ - Copyright 2015 Internet Security Research Group + Copyright 2015 Electronic Frontier Foundation and others Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/letsencrypt-apache/LICENSE.txt b/letsencrypt-apache/LICENSE.txt index 7c13afb9d..981c46c9f 100644 --- a/letsencrypt-apache/LICENSE.txt +++ b/letsencrypt-apache/LICENSE.txt @@ -1,4 +1,4 @@ - Copyright 2015 Internet Security Research Group + Copyright 2015 Electronic Frontier Foundation and others Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/letsencrypt-compatibility-test/LICENSE.txt b/letsencrypt-compatibility-test/LICENSE.txt index 7c13afb9d..981c46c9f 100644 --- a/letsencrypt-compatibility-test/LICENSE.txt +++ b/letsencrypt-compatibility-test/LICENSE.txt @@ -1,4 +1,4 @@ - Copyright 2015 Internet Security Research Group + Copyright 2015 Electronic Frontier Foundation and others Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/letsencrypt-nginx/LICENSE.txt b/letsencrypt-nginx/LICENSE.txt index 7c13afb9d..981c46c9f 100644 --- a/letsencrypt-nginx/LICENSE.txt +++ b/letsencrypt-nginx/LICENSE.txt @@ -1,4 +1,4 @@ - Copyright 2015 Internet Security Research Group + Copyright 2015 Electronic Frontier Foundation and others Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/letshelp-letsencrypt/LICENSE.txt b/letshelp-letsencrypt/LICENSE.txt index 7c13afb9d..981c46c9f 100644 --- a/letshelp-letsencrypt/LICENSE.txt +++ b/letshelp-letsencrypt/LICENSE.txt @@ -1,4 +1,4 @@ - Copyright 2015 Internet Security Research Group + Copyright 2015 Electronic Frontier Foundation and others Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. From 32da607ae5580a164933b17e32599faefbe9b396 Mon Sep 17 00:00:00 2001 From: Liam Marshall Date: Sun, 4 Oct 2015 10:39:38 -0500 Subject: [PATCH 67/78] crypto_util: Remove asn1_generalizedtime_to_dt(...) Not used by any other code AFAIK (ack'd entire codebase). --- letsencrypt/crypto_util.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/letsencrypt/crypto_util.py b/letsencrypt/crypto_util.py index 79cd24ed6..73e4f9bdd 100644 --- a/letsencrypt/crypto_util.py +++ b/letsencrypt/crypto_util.py @@ -274,11 +274,6 @@ def asn1_generalizedtime_to_dt(timestamp): return datetime.datetime.strptime(timestamp, '%Y%m%d%H%M%SZ') -def pyopenssl_x509_name_as_text(x509name): - """Convert `OpenSSL.crypto.X509Name` to text.""" - return "/".join("{0}={1}" for key, value in x509name.get_components()) - - def dump_pyopenssl_chain(chain, filetype=OpenSSL.crypto.FILETYPE_PEM): """Dump certificate chain into a bundle. From 6994dad59b37d26007f14030558d560144bce81e Mon Sep 17 00:00:00 2001 From: Liam Marshall Date: Sun, 4 Oct 2015 11:17:07 -0500 Subject: [PATCH 68/78] crypto_util: Remove `asn1_generalizedtime_to_dt(...)` --- letsencrypt/crypto_util.py | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/letsencrypt/crypto_util.py b/letsencrypt/crypto_util.py index 73e4f9bdd..1007f18c7 100644 --- a/letsencrypt/crypto_util.py +++ b/letsencrypt/crypto_util.py @@ -261,19 +261,6 @@ def get_sans_from_csr(csr, typ=OpenSSL.crypto.FILETYPE_PEM): csr, OpenSSL.crypto.load_certificate_request, typ) -def asn1_generalizedtime_to_dt(timestamp): - """Convert ASN.1 GENERALIZEDTIME to datetime. - - Useful for deserialization of `OpenSSL.crypto.X509.get_notAfter` and - `OpenSSL.crypto.X509.get_notAfter` outputs. - - .. todo:: This function support only one format: `%Y%m%d%H%M%SZ`. - Implement remaining two. - - """ - return datetime.datetime.strptime(timestamp, '%Y%m%d%H%M%SZ') - - def dump_pyopenssl_chain(chain, filetype=OpenSSL.crypto.FILETYPE_PEM): """Dump certificate chain into a bundle. From 884d8e9905527d6df4f4bf42954763f4469fe788 Mon Sep 17 00:00:00 2001 From: Liam Marshall Date: Sun, 4 Oct 2015 11:28:03 -0500 Subject: [PATCH 69/78] crypto_util: Remove unused import --- letsencrypt/crypto_util.py | 1 - 1 file changed, 1 deletion(-) diff --git a/letsencrypt/crypto_util.py b/letsencrypt/crypto_util.py index 1007f18c7..2f24c4fb6 100644 --- a/letsencrypt/crypto_util.py +++ b/letsencrypt/crypto_util.py @@ -4,7 +4,6 @@ is capable of handling the signatures. """ -import datetime import logging import os From 032f3e8f642a9edde35d0cfa8fdbc30a70ab5c4d Mon Sep 17 00:00:00 2001 From: Liam Marshall Date: Sun, 4 Oct 2015 15:00:57 -0500 Subject: [PATCH 70/78] crypto_util: test _pyopenssl_load --- letsencrypt/tests/crypto_util_test.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/letsencrypt/tests/crypto_util_test.py b/letsencrypt/tests/crypto_util_test.py index b4d2aa394..91c1160a0 100644 --- a/letsencrypt/tests/crypto_util_test.py +++ b/letsencrypt/tests/crypto_util_test.py @@ -212,6 +212,17 @@ class GetSANsFromCSRTest(unittest.TestCase): self.assertEqual( [], self._call(test_util.load_vector('csr-nosans.pem'))) +class PyOpenSslLoaderTest(unittest.TestCase): + def test_pyopenssl_load(self): + from letsencrypt.crypto_util import _pyopenssl_load + + method_mock = mock.MagicMock() + fake_data = "this is test data" + fake_types = ('fake loading type',) + _pyopenssl_load(fake_data, method_mock, fake_types) + method_mock.assert_called_with(fake_types[0], fake_data) + + if __name__ == '__main__': unittest.main() # pragma: no cover From 7b2d40ce552c89a9d2c7bf1a94be66372497a006 Mon Sep 17 00:00:00 2001 From: Liam Marshall Date: Sun, 4 Oct 2015 15:34:57 -0500 Subject: [PATCH 71/78] crypto_util: test pyopenssl_load_certificate(...) --- letsencrypt/tests/crypto_util_test.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/letsencrypt/tests/crypto_util_test.py b/letsencrypt/tests/crypto_util_test.py index 91c1160a0..9ad36c83b 100644 --- a/letsencrypt/tests/crypto_util_test.py +++ b/letsencrypt/tests/crypto_util_test.py @@ -222,7 +222,13 @@ class PyOpenSslLoaderTest(unittest.TestCase): _pyopenssl_load(fake_data, method_mock, fake_types) method_mock.assert_called_with(fake_types[0], fake_data) +class CertLoaderTest(unittest.TestCase): + def test_it(self): + from letsencrypt.crypto_util import pyopenssl_load_certificate + cert, file_type = pyopenssl_load_certificate(CERT) + self.assertEqual(cert.digest('sha1'), + OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, CERT).digest('sha1')) if __name__ == '__main__': unittest.main() # pragma: no cover From aa15fae11d9ec80ecce250c53d5028ed875885d3 Mon Sep 17 00:00:00 2001 From: Liam Marshall Date: Sun, 4 Oct 2015 15:37:10 -0500 Subject: [PATCH 72/78] crypto_util: merge _pyopenssl_load into pyopenssl_load_certificate --- letsencrypt/crypto_util.py | 24 +++++++++++------------- letsencrypt/tests/crypto_util_test.py | 12 +++--------- 2 files changed, 14 insertions(+), 22 deletions(-) diff --git a/letsencrypt/crypto_util.py b/letsencrypt/crypto_util.py index 79cd24ed6..eac0530aa 100644 --- a/letsencrypt/crypto_util.py +++ b/letsencrypt/crypto_util.py @@ -201,25 +201,23 @@ def valid_privkey(privkey): return False -def _pyopenssl_load(data, method, types=( - OpenSSL.crypto.FILETYPE_PEM, OpenSSL.crypto.FILETYPE_ASN1)): - openssl_errors = [] - for filetype in types: - try: - return method(filetype, data), filetype - except OpenSSL.crypto.Error as error: # TODO: anything else? - openssl_errors.append(error) - raise errors.Error("Unable to load: {0}".format(",".join( - str(error) for error in openssl_errors))) - - def pyopenssl_load_certificate(data): """Load PEM/DER certificate. :raises errors.Error: """ - return _pyopenssl_load(data, OpenSSL.crypto.load_certificate) + + openssl_errors = [] + + for file_type in (OpenSSL.crypto.FILETYPE_PEM, OpenSSL.crypto.FILETYPE_ASN1): + try: + return OpenSSL.crypto.load_certificate(file_type, data), file_type + except OpenSSL.crypto.Error as error: # TODO: other errors? + openssl_errors.append(error) + raise errors.Error("Unable to load: {0}".format(",".join( + str(error) for error in openssl_errors))) + def _get_sans_from_cert_or_req( diff --git a/letsencrypt/tests/crypto_util_test.py b/letsencrypt/tests/crypto_util_test.py index 9ad36c83b..8ac8a0adc 100644 --- a/letsencrypt/tests/crypto_util_test.py +++ b/letsencrypt/tests/crypto_util_test.py @@ -212,17 +212,10 @@ class GetSANsFromCSRTest(unittest.TestCase): self.assertEqual( [], self._call(test_util.load_vector('csr-nosans.pem'))) -class PyOpenSslLoaderTest(unittest.TestCase): - def test_pyopenssl_load(self): - from letsencrypt.crypto_util import _pyopenssl_load - - method_mock = mock.MagicMock() - fake_data = "this is test data" - fake_types = ('fake loading type',) - _pyopenssl_load(fake_data, method_mock, fake_types) - method_mock.assert_called_with(fake_types[0], fake_data) class CertLoaderTest(unittest.TestCase): + """Tests for letsencrypt.crypto_util.pyopenssl_load_certificate""" + def test_it(self): from letsencrypt.crypto_util import pyopenssl_load_certificate @@ -230,5 +223,6 @@ class CertLoaderTest(unittest.TestCase): self.assertEqual(cert.digest('sha1'), OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, CERT).digest('sha1')) + if __name__ == '__main__': unittest.main() # pragma: no cover From 7b50f5d9bf02c2cc69298c601098901a4741deb3 Mon Sep 17 00:00:00 2001 From: Liam Marshall Date: Sun, 4 Oct 2015 15:42:05 -0500 Subject: [PATCH 73/78] Make pep8 happy --- letsencrypt/crypto_util.py | 7 +++---- letsencrypt/tests/crypto_util_test.py | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/letsencrypt/crypto_util.py b/letsencrypt/crypto_util.py index eac0530aa..777b4d006 100644 --- a/letsencrypt/crypto_util.py +++ b/letsencrypt/crypto_util.py @@ -213,15 +213,14 @@ def pyopenssl_load_certificate(data): for file_type in (OpenSSL.crypto.FILETYPE_PEM, OpenSSL.crypto.FILETYPE_ASN1): try: return OpenSSL.crypto.load_certificate(file_type, data), file_type - except OpenSSL.crypto.Error as error: # TODO: other errors? + except OpenSSL.crypto.Error as error: # TODO: other errors? openssl_errors.append(error) raise errors.Error("Unable to load: {0}".format(",".join( str(error) for error in openssl_errors))) - -def _get_sans_from_cert_or_req( - cert_or_req_str, load_func, typ=OpenSSL.crypto.FILETYPE_PEM): +def _get_sans_from_cert_or_req(cert_or_req_str, load_func, + typ=OpenSSL.crypto.FILETYPE_PEM): try: cert_or_req = load_func(typ, cert_or_req_str) except OpenSSL.crypto.Error as error: diff --git a/letsencrypt/tests/crypto_util_test.py b/letsencrypt/tests/crypto_util_test.py index 8ac8a0adc..b0c8c4482 100644 --- a/letsencrypt/tests/crypto_util_test.py +++ b/letsencrypt/tests/crypto_util_test.py @@ -221,7 +221,7 @@ class CertLoaderTest(unittest.TestCase): cert, file_type = pyopenssl_load_certificate(CERT) self.assertEqual(cert.digest('sha1'), - OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, CERT).digest('sha1')) + OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, CERT).digest('sha1')) if __name__ == '__main__': From b3bd71b42446d4ea5762c568e4256058791e54bf Mon Sep 17 00:00:00 2001 From: Liam Marshall Date: Sun, 4 Oct 2015 15:43:51 -0500 Subject: [PATCH 74/78] Use previously-returned file_type in assertion --- letsencrypt/tests/crypto_util_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt/tests/crypto_util_test.py b/letsencrypt/tests/crypto_util_test.py index b0c8c4482..c5b298e85 100644 --- a/letsencrypt/tests/crypto_util_test.py +++ b/letsencrypt/tests/crypto_util_test.py @@ -221,7 +221,7 @@ class CertLoaderTest(unittest.TestCase): cert, file_type = pyopenssl_load_certificate(CERT) self.assertEqual(cert.digest('sha1'), - OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, CERT).digest('sha1')) + OpenSSL.crypto.load_certificate(file_type, CERT).digest('sha1')) if __name__ == '__main__': From 9c8f09ec43e9ab84a0434e0a4a9bd9c833d0937d Mon Sep 17 00:00:00 2001 From: Liam Marshall Date: Sun, 4 Oct 2015 15:49:56 -0500 Subject: [PATCH 75/78] Test that loading an invalid cert throws an error --- letsencrypt/tests/crypto_util_test.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/letsencrypt/tests/crypto_util_test.py b/letsencrypt/tests/crypto_util_test.py index c5b298e85..e5217505d 100644 --- a/letsencrypt/tests/crypto_util_test.py +++ b/letsencrypt/tests/crypto_util_test.py @@ -8,6 +8,7 @@ import OpenSSL import mock import zope.component +from letsencrypt import errors from letsencrypt import interfaces from letsencrypt.tests import test_util @@ -216,13 +217,20 @@ class GetSANsFromCSRTest(unittest.TestCase): class CertLoaderTest(unittest.TestCase): """Tests for letsencrypt.crypto_util.pyopenssl_load_certificate""" - def test_it(self): + def test_load_valid_cert(self): from letsencrypt.crypto_util import pyopenssl_load_certificate cert, file_type = pyopenssl_load_certificate(CERT) self.assertEqual(cert.digest('sha1'), OpenSSL.crypto.load_certificate(file_type, CERT).digest('sha1')) + def test_load_invalid_cert(self): + from letsencrypt.crypto_util import pyopenssl_load_certificate + bad_cert_data = CERT.replace("BEGIN CERTIFICATE", "ASDFASDFASDF!!!") + + with self.assertRaises(errors.Error): + cert, file_type = pyopenssl_load_certificate(bad_cert_data) + if __name__ == '__main__': unittest.main() # pragma: no cover From 917a6d63734b63400e9bcf038ccf7d62853ec254 Mon Sep 17 00:00:00 2001 From: Liam Marshall Date: Sun, 4 Oct 2015 15:56:40 -0500 Subject: [PATCH 76/78] Make lint happy, remove unused variables from crypto_util_test --- letsencrypt/tests/crypto_util_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt/tests/crypto_util_test.py b/letsencrypt/tests/crypto_util_test.py index e5217505d..2e04c748a 100644 --- a/letsencrypt/tests/crypto_util_test.py +++ b/letsencrypt/tests/crypto_util_test.py @@ -229,7 +229,7 @@ class CertLoaderTest(unittest.TestCase): bad_cert_data = CERT.replace("BEGIN CERTIFICATE", "ASDFASDFASDF!!!") with self.assertRaises(errors.Error): - cert, file_type = pyopenssl_load_certificate(bad_cert_data) + pyopenssl_load_certificate(bad_cert_data) if __name__ == '__main__': From d5ebc38b33eabf19fa033d4c5b7b266be05639b8 Mon Sep 17 00:00:00 2001 From: Liam Marshall Date: Sun, 4 Oct 2015 20:39:19 -0500 Subject: [PATCH 77/78] Fix pep8 warnings (down to only one now!) --- letsencrypt/cli.py | 25 ++++++++++++++----------- letsencrypt/tests/cli_test.py | 2 -- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 73dd24bdb..0bd5f537e 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -729,11 +729,13 @@ def create_parser(plugins, args): return helpful.parser, helpful.args + # For now unfortunately this constant just needs to match the code below; # there isn't an elegant way to autogenerate it in time. VERBS = ["run", "auth", "install", "revoke", "rollback", "config_changes", "plugins"] HELP_TOPICS = ["all", "security", "paths", "automation", "testing"] + VERBS + def _create_subparsers(helpful): subparsers = helpful.parser.add_subparsers(metavar="SUBCOMMAND") @@ -741,7 +743,7 @@ def _create_subparsers(helpful): if name == "plugins": func = plugins_cmd else: - func = eval(name) # pylint: disable=eval-used + func = eval(name) # pylint: disable=eval-used h = func.__doc__.splitlines()[0] subparser = subparsers.add_parser(name, help=h, description=func.__doc__) subparser.set_defaults(func=func) @@ -762,22 +764,23 @@ def _create_subparsers(helpful): helpful.add_group("plugins", description="Plugin options") helpful.add("auth", - "--csr", type=read_file, help="Path to a Certificate Signing Request (CSR) in DER format.") + "--csr", type=read_file, + help="Path to a Certificate Signing Request (CSR) in DER format.") helpful.add("rollback", - "--checkpoints", type=int, metavar="N", - default=flag_default("rollback_checkpoints"), - help="Revert configuration N number of checkpoints.") + "--checkpoints", type=int, metavar="N", + default=flag_default("rollback_checkpoints"), + help="Revert configuration N number of checkpoints.") helpful.add("plugins", - "--init", action="store_true", help="Initialize plugins.") + "--init", action="store_true", help="Initialize plugins.") helpful.add("plugins", - "--prepare", action="store_true", help="Initialize and prepare plugins.") + "--prepare", action="store_true", help="Initialize and prepare plugins.") helpful.add("plugins", - "--authenticators", action="append_const", dest="ifaces", - const=interfaces.IAuthenticator, help="Limit to authenticator plugins only.") + "--authenticators", action="append_const", dest="ifaces", + const=interfaces.IAuthenticator, help="Limit to authenticator plugins only.") helpful.add("plugins", - "--installers", action="append_const", dest="ifaces", - const=interfaces.IInstaller, help="Limit to installer plugins only.") + "--installers", action="append_const", dest="ifaces", + const=interfaces.IInstaller, help="Limit to installer plugins only.") def _paths_parser(helpful): diff --git a/letsencrypt/tests/cli_test.py b/letsencrypt/tests/cli_test.py index 0a92aba62..d0fae370d 100644 --- a/letsencrypt/tests/cli_test.py +++ b/letsencrypt/tests/cli_test.py @@ -57,7 +57,6 @@ class CLITest(unittest.TestCase): ret = cli.main(args) return ret, None, stderr, client - def test_no_flags(self): with mock.patch('letsencrypt.cli.run') as mock_run: self._call([]) @@ -91,7 +90,6 @@ class CLITest(unittest.TestCase): from letsencrypt import cli self.assertTrue(cli.USAGE in out) - def test_rollback(self): _, _, _, client = self._call(['rollback']) self.assertEqual(1, client.rollback.call_count) From 9a59a41b049482ef4c1fafe467039ddc3d31e4f9 Mon Sep 17 00:00:00 2001 From: Martijn Braam Date: Mon, 5 Oct 2015 15:08:43 +0200 Subject: [PATCH 78/78] Added bootstrap script for archlinux --- bootstrap/archlinux.sh | 2 ++ 1 file changed, 2 insertions(+) create mode 100755 bootstrap/archlinux.sh diff --git a/bootstrap/archlinux.sh b/bootstrap/archlinux.sh new file mode 100755 index 000000000..fbe0987fe --- /dev/null +++ b/bootstrap/archlinux.sh @@ -0,0 +1,2 @@ +#!/bin/sh +pacman -S git python2 python2-virtualenv gcc dialog augeas openssl libffi ca-certificates \ No newline at end of file