diff --git a/AUTHORS.md b/AUTHORS.md new file mode 100644 index 000000000..6ee739bc0 --- /dev/null +++ b/AUTHORS.md @@ -0,0 +1,259 @@ +Authors +======= + +* [Aaron Zirbes](https://github.com/aaronzirbes) +* Aaron Zuehlke +* Ada Lovelace +* [Adam Woodbeck](https://github.com/awoodbeck) +* [Aidin Gharibnavaz](https://github.com/aidin36) +* [AJ ONeal](https://github.com/coolaj86) +* [Alcaro](https://github.com/Alcaro) +* [Alexander Mankuta](https://github.com/pointlessone) +* [Alex Bowers](https://github.com/alexbowers) +* [Alex Conlin](https://github.com/alexconlin) +* [Alex Gaynor](https://github.com/alex) +* [Alex Halderman](https://github.com/jhalderm) +* [Alex Jordan](https://github.com/strugee) +* [Amjad Mashaal](https://github.com/TheNavigat) +* [Andrew Murray](https://github.com/radarhere) +* [Anselm Levskaya](https://github.com/levskaya) +* [Antoine Jacoutot](https://github.com/ajacoutot) +* [asaph](https://github.com/asaph) +* [Axel Beckert](https://github.com/xtaran) +* [Bas](https://github.com/Mechazawa) +* [benbankes](https://github.com/benbankes) +* [Ben Irving](https://github.com/benileo) +* [Benjamin Kerensa](https://github.com/bkerensa) +* [Benjamin Neff](https://github.com/SuperTux88) +* [Benjamin Piouffle](https://github.com/Betree) +* [Ben Ubois](https://github.com/benubois) +* [Ben Wolfe](https://github.com/bwolfe) +* [Bigfish](https://github.com/bwolfe) +* [Blake Griffith](https://github.com/cowlicks) +* [Brad Warren](https://github.com/bmw) +* [Brandon Kraft](https://github.com/kraftbj) +* [Brandon Kreisel](https://github.com/kraftbj) +* [Ceesjan Luiten](https://github.com/quinox) +* [Chad Whitacre](https://github.com/whit537) +* [Chhatoi Pritam Baral](https://github.com/pritambaral) +* [Chris Johns](https://github.com/ter0) +* [Chris Lamb](https://github.com/lamby) +* [chrismarget](https://github.com/chrismarget) +* [Christian Gärtner](https://github.com/ChristianGaertner) +* [Christian Rosentreter](https://github.com/the-real-tokai) +* [Christopher Brown](https://github.com/chbrown) +* [Christopher Manning](https://github.com/christophermanning) +* [Christoph Kisfeld](https://github.com/chk1) +* [Clif Houck](https://github.com/ClifHouck) +* [Cooper Quintin](https://github.com/cooperq) +* [Corey Farwell](https://github.com/frewsxcv) +* [Craig Smith](https://github.com/dashaxiong) +* [Damian Poddebniak](https://github.com/duesee) +* [Damien Nozay](https://github.com/dnozay) +* [Damien Tournoud](https://github.com/damz) +* [DanCld](https://github.com/DanCld) +* [Daniel Albers](https://github.com/AID) +* [Daniel Aleksandersen](https://github.com/da2x) +* [Daniel Convissor](https://github.com/convissor) +* [Daniel Huang](https://github.com/dhuang) +* [Dave Guarino](https://github.com/daguar) +* [David cz](https://github.com/dave-cz) +* [David Dworken](https://github.com/ddworken) +* [David Kreitschmann](https://kreitschmann.de) +* [David Xia](https://github.com/davidxia) +* [Devin Howard](https://github.com/devvmh) +* [dokazaki](https://github.com/dokazaki) +* [Dominic Cleal](https://github.com/domcleal) +* [Dominic Lüchinger](https://github.com/dol) +* [Douglas José](https://github.com/douglasjose) +* [Erica Portnoy](https://github.com/ohemorange) +* [Eric Engestrom](https://github.com/1ace) +* [Eric Rescorla](https://github.com/ekr) +* [Eric Wustrow](https://github.com/ewust) +* [Erik Rose](https://github.com/erikrose) +* [Eugene Kazakov](https://github.com/xgin) +* [Fabian](https://github.com/faerbit) +* [Faidon Liambotis](https://github.com/paravoid) +* [Fan Jiang](https://github.com/tcz001) +* [Felix Schwarz](https://github.com/FelixSchwarz) +* [Felix Yan](https://github.com/felixonmars) +* [Filip Ochnik](https://github.com/filipochnik) +* [Francois Marier](https://github.com/fmarier) +* [Frank](https://github.com/Frankkkkk) +* [Frederic BLANC](https://github.com/fblanc) +* [Garrett Robinson](https://github.com/garrettr) +* [Gene Wood](https://github.com/gene1wood) +* [Geoffroy Doucet](https://www.geoffroydoucet.com) +* [Gian Carlo Pace](https://github.com/gicappa) +* [Gilles Pietri](https://github.com/gilou) +* [Giovanni Pellerano](https://github.com/evilaliv3) +* [Giovanni Toraldo](https://github.com/gionn) +* [Gordin](https://github.com/Gordin) +* [Gregor Dschung](https://github.com/chkpnt) +* [Gregory L. Dietsche](https://github.com/farmergreg) +* [Greg Osuri](https://github.com/gosuri) +* [Guillaume Boudreau](https://github.com/gboudreau) +* [Harlan Lieberman-Berg](https://github.com/hlieberman) +* [Henri Salo](https://github.com/fgeek) +* [Henry Chen](https://github.com/henrychen95) +* [Ingolf Becker](https://github.com/watercrossing) +* [Jaap Eldering](https://github.com/eldering) +* [Jacob Hoffman-Andrews](https://github.com/jsha) +* [Jacob Sachs](https://github.com/jsachs) +* [Jairo Llopis](https://github.com/Yajo) +* [Jakub Warmuz](https://github.com/kuba) +* [James Kasten](https://github.com/jdkasten) +* [Jason Grinblat](https://github.com/ptychomancer) +* [Jay Faulkner](https://github.com/jayofdoom) +* [J.C. Jones](https://github.com/jcjones) +* [Jeff Hodges](https://github.com/jmhodges) +* [Jeremy Gillula](https://github.com/jgillula) +* [Jeroen Ketelaar](https://github.com/JKetelaar) +* [Jeroen Pluimers](https://github.com/jpluimers) +* [j](https://github.com/bit) +* [Jim Tittsler](https://github.com/jimt) +* [Joe Ranweiler](https://github.com/ranweiler) +* [Joerg Sonnenberger](https://github.com/jsonn) +* [John Leach](https://github.com/johnl) +* [John Reed](https://github.com/leerspace) +* [Jonas Berlin](https://github.com/xkr47) +* [Jonathan Herlin](https://github.com/Jonher937) +* [Jon Walsh](https://github.com/code-tree) +* [Joona Hoikkala](https://github.com/joohoi) +* [Josh Soref](https://github.com/jsoref) +* [Joubin Jabbari](https://github.com/joubin) +* [Juho Juopperi](https://github.com/jkjuopperi) +* [Kane York](https://github.com/riking) +* [Kenneth Skovhede](https://github.com/kenkendk) +* [Kevin Burke](https://github.com/kevinburke) +* [Kevin London](https://github.com/kevinlondon) +* [Kubilay Kocak](https://github.com/koobs) +* [LeCoyote](https://github.com/LeCoyote) +* [Lee Watson](https://github.com/TheReverend403) +* [Leo Famulari](https://github.com/lfam) +* [lf](https://github.com/lf-) +* [Liam Marshall](https://github.com/liamim) +* [Lior Sabag](https://github.com/liorsbg) +* [Lipis](https://github.com/lipis) +* [lord63](https://github.com/lord63) +* [Luca Beltrame](https://github.com/lbeltrame) +* [Luca Ebach](https://github.com/lucebac) +* [Luca Olivetti](https://github.com/olivluca) +* [Luke Rogers](https://github.com/lukeroge) +* [Maarten](https://github.com/mrtndwrd) +* [Maikel Martens](https://github.com/krukas) +* [Malte Janduda](https://github.com/MalteJ) +* [Mantas Mikulėnas](https://github.com/grawity) +* [Marcel Krüger](https://github.com/zauguin) +* [Marcos Caceres](https://github.com/marcoscaceres) +* [Marek Viger](https://github.com/freezy-sk) +* [Mario Villaplana](https://github.com/supermari0) +* [Marius Gedminas](https://github.com/mgedmin) +* [Martey Dodoo](https://github.com/martey) +* [Martijn Bastiaan](https://github.com/martijnbastiaan) +* [Martijn Braam](https://github.com/MartijnBraam) +* [Martin Brugger](https://github.com/mbrugger) +* [Mathieu Leduc-Hamel](https://github.com/mlhamel) +* [Matt Bostock](https://github.com/mattbostock) +* [Matthew Ames](https://github.com/SuperMatt) +* [Michael Schumacher](https://github.com/schumaml) +* [Michael Strache](https://github.com/Jarodiv) +* [Michael Sverdlin](https://github.com/sveder) +* [Michal Moravec](https://github.com/https://github.com/Majkl578) +* [Michal Papis](https://github.com/mpapis) +* [Minn Soe](https://github.com/MinnSoe) +* [Min RK](https://github.com/minrk) +* [Miquel Ruiz](https://github.com/miquelruiz) +* [Môshe van der Sterre](https://github.com/moshevds) +* [mrstanwell](https://github.com/mrstanwell) +* [Nav Aulakh](https://github.com/Nav) +* [Nelson Elhage](https://github.com/nelhage) +* [Nick Fong](https://github.com/nickfong) +* [Nick Le Mouton](https://github.com/NoodlesNZ) +* [Nikos Roussos](https://github.com/comzeradd) +* [Noah Swartz](https://github.com/swartzcr) +* [Ola Bini](https://github.com/olabini) +* [Ondřej Súkup](https://github.com/mimi1vx) +* [Ondřej Surý](https://github.com/oerdnj) +* [osirisinferi](https://github.com/osirisinferi) +* Patrick Figel +* [Patrick Heppler](https://github.com/PatrickHeppler) +* [Paul Feitzinger](https://github.com/pfeyz) +* [Pavan Gupta](https://github.com/pavgup) +* [Pavel Pavlov](https://github.com/ghost355) +* [Peter Conrad](https://github.com/pconrad-fb) +* [Peter Eckersley](https://github.com/pde) +* [Peter Mosmans](https://github.com/PeterMosmans) +* [Philippe Langlois](https://github.com/langloisjp) +* [Philipp Spitzer](https://github.com/spitza) +* [Piero Steinger](https://github.com/Jadaw1n) +* [Pierre Jaury](https://github.com/kaiyou) +* [Piotr Kasprzyk](https://github.com/kwadrat) +* [Prayag Verma](https://github.com/pra85) +* [Reinaldo de Souza Jr](https://github.com/juniorz) +* [Remi Rampin](https://github.com/remram44) +* [Rémy HUBSCHER](https://github.com/Natim) +* [Rémy Léone](https://github.com/sieben) +* [Richard Barnes](https://github.com/r-barnes) +* [Richard Panek](https://github.com/kernelpanek) +* [Robert Buchholz](https://github.com/rbu) +* [Robert Habermann](https://github.com/frennkie) +* [Robert Xiao](https://github.com/nneonneo) +* [Roland Shoemaker](https://github.com/rolandshoemaker) +* [Roy Wellington Ⅳ](https://github.com/thanatos) +* [rugk](https://github.com/rugk) +* [Sachi King](https://github.com/nakato) +* [Sagi Kedmi](https://github.com/sagi) +* [Sam Lanning](https://github.com/samlanning) +* [sapics](https://github.com/sapics) +* [Scott Barr](https://github.com/scottjbarr) +* [Scott Merrill](https://github.com/skpy) +* [Sebastian Bögl](https://github.com/TheBoegl) +* [Sebastian Wagner](https://github.com/sebix) +* [sedrubal](https://github.com/sedrubal) +* [Seppe Stas](https://github.com/seppestas) +* [Sergey Nuzdhin](https://github.com/lwolf) +* [Seth Schoen](https://github.com/schoen) +* [Sharif Nassar](https://github.com/mrwacky42) +* [Shaun Cummiskey](https://github.com/ampersign) +* [Shiloh Heurich](https://github.com/sheurich) +* [silverwind](https://github.com/silverwind) +* [Sorvani](https://github.com/sorvani) +* [Spencer Bliven](https://github.com/sbliven) +* [Stacey Sheldon](https://github.com/solidgoldbomb) +* [Stavros Korokithakis](https://github.com/skorokithakis) +* [Stefan Weil](https://github.com/stweil) +* [Steve Desmond](https://github.com/stevedesmond-ca) +* [Tan Jay Jun](https://github.com/jayjun) +* [Tapple Gao](https://github.com/tapple) +* [Telepenin Nikolay](https://github.com/telepenin) +* [Thomas Cottier](https://github.com/tcottier-enalean) +* [Thomas Mayer](https://github.com/thomaszbz) +* [Thomas Waldmann](https://github.com/ThomasWaldmann) +* [Thom Wiggers](https://github.com/thomwiggers) +* [Till Maas](https://github.com/tyll) +* [Timothy Guan-tin Chien](https://github.com/timdream) +* [Torsten Bögershausen](https://github.com/tboegi) +* [Travis Raines](https://github.com/rainest) +* [Trung Ngo](https://github.com/Ngo-The-Trung) +* [Valentin](https://github.com/e00E) +* [venyii](https://github.com/venyii) +* [Viktor Szakats](https://github.com/vszakats) +* [Ville Skyttä](https://github.com/scop) +* [Vinney Cavallo](https://github.com/vcavallo) +* [Vladimir Rutsky](https://github.com/rutsky) +* [Wang Yu](https://github.com/wyhitcs) +* [Ward Vandewege](https://github.com/cure) +* [Whyfoo](https://github.com/whyfoo) +* [Wilfried Teiken](https://github.com/wteiken) +* [Willem Fibbe](https://github.com/fibbers) +* [William Budington](https://github.com/Hainish) +* [Will Newby](https://github.com/willnewby) +* [Will Oller](https://github.com/willoller) +* [Yan](https://github.com/diracdeltas) +* [Yen Chi Hsuan](https://github.com/yan12125) +* [Yomna](https://github.com/ynasser) +* [Yoni Jah](https://github.com/yonjah) +* [YourDaddyIsHere](https://github.com/YourDaddyIsHere) +* [Zach Shepherd](https://github.com/zjs) +* [陈三](https://github.com/chenxsan) diff --git a/acme/setup.py b/acme/setup.py index a640ae6bb..cb12df51f 100644 --- a/acme/setup.py +++ b/acme/setup.py @@ -8,7 +8,6 @@ version = '0.14.0.dev0' # Please update tox.ini when modifying dependency version requirements install_requires = [ - 'argparse', # load_pem_private/public_key (>=0.6) # rsa_recover_prime_factors (>=0.8) 'cryptography>=0.8', @@ -28,6 +27,10 @@ install_requires = [ 'six', ] +# env markers cause problems with older pip and setuptools +if sys.version_info < (2, 7): + install_requires.append('argparse') + dev_extras = [ 'nose', 'tox', @@ -59,6 +62,7 @@ setup( 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', 'Topic :: Internet :: WWW/HTTP', 'Topic :: Security', ], diff --git a/certbot-apache/setup.cfg b/certbot-apache/setup.cfg new file mode 100644 index 000000000..2a9acf13d --- /dev/null +++ b/certbot-apache/setup.cfg @@ -0,0 +1,2 @@ +[bdist_wheel] +universal = 1 diff --git a/certbot-apache/setup.py b/certbot-apache/setup.py index 9a473c584..19f0c74f8 100644 --- a/certbot-apache/setup.py +++ b/certbot-apache/setup.py @@ -42,6 +42,11 @@ setup( 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.3', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', 'Topic :: Internet :: WWW/HTTP', 'Topic :: Security', 'Topic :: System :: Installation/Setup', diff --git a/certbot-compatibility-test/setup.cfg b/certbot-compatibility-test/setup.cfg new file mode 100644 index 000000000..2a9acf13d --- /dev/null +++ b/certbot-compatibility-test/setup.cfg @@ -0,0 +1,2 @@ +[bdist_wheel] +universal = 1 diff --git a/certbot-compatibility-test/setup.py b/certbot-compatibility-test/setup.py index aecae329f..dfd05bfd9 100644 --- a/certbot-compatibility-test/setup.py +++ b/certbot-compatibility-test/setup.py @@ -42,6 +42,11 @@ setup( 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.3', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', 'Topic :: Internet :: WWW/HTTP', 'Topic :: Security', ], diff --git a/certbot-nginx/certbot_nginx/parser.py b/certbot-nginx/certbot_nginx/parser.py index 6f3f344db..9f1a08b3b 100644 --- a/certbot-nginx/certbot_nginx/parser.py +++ b/certbot-nginx/certbot_nginx/parser.py @@ -110,7 +110,7 @@ class NginxParser(object): srv = servers[filename] # workaround undefined loop var in lambdas # Find all the server blocks - _do_for_subarray(tree, lambda x: x[0] == ['server'], + _do_for_subarray(tree, lambda x: len(x) >= 2 and x[0] == ['server'], lambda x, y: srv.append((x[1], y))) # Find 'include' statements in server blocks and append their trees diff --git a/certbot-nginx/certbot_nginx/tests/parser_test.py b/certbot-nginx/certbot_nginx/tests/parser_test.py index 9c2b8656e..8a8bd0ff1 100644 --- a/certbot-nginx/certbot_nginx/tests/parser_test.py +++ b/certbot-nginx/certbot_nginx/tests/parser_test.py @@ -125,13 +125,13 @@ class NginxParserTest(util.NginxTest): False, True, set(['localhost', r'~^(www\.)?(example|bar)\.']), - [], [9, 1, 9]) + [], [10, 1, 9]) vhost2 = obj.VirtualHost(nparser.abs_path('nginx.conf'), [obj.Addr('somename', '8080', False, False), obj.Addr('', '8000', False, False)], False, True, set(['somename', 'another.alias', 'alias']), - [], [9, 1, 12]) + [], [10, 1, 12]) vhost3 = obj.VirtualHost(nparser.abs_path('sites-enabled/example.com'), [obj.Addr('69.50.225.155', '9000', False, False), @@ -186,7 +186,7 @@ class NginxParserTest(util.NginxTest): None, None, None, set(['localhost', r'~^(www\.)?(example|bar)\.']), - None, [9, 1, 9]) + None, [10, 1, 9]) nparser.add_server_directives(mock_vhost, [['foo', 'bar'], ['\n ', 'ssl_certificate', ' ', '/etc/ssl/cert.pem']], diff --git a/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/nginx.conf b/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/nginx.conf index 0af503e6b..ccce4dc1b 100644 --- a/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/nginx.conf +++ b/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/nginx.conf @@ -14,6 +14,9 @@ events { worker_connections 1024; } +empty { +} + include foo.conf; http { diff --git a/certbot-nginx/certbot_nginx/tls_sni_01.py b/certbot-nginx/certbot_nginx/tls_sni_01.py index 2e8125911..347d9f21f 100644 --- a/certbot-nginx/certbot_nginx/tls_sni_01.py +++ b/certbot-nginx/certbot_nginx/tls_sni_01.py @@ -100,9 +100,13 @@ class NginxTlsSni01(common.TLSSNI01): if line[0] == ['http']: body = line[1] found_bucket = False + posn = 0 for inner_line in body: if inner_line[0] == bucket_directive[1]: + if int(inner_line[1]) < int(bucket_directive[3]): + body[posn] = bucket_directive found_bucket = True + posn += 1 if not found_bucket: body.insert(0, bucket_directive) if include_directive not in body: diff --git a/certbot-nginx/setup.cfg b/certbot-nginx/setup.cfg new file mode 100644 index 000000000..2a9acf13d --- /dev/null +++ b/certbot-nginx/setup.cfg @@ -0,0 +1,2 @@ +[bdist_wheel] +universal = 1 diff --git a/certbot-nginx/setup.py b/certbot-nginx/setup.py index 786b5a1a1..63b6f16af 100644 --- a/certbot-nginx/setup.py +++ b/certbot-nginx/setup.py @@ -42,6 +42,11 @@ setup( 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.3', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', 'Topic :: Internet :: WWW/HTTP', 'Topic :: Security', 'Topic :: System :: Installation/Setup', diff --git a/certbot/cli.py b/certbot/cli.py index 2a071bbb1..0a707691a 100644 --- a/certbot/cli.py +++ b/certbot/cli.py @@ -1115,10 +1115,13 @@ def _create_subparsers(helpful): helpful.add( None, "--user-agent", default=None, help="Set a custom user agent string for the client. User agent strings allow " - "the CA to collect high level statistics about success rates by OS and " - "plugin. If you wish to hide your server OS version from the Let's " + "the CA to collect high level statistics about success rates by OS, " + "plugin and use case, and to know when to deprecate support for past Python " + "versions and flags. If you wish to hide this information from the Let's " 'Encrypt server, set this to "". ' - '(default: {0})'.format(sample_user_agent())) + '(default: {0}). The flags encoded in the user agent are: ' + '--duplicate, --force-renew, --allow-subset-of-names, -n, and ' + 'whether any hooks are set.'.format(sample_user_agent())) helpful.add("certonly", "--csr", type=read_file, help="Path to a Certificate Signing Request (CSR) in DER or PEM format." diff --git a/certbot/client.py b/certbot/client.py index 1ef6fc877..24ec79202 100644 --- a/certbot/client.py +++ b/certbot/client.py @@ -1,6 +1,7 @@ """Certbot client API.""" import logging import os +import platform from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives.asymmetric import rsa @@ -53,21 +54,49 @@ def determine_user_agent(config): """ if config.user_agent is None: - ua = "CertbotACMEClient/{0} ({1}) Authenticator/{2} Installer/{3}" - ua = ua.format(certbot.__version__, util.get_os_info_ua(), - config.authenticator, config.installer) + ua = ("CertbotACMEClient/{0} ({1}; {2}) Authenticator/{3} Installer/{4} " + "({5}; flags: {6}) Py/{7}") + ua = ua.format(certbot.__version__, cli.cli_command, util.get_os_info_ua(), + config.authenticator, config.installer, config.verb, + ua_flags(config), platform.python_version()) else: ua = config.user_agent return ua +def ua_flags(config): + "Turn some very important CLI flags into clues in the user agent." + if isinstance(config, DummyConfig): + return "FLAGS" + flags = [] + if config.duplicate: + flags.append("dup") + if config.renew_by_default: + flags.append("frn") + if config.allow_subset_of_names: + flags.append("asn") + if config.noninteractive_mode: + flags.append("n") + hook_names = ("pre", "post", "renew", "manual_auth", "manual_cleanup") + hooks = [getattr(config, h + "_hook") for h in hook_names] + if any(hooks): + flags.append("hook") + return " ".join(flags) + +class DummyConfig(object): + "Shim for computing a sample user agent." + def __init__(self): + self.authenticator = "XXX" + self.installer = "YYY" + self.user_agent = None + self.verb = "SUBCOMMAND" + + def __getattr__(self, name): + "Any config properties we might have are None." + return None + def sample_user_agent(): "Document what this Certbot's user agent string will be like." - class DummyConfig(object): - "Shim for computing a sample user agent." - def __init__(self): - self.authenticator = "XXX" - self.installer = "YYY" - self.user_agent = None + return determine_user_agent(DummyConfig()) diff --git a/certbot/crypto_util.py b/certbot/crypto_util.py index f8f0c2105..5f6b8acf1 100644 --- a/certbot/crypto_util.py +++ b/certbot/crypto_util.py @@ -50,13 +50,13 @@ def save_key(key_pem, key_dir, keyname="key-certbot.pem"): config.strict_permissions) if config.dry_run: key_path = None - logger.info("Dry run, not saving private key to file") + logger.debug("Dry run, not saving private key to file") else: key_f, key_path = util.unique_file( os.path.join(key_dir, keyname), 0o600, "wb") with key_f: key_f.write(key_pem) - logger.info("Saving private key: %s", key_path) + logger.debug("Saving private key: %s", key_path) return util.Key(key_path, key_pem) @@ -85,13 +85,13 @@ def init_save_csr(privkey, names, path): config.strict_permissions) if config.dry_run: csr_filename = None - logger.info("Creating CSR: not saving to file") + logger.debug("Creating CSR: not saving to file") else: csr_f, csr_filename = util.unique_file( os.path.join(path, "csr-certbot.pem"), 0o644, "wb") with csr_f: csr_f.write(csr_pem) - logger.info("Creating CSR: %s", csr_filename) + logger.debug("Creating CSR: %s", csr_filename) return util.CSR(csr_filename, csr_pem, "pem") diff --git a/certbot/display/ops.py b/certbot/display/ops.py index 4bbf9e5b8..ffd4947ad 100644 --- a/certbot/display/ops.py +++ b/certbot/display/ops.py @@ -289,3 +289,60 @@ def _gen_https_names(domains): domains[-1]) return "" + + +def _get_validated(method, validator, message, default=None, **kwargs): + if default is not None: + try: + validator(default) + except errors.Error as error: + logger.debug('Encountered invalid default value "%s" when prompting for "%s"', + default, + message, + exc_info=True) + raise AssertionError('Invalid default "{0}"'.format(default)) + + while True: + code, raw = method(message, default=default, **kwargs) + if code == display_util.OK: + try: + validator(raw) + return code, raw + except errors.Error as error: + logger.debug('Validator rejected "%s" when prompting for "%s"', + raw, + message, + exc_info=True) + zope.component.getUtility(interfaces.IDisplay).notification(str(error), pause=False) + else: + return code, raw + + +def validated_input(validator, *args, **kwargs): + """Like `~certbot.interfaces.IDisplay.input`, but with validation. + + :param callable validator: A method which will be called on the + supplied input. If the method raises a `errors.Error`, its + text will be displayed and the user will be re-prompted. + :param list *args: Arguments to be passed to `~certbot.interfaces.IDisplay.input` + :param dict **kwargs: Arguments to be passed to `~certbot.interfaces.IDisplay.input` + :return: as `~certbot.interfaces.IDisplay.input` + :rtype: tuple + """ + return _get_validated(zope.component.getUtility(interfaces.IDisplay).input, + validator, *args, **kwargs) + + +def validated_directory(validator, *args, **kwargs): + """Like `~certbot.interfaces.IDisplay.directory_select`, but with validation. + + :param callable validator: A method which will be called on the + supplied input. If the method raises a `errors.Error`, its + text will be displayed and the user will be re-prompted. + :param list *args: Arguments to be passed to `~certbot.interfaces.IDisplay.directory_select` + :param dict **kwargs: Arguments to be passed to `~certbot.interfaces.IDisplay.directory_select` + :return: as `~certbot.interfaces.IDisplay.directory_select` + :rtype: tuple + """ + return _get_validated(zope.component.getUtility(interfaces.IDisplay).directory_select, + validator, *args, **kwargs) diff --git a/certbot/display/util.py b/certbot/display/util.py index 87c75739b..4d69f1263 100644 --- a/certbot/display/util.py +++ b/certbot/display/util.py @@ -123,7 +123,6 @@ class FileDisplay(object): def input(self, message, default=None, cli_flag=None, force_interactive=False, **unused_kwargs): - # pylint: disable=no-self-use """Accept input from the user. :param str message: message to display to the user diff --git a/certbot/plugins/webroot.py b/certbot/plugins/webroot.py index b3ec4a692..aad6ffc82 100644 --- a/certbot/plugins/webroot.py +++ b/certbot/plugins/webroot.py @@ -16,6 +16,7 @@ from certbot import cli from certbot import errors from certbot import interfaces from certbot.display import util as display_util +from certbot.display import ops from certbot.plugins import common @@ -136,22 +137,17 @@ to serve all files under specified web root ({0}).""" return None if index == 0 else known_webroots[index - 1] def _prompt_for_new_webroot(self, domain): - display = zope.component.getUtility(interfaces.IDisplay) - - while True: - code, webroot = display.directory_select( - "Input the webroot for {0}:".format(domain), - force_interactive=True) - if code == display_util.HELP: - # Displaying help is not currently implemented - return None - elif code == display_util.CANCEL: - return None - else: # code == display_util.OK - try: - return _validate_webroot(webroot) - except errors.PluginError as error: - display.notification(str(error), pause=False) + code, webroot = ops.validated_directory( + _validate_webroot, + "Input the webroot for {0}:".format(domain), + force_interactive=True) + if code == display_util.HELP: + # Displaying help is not currently implemented + return None + elif code == display_util.CANCEL or code == display_util.ESC: + return None + else: # code == display_util.OK + return _validate_webroot(webroot) def _create_challenge_dirs(self): path_map = self.conf("map") diff --git a/certbot/plugins/webroot_test.py b/certbot/plugins/webroot_test.py index 3e9a68b84..53809764b 100644 --- a/certbot/plugins/webroot_test.py +++ b/certbot/plugins/webroot_test.py @@ -100,22 +100,16 @@ class AuthenticatorTest(unittest.TestCase): self.config.webroot_path = [] self.config.webroot_map = {} - imaginary_dir = os.path.join(os.sep, "imaginary", "dir") - mock_display = mock_get_utility() mock_display.menu.return_value = (display_util.OK, 0,) - mock_display.directory_select.side_effect = ( - (display_util.HELP, -1,), (display_util.CANCEL, -1,), - (display_util.OK, imaginary_dir,), (display_util.OK, self.path,),) - self.auth.perform([self.achall]) + with mock.patch('certbot.display.ops.validated_directory') as m: + m.side_effect = ((display_util.HELP, -1), + (display_util.CANCEL, -1), + (display_util.OK, self.path,)) - self.assertTrue(mock_display.notification.called) - for call in mock_display.notification.call_args_list: - self.assertTrue(imaginary_dir in call[0][0]) + self.auth.perform([self.achall]) - self.assertTrue(mock_display.directory_select.called) - for call in mock_display.directory_select.call_args_list: - self.assertTrue(self.achall.domain in call[0][0]) + self.assertEqual(self.config.webroot_map[self.achall.domain], self.path) def test_perform_missing_root(self): self.config.webroot_path = None diff --git a/certbot/tests/display/ops_test.py b/certbot/tests/display/ops_test.py index 89cd9e43d..306b95841 100644 --- a/certbot/tests/display/ops_test.py +++ b/certbot/tests/display/ops_test.py @@ -14,6 +14,7 @@ from certbot import account from certbot import errors from certbot.display import util as display_util +from certbot.display import ops import certbot.tests.util as test_util @@ -112,7 +113,6 @@ class ChooseAccountTest(test_util.TempDirTestCase): @classmethod def _call(cls, accounts): - from certbot.display import ops return ops.choose_account(accounts) @test_util.patch_get_utility("certbot.display.ops.z_util") @@ -405,5 +405,77 @@ class SuccessRevocationTest(unittest.TestCase): os.linesep), pause=False) self.assertTrue(path in mock_util().notification.call_args[0][0]) + +class ValidatorTests(unittest.TestCase): + """Tests for `validated_input` and `validated_directory`.""" + + __ERROR = "Must be non-empty" + + valid_input = "asdf" + valid_directory = "/var/www/html" + + @staticmethod + def __validator(m): + if m == "": + raise errors.PluginError(ValidatorTests.__ERROR) + + @test_util.patch_get_utility() + def test_input_blank_with_validator(self, mock_util): + mock_util().input.side_effect = [(display_util.OK, ""), + (display_util.OK, ""), + (display_util.OK, ""), + (display_util.OK, self.valid_input)] + + returned = ops.validated_input(self.__validator, "message", force_interactive=True) + self.assertEqual(ValidatorTests.__ERROR, mock_util().notification.call_args[0][0]) + self.assertEqual(returned, (display_util.OK, self.valid_input)) + + @test_util.patch_get_utility() + def test_input_validation_with_default(self, mock_util): + mock_util().input.side_effect = [(display_util.OK, self.valid_input)] + + returned = ops.validated_input(self.__validator, "msg", default="other") + self.assertEqual(returned, (display_util.OK, self.valid_input)) + + @test_util.patch_get_utility() + def test_input_validation_with_bad_default(self, mock_util): + mock_util().input.side_effect = [(display_util.OK, self.valid_input)] + + self.assertRaises(AssertionError, + ops.validated_input, + self.__validator, "msg", default="") + + @test_util.patch_get_utility() + def test_input_cancel_with_validator(self, mock_util): + mock_util().input.side_effect = [(display_util.CANCEL, "")] + + code, unused_raw = ops.validated_input(self.__validator, "message", force_interactive=True) + self.assertEqual(code, display_util.CANCEL) + + @test_util.patch_get_utility() + def test_directory_select_validation(self, mock_util): + mock_util().directory_select.side_effect = [(display_util.OK, ""), + (display_util.OK, self.valid_directory)] + + returned = ops.validated_directory(self.__validator, "msg", force_interactive=True) + self.assertEqual(ValidatorTests.__ERROR, mock_util().notification.call_args[0][0]) + self.assertEqual(returned, (display_util.OK, self.valid_directory)) + + @test_util.patch_get_utility() + def test_directory_select_validation_with_default(self, mock_util): + mock_util().directory_select.side_effect = [(display_util.OK, self.valid_directory)] + + returned = ops.validated_directory(self.__validator, "msg", default="other") + self.assertEqual(returned, (display_util.OK, self.valid_directory)) + + @test_util.patch_get_utility() + def test_directory_select_validation_with_bad_default(self, mock_util): + mock_util().directory_select.side_effect = [(display_util.OK, self.valid_directory)] + + self.assertRaises(AssertionError, + ops.validated_directory, + self.__validator, "msg", default="") + + if __name__ == "__main__": unittest.main() # pragma: no cover diff --git a/docs/install.rst b/docs/install.rst index 6c56584be..a1e91c010 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -22,8 +22,8 @@ your system. System Requirements =================== -Certbot currently requires Python 2.6 or 2.7. By default, it requires root -access in order to write to ``/etc/letsencrypt``, +Certbot currently requires Python 2.6, 2.7, or 3.3+. By default, it requires +root access in order to write to ``/etc/letsencrypt``, ``/var/log/letsencrypt``, ``/var/lib/letsencrypt``; to bind to ports 80 and 443 (if you use the ``standalone`` plugin) and to read and modify webserver configurations (if you use the ``apache`` or ``nginx`` plugins). If none of diff --git a/docs/packaging.rst b/docs/packaging.rst index 5b37c2e24..a60ce72d5 100644 --- a/docs/packaging.rst +++ b/docs/packaging.rst @@ -68,7 +68,8 @@ In Fedora 23+. FreeBSD ------- -- https://svnweb.freebsd.org/ports/head/security/py-certbot/ +- https://www.freshports.org/security/py-acme/ +- https://www.freshports.org/security/py-certbot/ Gentoo ------ diff --git a/docs/using.rst b/docs/using.rst index 7eaa92f84..1bcf1483b 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -182,7 +182,7 @@ The ``http`` challenge will ask you to place a file with a specific name and specific content in the ``/.well-known/acme-challenge/`` directory directly in the top-level directory (“web root”) containing the files served by your webserver. In essence it's the same as the webroot_ plugin, but not automated. -When using the ``dns`` plugin, ``certbot`` will ask you to place a TXT DNS +When using the ``dns`` challenge, ``certbot`` will ask you to place a TXT DNS record with specific contents under the domain name consisting of the hostname for which you want a certificate issued, prepended by ``_acme-challenge``. diff --git a/letshelp-certbot/setup.cfg b/letshelp-certbot/setup.cfg new file mode 100644 index 000000000..2a9acf13d --- /dev/null +++ b/letshelp-certbot/setup.cfg @@ -0,0 +1,2 @@ +[bdist_wheel] +universal = 1 diff --git a/letshelp-certbot/setup.py b/letshelp-certbot/setup.py index b26ab41fe..3ce442b3e 100644 --- a/letshelp-certbot/setup.py +++ b/letshelp-certbot/setup.py @@ -33,6 +33,11 @@ setup( 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.3', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', 'Topic :: Internet :: WWW/HTTP', 'Topic :: Security', 'Topic :: System :: Installation/Setup', diff --git a/setup.cfg b/setup.cfg index 8d68bac30..3b4dbaf87 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,3 +1,6 @@ +[bdist_wheel] +universal = 1 + [easy_install] zip_ok = false diff --git a/setup.py b/setup.py index 0e8d19a22..b0cf57810 100644 --- a/setup.py +++ b/setup.py @@ -36,7 +36,6 @@ version = meta['version'] # https://github.com/pypa/pip/issues/988 for more info. install_requires = [ 'acme=={0}'.format(version), - 'argparse', # We technically need ConfigArgParse 0.10.0 for Python 2.6 support, but # saying so here causes a runtime error against our temporary fork of 0.9.3 # in which we added 2.6 support (see #2243), so we relax the requirement. @@ -56,6 +55,10 @@ install_requires = [ 'zope.interface', ] +# env markers cause problems with older pip and setuptools +if sys.version_info < (2, 7): + install_requires.append('argparse') + dev_extras = [ # Pin astroid==1.3.5, pylint==1.4.2 as a workaround for #289 'astroid==1.3.5', @@ -94,6 +97,11 @@ setup( 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.3', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', 'Topic :: Internet :: WWW/HTTP', 'Topic :: Security', 'Topic :: System :: Installation/Setup',