mirror of
https://github.com/certbot/certbot.git
synced 2026-06-03 22:08:07 -04:00
Merge branch 'master' into reverter
Conflicts: letsencrypt/client/apache/configurator.py letsencrypt/client/crypto_util.py letsencrypt/client/display.py letsencrypt/client/revoker.py letsencrypt/client/tests/apache/configurator_test.py letsencrypt/client/tests/apache/dvsni_test.py letsencrypt/scripts/main.py
This commit is contained in:
commit
8c6cfaded0
27 changed files with 214 additions and 210 deletions
20
.pylintrc
20
.pylintrc
|
|
@ -38,7 +38,7 @@ load-plugins=
|
|||
# --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=
|
||||
disable=fixme,locally-disabled
|
||||
|
||||
|
||||
[REPORTS]
|
||||
|
|
@ -81,7 +81,7 @@ required-attributes=
|
|||
bad-functions=map,filter,apply,input,file
|
||||
|
||||
# Good variable names which should always be accepted, separated by a comma
|
||||
good-names=i,j,k,ex,Run,_
|
||||
good-names=i,j,k,ex,Run,_,fd
|
||||
|
||||
# Bad variable names which should always be refused, separated by a comma
|
||||
bad-names=foo,bar,baz,toto,tutu,tata
|
||||
|
|
@ -94,10 +94,10 @@ name-group=
|
|||
include-naming-hint=no
|
||||
|
||||
# Regular expression matching correct function names
|
||||
function-rgx=[a-z_][a-z0-9_]{2,30}$
|
||||
function-rgx=[a-z_][a-z0-9_]{2,40}$
|
||||
|
||||
# Naming hint for function names
|
||||
function-name-hint=[a-z_][a-z0-9_]{2,30}$
|
||||
function-name-hint=[a-z_][a-z0-9_]{2,40}$
|
||||
|
||||
# Regular expression matching correct variable names
|
||||
variable-rgx=[a-z_][a-z0-9_]{2,30}$
|
||||
|
|
@ -148,14 +148,14 @@ module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
|
|||
module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
|
||||
|
||||
# Regular expression matching correct method names
|
||||
method-rgx=[a-z_][a-z0-9_]{2,30}$
|
||||
method-rgx=[a-z_][a-z0-9_]{2,40}$
|
||||
|
||||
# Naming hint for method names
|
||||
method-name-hint=[a-z_][a-z0-9_]{2,30}$
|
||||
method-name-hint=[a-z_][a-z0-9_]{2,40}$
|
||||
|
||||
# Regular expression which should only match function or class names that do
|
||||
# not require a docstring.
|
||||
no-docstring-rgx=(__.*__)|(test_[A-Za-z0-9_]*)|(_.*)
|
||||
no-docstring-rgx=(__.*__)|(test_[A-Za-z0-9_]*)|(_.*)|(.*Test$)
|
||||
|
||||
# Minimum line length for functions/classes that require docstrings, shorter
|
||||
# ones are exempt.
|
||||
|
|
@ -182,7 +182,7 @@ init-import=no
|
|||
|
||||
# A regular expression matching the name of dummy variables (i.e. expectedly
|
||||
# not used).
|
||||
dummy-variables-rgx=_$|dummy
|
||||
dummy-variables-rgx=(unused)?_.*|dummy
|
||||
|
||||
# List of additional names supposed to be defined in builtins. Remember that
|
||||
# you should avoid to define new builtins when possible.
|
||||
|
|
@ -278,8 +278,8 @@ int-import-graph=
|
|||
[CLASSES]
|
||||
|
||||
# List of interface methods to ignore, separated by a comma. This is used for
|
||||
# instance to not check methods defines in Zope's Interface base class.
|
||||
ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by
|
||||
# instance to not check methods defined in Zope's Interface base class.
|
||||
ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by,implementedBy,providedBy
|
||||
|
||||
# List of method names used to declare (i.e. assign) instance attributes.
|
||||
defining-attr-methods=__init__,__new__,setUp
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ In general:
|
|||
|
||||
```
|
||||
sudo apt-get install python python-setuptools python-virtualenv \
|
||||
python-dev gcc swig dialog libaugeas0 libssl-dev
|
||||
python-dev gcc swig dialog libaugeas0 libssl-dev ca-certificates
|
||||
```
|
||||
|
||||
#### Mac OSX
|
||||
|
|
@ -77,7 +77,7 @@ In order to generate the sphinx documentation, run the following commands.
|
|||
```
|
||||
./venv/bin/python setup.py docs
|
||||
cd docs
|
||||
make html SPHINXBUILD='../venv/bin/python ../venv/bin/sphinx-build'
|
||||
make clean html SPHINXBUILD=../venv/bin/sphinx-build
|
||||
```
|
||||
|
||||
This should generate documentation in the /lets-encrypt-preview/docs/_build/html
|
||||
|
|
|
|||
0
docs/_static/.gitignore
vendored
Normal file
0
docs/_static/.gitignore
vendored
Normal file
|
|
@ -1,5 +1,5 @@
|
|||
:mod:`letsencrypt.client.auth_handler`
|
||||
--------------------------------
|
||||
--------------------------------------
|
||||
|
||||
.. automodule:: letsencrypt.client.auth_handler
|
||||
:members:
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
:mod:`letsencrypt.client.client_authenticator`
|
||||
--------------------------------
|
||||
----------------------------------------------
|
||||
|
||||
.. automodule:: letsencrypt.client.client_authenticator
|
||||
:members:
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ CERT_KEY_BACKUP = os.path.join(WORK_DIR, "keys-certs/")
|
|||
REV_TOKENS_DIR = os.path.join(WORK_DIR, "revocation_tokens/")
|
||||
"""Directory where all revocation tokens are saved."""
|
||||
|
||||
KEY_DIR = os.path.join(SERVER_ROOT, "ssl/")
|
||||
KEY_DIR = os.path.join(SERVER_ROOT, "keys/")
|
||||
"""Where all keys should be stored"""
|
||||
|
||||
CERT_DIR = os.path.join(SERVER_ROOT, "certs/")
|
||||
|
|
|
|||
|
|
@ -423,7 +423,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
"based virtual host", addr)
|
||||
self.add_name_vhost(addr)
|
||||
|
||||
def make_vhost_ssl(self, nonssl_vhost):
|
||||
def make_vhost_ssl(self, nonssl_vhost): # pylint: disable=too-many-locals
|
||||
"""Makes an ssl_vhost version of a nonssl_vhost.
|
||||
|
||||
Duplicates vhost and adds default ssl options
|
||||
|
|
@ -519,8 +519,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
|
||||
return ssl_vhost
|
||||
|
||||
# pylint: disable=no-method-argument,no-self-use,unused-argument
|
||||
def supported_enhancements():
|
||||
def supported_enhancements(self): # pylint: disable=no-self-use
|
||||
"""Returns currently supported enhancements."""
|
||||
return ["redirect"]
|
||||
|
||||
|
|
@ -529,7 +528,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
|
||||
:param str domain: domain to enhance
|
||||
:param str enhancement: enhancement type defined in
|
||||
:class:`letsencrypt.client.CONFIG.ENHANCEMENTS
|
||||
:class:`letsencrypt.client.CONFIG.ENHANCEMENTS`
|
||||
:param options: options for the enhancement
|
||||
:type options: See :class:`letsencrypt.client.CONFIG.ENHANCEMENTS`
|
||||
documentation for appropriate parameter.
|
||||
|
|
@ -544,7 +543,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
except errors.LetsEncryptConfiguratorError:
|
||||
logging.warn("Failed %s for %s", enhancement, domain)
|
||||
|
||||
def _enable_redirect(self, ssl_vhost, options):
|
||||
def _enable_redirect(self, ssl_vhost, unused_options):
|
||||
"""Redirect all equivalent HTTP traffic to ssl_vhost.
|
||||
|
||||
.. todo:: This enhancement should be rewritten and will unfortunately
|
||||
|
|
@ -559,8 +558,8 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
:param ssl_vhost: Destination of traffic, an ssl enabled vhost
|
||||
:type ssl_vhost: :class:`letsencrypt.client.apache.obj.VirtualHost`
|
||||
|
||||
:param options: Not currently used
|
||||
:type options: Not Available
|
||||
:param unused_options: Not currently used
|
||||
:type unused_options: Not Available
|
||||
|
||||
:returns: Success, general_vhost (HTTP vhost)
|
||||
:rtype: (bool, :class:`letsencrypt.client.apache.obj.VirtualHost`)
|
||||
|
|
@ -886,7 +885,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
"""
|
||||
return apache_restart()
|
||||
|
||||
def config_test(self):
|
||||
def config_test(self): # pylint: disable=no-self-use
|
||||
"""Check the configuration of Apache for errors.
|
||||
|
||||
:returns: Success
|
||||
|
|
@ -976,7 +975,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
fulfilled by configurator.
|
||||
|
||||
:returns: list of responses. All responses are returned in the same
|
||||
order as received by the perform function. A None response
|
||||
order as received by the perform function. A None response
|
||||
indicates the challenge was not perfromed.
|
||||
:rtype: list
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ class ApacheDvsni(object):
|
|||
:ivar dvsni_chall: Data required for challenges.
|
||||
where DvsniChall tuples have the following fields
|
||||
`domain` (`str`), `r_b64` (base64 `str`), `nonce` (hex `str`)
|
||||
`key` (:class:`letsencrypt.client.client.Client.Key`)
|
||||
`key` (:class:`letsencrypt.client.client.Client.Key`)
|
||||
:type dvsni_chall: `list` of
|
||||
:class:`letsencrypt.client.challenge_util.DvsniChall`
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
|
||||
class Addr(object):
|
||||
"""Represents an Apache VirtualHost address.
|
||||
r"""Represents an Apache VirtualHost address.
|
||||
|
||||
:param str addr: addr part of vhost address
|
||||
:param str port: port number or \*, or ""
|
||||
|
|
@ -58,7 +58,7 @@ class VirtualHost(object):
|
|||
|
||||
"""
|
||||
|
||||
def __init__(self, filep, path, addrs, ssl, enabled, names=None):
|
||||
def __init__(self, filep, path, addrs, ssl, enabled, names=None): # pylint: disable=too-many-arguments
|
||||
"""Initialize a VH."""
|
||||
self.filep = filep
|
||||
self.path = path
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ from letsencrypt.client import challenge_util
|
|||
from letsencrypt.client import errors
|
||||
|
||||
|
||||
class AuthHandler(object):
|
||||
class AuthHandler(object): # pylint: disable=too-many-instance-attributes
|
||||
"""ACME Authorization Handler for a client.
|
||||
|
||||
:ivar dv_auth: Authenticator capable of solving CONFIG.DV_CHALLENGES
|
||||
|
|
@ -286,7 +286,7 @@ class AuthHandler(object):
|
|||
raise errors.LetsEncryptClientError(
|
||||
"Unimplemented Auth Challenge: %s" % chall["type"])
|
||||
|
||||
def _construct_client_chall(self, chall, domain):
|
||||
def _construct_client_chall(self, chall, domain): # pylint: disable=no-self-use
|
||||
"""Construct Client Type Challenges.
|
||||
|
||||
:param dict chall: Single challenge
|
||||
|
|
|
|||
|
|
@ -226,6 +226,7 @@ def get_cert_info(filename):
|
|||
.. todo:: Pub key is assumed to be RSA... find a good solution to allow EC.
|
||||
|
||||
:param str filename: Name of file containing certificate in PEM format.
|
||||
|
||||
:rtype: dict
|
||||
|
||||
"""
|
||||
|
|
@ -233,8 +234,8 @@ def get_cert_info(filename):
|
|||
cert = M2Crypto.X509.load_cert(filename)
|
||||
|
||||
try:
|
||||
san = cert.get_ext('subjectAltName').get_value()
|
||||
except:
|
||||
san = cert.get_ext("subjectAltName").get_value()
|
||||
except LookupError:
|
||||
san = ""
|
||||
|
||||
return {
|
||||
|
|
@ -251,5 +252,6 @@ def get_cert_info(filename):
|
|||
|
||||
|
||||
def b64_cert_to_pem(b64_der_cert):
|
||||
"""Convert JOSE Base-64 encoded DER cert to PEM."""
|
||||
return M2Crypto.X509.load_cert_der_string(
|
||||
le_util.jose_b64decode(b64_der_cert)).as_pem()
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
"""Lets Encrypt display."""
|
||||
import textwrap
|
||||
|
||||
import dialog
|
||||
|
|
@ -11,10 +12,10 @@ WIDTH = 72
|
|||
HEIGHT = 20
|
||||
|
||||
|
||||
class CommonDisplayMixin(object):
|
||||
"""methods common to both NcursesDisplay and FileDisplay"""
|
||||
class CommonDisplayMixin(object): # pylint: disable=too-few-public-methods
|
||||
"""Mixin with methods common to classes implementing IDisplay."""
|
||||
|
||||
def redirect_by_default(self):
|
||||
def redirect_by_default(self): # pylint: disable=missing-docstring
|
||||
choices = [
|
||||
("Easy", "Allow both HTTP and HTTPS access to these sites"),
|
||||
("Secure", "Make all requests redirect to secure HTTPS access")]
|
||||
|
|
@ -31,6 +32,8 @@ class CommonDisplayMixin(object):
|
|||
|
||||
|
||||
class NcursesDisplay(CommonDisplayMixin):
|
||||
"""Ncurses-based display."""
|
||||
|
||||
zope.interface.implements(interfaces.IDisplay)
|
||||
|
||||
def __init__(self, width=WIDTH, height=HEIGHT):
|
||||
|
|
@ -39,10 +42,10 @@ class NcursesDisplay(CommonDisplayMixin):
|
|||
self.width = width
|
||||
self.height = height
|
||||
|
||||
def generic_notification(self, message):
|
||||
def generic_notification(self, message): # pylint: disable=missing-docstring
|
||||
self.dialog.msgbox(message, width=self.width)
|
||||
|
||||
def generic_menu(self, message, choices, input_text=""):
|
||||
def generic_menu(self, message, choices, unused_input_text=""): # pylint: disable=missing-docstring
|
||||
# Can accept either tuples or just the actual choices
|
||||
if choices and isinstance(choices[0], tuple):
|
||||
code, selection = self.dialog.menu(
|
||||
|
|
@ -55,26 +58,27 @@ class NcursesDisplay(CommonDisplayMixin):
|
|||
|
||||
return code(int(tag) - 1)
|
||||
|
||||
def generic_input(self, message):
|
||||
def generic_input(self, message): # pylint: disable=missing-docstring
|
||||
return self.dialog.inputbox(message)
|
||||
|
||||
def generic_yesno(self, message, yes="Yes", no="No"):
|
||||
def generic_yesno(self, message, yes_label="Yes", no_label="No"): # pylint: disable=missing-docstring
|
||||
return self.dialog.DIALOG_OK == self.dialog.yesno(
|
||||
message, self.height, self.width, yes_label=yes, no_label=no)
|
||||
message, self.height, self.width,
|
||||
yes_label=yes_label, no_label=no_label)
|
||||
|
||||
def filter_names(self, names):
|
||||
def filter_names(self, names): # pylint: disable=missing-docstring
|
||||
choices = [(n, "", 0) for n in names]
|
||||
code, names = self.dialog.checklist(
|
||||
"Which names would you like to activate HTTPS for?",
|
||||
choices=choices)
|
||||
return code, [str(s) for s in names]
|
||||
|
||||
def success_installation(self, domains):
|
||||
def success_installation(self, domains): # pylint: disable=missing-docstring
|
||||
self.dialog.msgbox(
|
||||
"\nCongratulations! You have successfully enabled "
|
||||
+ gen_https_names(domains) + "!", width=self.width)
|
||||
|
||||
def display_certs(self, certs):
|
||||
def display_certs(self, certs): # pylint: disable=missing-docstring
|
||||
list_choices = []
|
||||
for i, c in enumerate(certs):
|
||||
if c['installed']:
|
||||
|
|
@ -99,7 +103,7 @@ class NcursesDisplay(CommonDisplayMixin):
|
|||
tag = -1
|
||||
return code, (int(tag) - 1)
|
||||
|
||||
def confirm_revocation(self, cert):
|
||||
def confirm_revocation(self, cert): # pylint: disable=missing-docstring
|
||||
text = ("Are you sure you would like to revoke the following "
|
||||
"certificate:\n")
|
||||
text += cert_info_frame(cert)
|
||||
|
|
@ -107,7 +111,7 @@ class NcursesDisplay(CommonDisplayMixin):
|
|||
return self.dialog.DIALOG_OK == self.dialog.yesno(
|
||||
text, width=self.width, height=self.height)
|
||||
|
||||
def more_info_cert(self, cert):
|
||||
def more_info_cert(self, cert): # pylint: disable=missing-docstring
|
||||
text = "Certificate Information:\n"
|
||||
text += cert_info_frame(cert)
|
||||
print text
|
||||
|
|
@ -115,20 +119,21 @@ class NcursesDisplay(CommonDisplayMixin):
|
|||
|
||||
|
||||
class FileDisplay(CommonDisplayMixin):
|
||||
"""File-based display."""
|
||||
|
||||
zope.interface.implements(interfaces.IDisplay)
|
||||
|
||||
def __init__(self, outfile):
|
||||
super(FileDisplay, self).__init__()
|
||||
self.outfile = outfile
|
||||
|
||||
def generic_notification(self, message):
|
||||
def generic_notification(self, message): # pylint: disable=missing-docstring
|
||||
side_frame = '-' * 79
|
||||
wm = textwrap.fill(message, 80)
|
||||
text = "\n%s\n%s\n%s\n" % (side_frame, wm, side_frame)
|
||||
self.outfile.write(text)
|
||||
msg = textwrap.fill(message, 80)
|
||||
self.outfile.write("\n%s\n%s\n%s\n" % (side_frame, msg, side_frame))
|
||||
raw_input("Press Enter to Continue")
|
||||
|
||||
def generic_menu(self, message, choices, input_text=""):
|
||||
def generic_menu(self, message, choices, input_text=""): # pylint: disable=missing-docstring
|
||||
# Can take either tuples or single items in choices list
|
||||
if choices and isinstance(choices[0], tuple):
|
||||
choices = ["%s - %s" % (c[0], c[1]) for c in choices]
|
||||
|
|
@ -148,7 +153,7 @@ class FileDisplay(CommonDisplayMixin):
|
|||
|
||||
return code, (selection - 1)
|
||||
|
||||
def generic_input(self, message):
|
||||
def generic_input(self, message): # pylint: disable=no-self-use,missing-docstring
|
||||
ans = raw_input("%s (Enter c to cancel)\n" % message)
|
||||
|
||||
if ans.startswith('c') or ans.startswith('C'):
|
||||
|
|
@ -156,12 +161,12 @@ class FileDisplay(CommonDisplayMixin):
|
|||
else:
|
||||
return OK, ans
|
||||
|
||||
def generic_yesno(self, message, yes_label="Yes", no_label="No"):
|
||||
def generic_yesno(self, message, unused_yes_label="", unused_no_label=""): # pylint: disable=missing-docstring
|
||||
self.outfile.write("\n%s\n" % textwrap.fill(message, 80))
|
||||
ans = raw_input("y/n: ")
|
||||
return ans.startswith('y') or ans.startswith('Y')
|
||||
|
||||
def filter_names(self, names):
|
||||
def filter_names(self, names): # pylint: disable=missing-docstring
|
||||
code, tag = self.generic_menu(
|
||||
"Choose the names would you like to upgrade to HTTPS?",
|
||||
names, "Select the number of the name: ")
|
||||
|
|
@ -169,7 +174,7 @@ class FileDisplay(CommonDisplayMixin):
|
|||
# Make sure to return a list...
|
||||
return code, [names[tag]]
|
||||
|
||||
def display_certs(self, certs):
|
||||
def display_certs(self, certs): # pylint: disable=missing-docstring
|
||||
menu_choices = [(str(i+1), str(c["cn"]) + " - " + c["pub_key"] +
|
||||
" - " + str(c["not_before"])[:-6])
|
||||
for i, c in enumerate(certs)]
|
||||
|
|
@ -206,14 +211,13 @@ class FileDisplay(CommonDisplayMixin):
|
|||
|
||||
return code, selection
|
||||
|
||||
def success_installation(self, domains):
|
||||
s_f = '*' * 79
|
||||
wm = textwrap.fill(("Congratulations! You have successfully " +
|
||||
"enabled %s!") % gen_https_names(domains))
|
||||
msg = "%s\n%s\n%s\n"
|
||||
self.outfile.write(msg % (s_f, wm, s_f))
|
||||
def success_installation(self, domains): # pylint: disable=missing-docstring
|
||||
side_frame = '*' * 79
|
||||
msg = textwrap.fill("Congratulations! You have successfully "
|
||||
"enabled %s!" % gen_https_names(domains))
|
||||
self.outfile.write("%s\n%s\n%s\n" % (side_frame, msg, side_frame))
|
||||
|
||||
def confirm_revocation(self, cert):
|
||||
def confirm_revocation(self, cert): # pylint: disable=missing-docstring
|
||||
self.outfile.write("Are you sure you would like to revoke "
|
||||
"the following certificate:\n")
|
||||
self.outfile.write(cert_info_frame(cert))
|
||||
|
|
@ -221,7 +225,7 @@ class FileDisplay(CommonDisplayMixin):
|
|||
ans = raw_input("y/n")
|
||||
return ans.startswith('y') or ans.startswith('Y')
|
||||
|
||||
def more_info_cert(self, cert):
|
||||
def more_info_cert(self, cert): # pylint: disable=missing-docstring
|
||||
self.outfile.write("\nCertificate Information:\n")
|
||||
self.outfile.write(cert_info_frame(cert))
|
||||
|
||||
|
|
@ -230,14 +234,14 @@ CANCEL = "cancel"
|
|||
HELP = "help"
|
||||
|
||||
|
||||
def cert_info_frame(cert):
|
||||
def cert_info_frame(cert): # pylint: disable=missing-docstring
|
||||
text = "-" * (WIDTH - 4) + "\n"
|
||||
text += cert_info_string(cert)
|
||||
text += "-" * (WIDTH - 4)
|
||||
return text
|
||||
|
||||
|
||||
def cert_info_string(cert):
|
||||
def cert_info_string(cert): # pylint: disable=missing-docstring
|
||||
text = "Subject: %s\n" % cert["subject"]
|
||||
text += "SAN: %s\n" % cert["san"]
|
||||
text += "Issuer: %s\n" % cert["issuer"]
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
"""Interactive challenge."""
|
||||
import textwrap
|
||||
|
||||
import dialog
|
||||
|
|
@ -23,7 +24,7 @@ class InteractiveChallenge(object):
|
|||
super(InteractiveChallenge, self).__init__()
|
||||
self.string = string
|
||||
|
||||
def perform(self, quiet=True):
|
||||
def perform(self, quiet=True): # pylint: disable=missing-docstring
|
||||
if quiet:
|
||||
dialog.Dialog().msgbox(
|
||||
self.get_display_string(), width=self.BOX_SIZE)
|
||||
|
|
@ -33,7 +34,7 @@ class InteractiveChallenge(object):
|
|||
|
||||
return True
|
||||
|
||||
def get_display_string(self):
|
||||
def get_display_string(self): # pylint: disable=missing-docstring
|
||||
return (textwrap.fill(self.string, width=self.BOX_SIZE) +
|
||||
"\n\nPlease Press Enter to Continue")
|
||||
|
||||
|
|
|
|||
|
|
@ -49,25 +49,24 @@ def check_permissions(filepath, mode, uid=0):
|
|||
return stat.S_IMODE(file_stat.st_mode) == mode and file_stat.st_uid == uid
|
||||
|
||||
|
||||
def unique_file(default_name, mode=0o777):
|
||||
def unique_file(path, mode=0o777):
|
||||
"""Safely finds a unique file for writing only (by default).
|
||||
|
||||
:param str default_name: Default file name
|
||||
:param str path: path/filename.ext
|
||||
:param int mode: File mode
|
||||
|
||||
:return: tuple of file object and file name
|
||||
|
||||
"""
|
||||
count = 1
|
||||
f_parsed = os.path.splitext(default_name)
|
||||
while 1:
|
||||
path, tail = os.path.split(path)
|
||||
count = 0
|
||||
while True:
|
||||
fname = os.path.join(path, "%04d_%s" % (count, tail))
|
||||
try:
|
||||
file_d = os.open(
|
||||
default_name, os.O_CREAT | os.O_EXCL | os.O_RDWR, mode)
|
||||
return os.fdopen(file_d, 'w'), default_name
|
||||
file_d = os.open(fname, os.O_CREAT | os.O_EXCL | os.O_RDWR, mode)
|
||||
return os.fdopen(file_d, 'w'), fname
|
||||
except OSError:
|
||||
pass
|
||||
default_name = f_parsed[0] + '_' + str(count) + f_parsed[1]
|
||||
count += 1
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -30,8 +30,8 @@ class RecoveryContact(object):
|
|||
self.contact = contact
|
||||
self.poll_delay = poll_delay
|
||||
|
||||
def perform(self, quiet=True):
|
||||
d = dialog.Dialog()
|
||||
def perform(self, quiet=True): # pylint: disable=missing-docstring
|
||||
d = dialog.Dialog() # pylint: disable=invalid-name
|
||||
if quiet:
|
||||
if self.success_url:
|
||||
d.infobox(self.get_display_string())
|
||||
|
|
@ -50,7 +50,7 @@ class RecoveryContact(object):
|
|||
|
||||
return True
|
||||
|
||||
def cleanup(self):
|
||||
def cleanup(self): # pylint: disable=no-self-use,missing-docstring
|
||||
return
|
||||
|
||||
def get_display_string(self):
|
||||
|
|
@ -91,7 +91,7 @@ class RecoveryContact(object):
|
|||
else:
|
||||
return False
|
||||
|
||||
def prompt_continue(self, quiet=True):
|
||||
def prompt_continue(self, quiet=True): # pylint: disable=no-self-use
|
||||
"""Prompt user for continuation.
|
||||
|
||||
:param bool quiet: Display dialog box if True, raw prompt otherwise.
|
||||
|
|
@ -109,7 +109,7 @@ class RecoveryContact(object):
|
|||
|
||||
return ans.startswith('y') or ans.startswith('Y')
|
||||
|
||||
def generate_response(self):
|
||||
def generate_response(self): # pylint: disable=missing-docstring
|
||||
if not self.token:
|
||||
return {"type": "recoveryContact"}
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -57,12 +57,13 @@ class Revoker(object):
|
|||
return
|
||||
|
||||
c_sha1_vh = {}
|
||||
|
||||
if self.installer is not None:
|
||||
for (cert, _, path) in self.installer.get_all_certs_keys():
|
||||
try:
|
||||
c_sha1_vh[M2Crypto.X509.load_cert(
|
||||
cert).get_fingerprint(md='sha1')] = path
|
||||
except:
|
||||
except M2Crypto.X509.X509Error:
|
||||
continue
|
||||
|
||||
with open(list_file, 'rb') as csvfile:
|
||||
|
|
|
|||
|
|
@ -45,7 +45,8 @@ class ACMEObjectValidateTest(unittest.TestCase):
|
|||
class PrettyTest(unittest.TestCase):
|
||||
"""Tests for letsencrypt.client.acme.pretty."""
|
||||
|
||||
def _call(self, json_string):
|
||||
@classmethod
|
||||
def _call(cls, json_string):
|
||||
from letsencrypt.client.acme import pretty
|
||||
return pretty(json_string)
|
||||
|
||||
|
|
@ -64,31 +65,19 @@ class MessageFactoriesTest(unittest.TestCase):
|
|||
self.nonce = '\xec\xd6\xf2oYH\xeb\x13\xd5#q\xe0\xdd\xa2\x92\xa9'
|
||||
self.b64nonce = '7Nbyb1lI6xPVI3Hg3aKSqQ'
|
||||
|
||||
def _validate(self, msg):
|
||||
@classmethod
|
||||
def _validate(cls, msg):
|
||||
from letsencrypt.client.acme import SCHEMATA
|
||||
jsonschema.validate(msg, SCHEMATA[msg['type']])
|
||||
|
||||
def _signature(self, sig):
|
||||
return {
|
||||
'nonce': self.b64nonce,
|
||||
'alg': 'RS256',
|
||||
'jwk': {
|
||||
'kty': 'RSA',
|
||||
'e': 'AQAB',
|
||||
'n': 'rHVztFHtH92ucFJD_N_HW9AsdRsUuHUBBBDlHwNlRd3fp5'
|
||||
'80rv2-6QWE30cWgdmJS86ObRz6lUTor4R0T-3C5Q',
|
||||
},
|
||||
'sig': sig,
|
||||
}
|
||||
|
||||
def test_challenge_request(self):
|
||||
from letsencrypt.client.acme import challenge_request
|
||||
msg = challenge_request('example.com')
|
||||
self._validate(msg)
|
||||
self.assertEqual(msg, {
|
||||
'type': 'challengeRequest',
|
||||
'identifier': 'example.com',
|
||||
})
|
||||
self._validate(msg)
|
||||
|
||||
def test_authorization_request(self):
|
||||
from letsencrypt.client.acme import authorization_request
|
||||
|
|
@ -112,51 +101,57 @@ class MessageFactoriesTest(unittest.TestCase):
|
|||
self.nonce,
|
||||
)
|
||||
|
||||
self._validate(msg)
|
||||
self.assertEqual(
|
||||
msg.pop('signature')['sig'],
|
||||
'VkpReso87ogwGul2MGck96TkYs4QoblIgNthgrm9O7EBGlzCRCnTHnx'
|
||||
'bj6loqaC4f5bn1rgS927Gp1Kvbqnmqg'
|
||||
)
|
||||
self.assertEqual(msg, {
|
||||
'type': 'authorizationRequest',
|
||||
'sessionID': 'aefoGaavieG9Wihuk2aufai3aeZ5EeW4',
|
||||
'nonce': 'czpsrF0KMH6dgajig3TGHw',
|
||||
'signature': self._signature(
|
||||
'VkpReso87ogwGul2MGck96TkYs4QoblIgNthgrm9O7EBGlzCRCnTHnx'
|
||||
'bj6loqaC4f5bn1rgS927Gp1Kvbqnmqg'),
|
||||
'responses': responses,
|
||||
})
|
||||
self._validate(msg)
|
||||
|
||||
def test_certificate_request(self):
|
||||
from letsencrypt.client.acme import certificate_request
|
||||
msg = certificate_request(
|
||||
'TODO: real DER CSR?', self.privkey, self.nonce)
|
||||
self._validate(msg)
|
||||
self.assertEqual(
|
||||
msg.pop('signature')['sig'],
|
||||
'HEQVN4MU1yDrArP2T7WZQ12XlHCn5DgTPgb5eWT5_vjRPppLSNe6uWE'
|
||||
'x9SFwG9d9umqn49nZCSW7uskA2lcW6Q'
|
||||
)
|
||||
self.assertEqual(msg, {
|
||||
'type': 'certificateRequest',
|
||||
'csr': 'VE9ETzogcmVhbCBERVIgQ1NSPw',
|
||||
'signature': self._signature(
|
||||
'HEQVN4MU1yDrArP2T7WZQ12XlHCn5DgTPgb5eWT5_vjRPppLSNe6uWE'
|
||||
'x9SFwG9d9umqn49nZCSW7uskA2lcW6Q'),
|
||||
})
|
||||
self._validate(msg)
|
||||
|
||||
def test_revocation_request(self):
|
||||
from letsencrypt.client.acme import revocation_request
|
||||
msg = revocation_request(
|
||||
'TODO: real DER cert?', self.privkey, self.nonce)
|
||||
self._validate(msg)
|
||||
self.assertEqual(
|
||||
msg.pop('signature')['sig'],
|
||||
'ABXA1IsyTalTXIojxmGnIUGyZASmvqEvTQ98jJ5KFs2FTswLEmsoqFX'
|
||||
'fU6l5_fous-tsbXOfLN-7PjfZ5XWPvg'
|
||||
)
|
||||
self.assertEqual(msg, {
|
||||
'type': 'revocationRequest',
|
||||
'certificate': 'VE9ETzogcmVhbCBERVIgY2VydD8',
|
||||
'signature': self._signature(
|
||||
'ABXA1IsyTalTXIojxmGnIUGyZASmvqEvTQ98jJ5KFs2FTswLEmsoqFX'
|
||||
'fU6l5_fous-tsbXOfLN-7PjfZ5XWPvg'),
|
||||
})
|
||||
self._validate(msg)
|
||||
|
||||
def test_status_request(self):
|
||||
from letsencrypt.client.acme import status_request
|
||||
msg = status_request(u'O7-s9MNq1siZHlgrMzi9_A')
|
||||
self._validate(msg)
|
||||
self.assertEqual(msg, {
|
||||
'type': 'statusRequest',
|
||||
'token': u'O7-s9MNq1siZHlgrMzi9_A',
|
||||
})
|
||||
self._validate(msg)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
"""Test for letsencrypt.client.apache.configurator."""
|
||||
import os
|
||||
import pkg_resources
|
||||
import re
|
||||
import shutil
|
||||
import unittest
|
||||
|
|
@ -15,30 +14,19 @@ from letsencrypt.client.apache import configurator
|
|||
from letsencrypt.client.apache import obj
|
||||
from letsencrypt.client.apache import parser
|
||||
|
||||
from letsencrypt.client.tests.apache import config_util
|
||||
from letsencrypt.client.tests.apache import util
|
||||
|
||||
|
||||
class TwoVhost80Test(unittest.TestCase):
|
||||
class TwoVhost80Test(util.ApacheTest):
|
||||
"""Test two standard well configured HTTP vhosts."""
|
||||
|
||||
def setUp(self):
|
||||
self.temp_dir, self.config_dir, self.work_dir = config_util.dir_setup(
|
||||
"debian_apache_2_4/two_vhost_80")
|
||||
super(TwoVhost80Test, self).setUp()
|
||||
|
||||
self.ssl_options = config_util.setup_apache_ssl_options(self.config_dir)
|
||||
self.config = util.get_apache_configurator(
|
||||
self.config_path, self.config_dir, self.work_dir, self.ssl_options)
|
||||
|
||||
# Final slash is currently important
|
||||
self.config_path = os.path.join(
|
||||
self.temp_dir, "debian_apache_2_4/two_vhost_80/apache2/")
|
||||
|
||||
with mock.patch("letsencrypt.client.apache.configurator."
|
||||
"mod_loaded") as mock_load:
|
||||
mock_load.return_value = True
|
||||
self.config = config_util.get_apache_configurator(
|
||||
self.config_path, self.config_dir, self.work_dir,
|
||||
self.ssl_options)
|
||||
|
||||
self.vh_truth = config_util.get_vh_truth(
|
||||
self.vh_truth = util.get_vh_truth(
|
||||
self.temp_dir, "debian_apache_2_4/two_vhost_80")
|
||||
|
||||
def tearDown(self):
|
||||
|
|
@ -172,12 +160,7 @@ class TwoVhost80Test(unittest.TestCase):
|
|||
def test_perform(self, mock_restart, mock_dvsni_perform):
|
||||
# Only tests functionality specific to configurator.perform
|
||||
# Note: As more challenges are offered this will have to be expanded
|
||||
rsa256_file = pkg_resources.resource_filename(
|
||||
"letsencrypt.client.tests", 'testdata/rsa256_key.pem')
|
||||
rsa256_pem = pkg_resources.resource_string(
|
||||
"letsencrypt.client.tests", 'testdata/rsa256_key.pem')
|
||||
|
||||
auth_key = client.Client.Key(rsa256_file, rsa256_pem)
|
||||
auth_key = client.Client.Key(self.rsa256_file, self.rsa256_pem)
|
||||
chall1 = challenge_util.DvsniChall(
|
||||
"encryption-example.demo",
|
||||
"jIq_Xy1mXGN37tb4L6Xj_es58fW571ZNyXekdZzhh7Q",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
"""Test for letsencrypt.client.apache.dvsni."""
|
||||
import os
|
||||
import pkg_resources
|
||||
import unittest
|
||||
import shutil
|
||||
|
|
@ -10,30 +9,19 @@ from letsencrypt.client import challenge_util
|
|||
from letsencrypt.client import client
|
||||
from letsencrypt.client import CONFIG
|
||||
|
||||
from letsencrypt.client.tests.apache import config_util
|
||||
from letsencrypt.client.tests.apache import util
|
||||
|
||||
|
||||
class DvsniPerformTest(unittest.TestCase):
|
||||
class DvsniPerformTest(util.ApacheTest):
|
||||
"""Test the ApacheDVSNI challenge."""
|
||||
|
||||
def setUp(self):
|
||||
super(DvsniPerformTest, self).setUp()
|
||||
|
||||
config = util.get_apache_configurator(
|
||||
self.config_path, self.config_dir, self.work_dir, self.ssl_options)
|
||||
|
||||
from letsencrypt.client.apache import dvsni
|
||||
|
||||
self.temp_dir, self.config_dir, self.work_dir = config_util.dir_setup(
|
||||
"debian_apache_2_4/two_vhost_80")
|
||||
|
||||
self.ssl_options = config_util.setup_apache_ssl_options(self.config_dir)
|
||||
|
||||
# Final slash is currently important
|
||||
self.config_path = os.path.join(
|
||||
self.temp_dir, "debian_apache_2_4/two_vhost_80/apache2/")
|
||||
|
||||
with mock.patch("letsencrypt.client.apache.configurator."
|
||||
"mod_loaded") as mock_load:
|
||||
mock_load.return_value = True
|
||||
config = config_util.get_apache_configurator(
|
||||
self.config_path, self.config_dir, self.work_dir,
|
||||
self.ssl_options)
|
||||
|
||||
self.sni = dvsni.ApacheDvsni(config)
|
||||
|
||||
rsa256_file = pkg_resources.resource_filename(
|
||||
|
|
@ -148,3 +136,7 @@ class DvsniPerformTest(unittest.TestCase):
|
|||
self.assertEqual(
|
||||
vhost.names,
|
||||
set([str(self.challs[1].nonce + CONFIG.INVALID_EXT)]))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
|
|
|||
|
|
@ -11,23 +11,18 @@ import zope.component
|
|||
from letsencrypt.client import display
|
||||
from letsencrypt.client import errors
|
||||
from letsencrypt.client.apache import parser
|
||||
from letsencrypt.client.tests.apache import config_util
|
||||
|
||||
from letsencrypt.client.tests.apache import util
|
||||
|
||||
|
||||
class ApacheParserTest(unittest.TestCase):
|
||||
class ApacheParserTest(util.ApacheTest):
|
||||
"""Apache Parser Test."""
|
||||
|
||||
def setUp(self):
|
||||
super(ApacheParserTest, self).setUp()
|
||||
|
||||
zope.component.provideUtility(display.FileDisplay(sys.stdout))
|
||||
|
||||
self.temp_dir, self.config_dir, self.work_dir = config_util.dir_setup(
|
||||
"debian_apache_2_4/two_vhost_80")
|
||||
|
||||
self.ssl_options = config_util.setup_apache_ssl_options(self.config_dir)
|
||||
|
||||
# Final slash is currently important
|
||||
self.config_path = os.path.join(
|
||||
self.temp_dir, "debian_apache_2_4/two_vhost_80/apache2/")
|
||||
|
||||
self.parser = parser.ApacheParser(
|
||||
augeas.Augeas(flags=augeas.Augeas.NONE),
|
||||
self.config_path, self.ssl_options)
|
||||
|
|
@ -112,3 +107,7 @@ class ApacheParserTest(unittest.TestCase):
|
|||
|
||||
self.assertEqual(results["default"], results["listen"])
|
||||
self.assertEqual(results["default"], results["name"])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
"""Common utilities for letsencrypt.client.apache."""
|
||||
import os
|
||||
import pkg_resources
|
||||
import shutil
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
import mock
|
||||
|
||||
|
|
@ -10,6 +12,26 @@ from letsencrypt.client.apache import configurator
|
|||
from letsencrypt.client.apache import obj
|
||||
|
||||
|
||||
class ApacheTest(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(ApacheTest, self).setUp()
|
||||
|
||||
self.temp_dir, self.config_dir, self.work_dir = dir_setup(
|
||||
"debian_apache_2_4/two_vhost_80")
|
||||
|
||||
self.ssl_options = setup_apache_ssl_options(self.config_dir)
|
||||
|
||||
# Final slash is currently important
|
||||
self.config_path = os.path.join(
|
||||
self.temp_dir, "debian_apache_2_4/two_vhost_80/apache2/")
|
||||
|
||||
self.rsa256_file = pkg_resources.resource_filename(
|
||||
"letsencrypt.client.tests", 'testdata/rsa256_key.pem')
|
||||
self.rsa256_pem = pkg_resources.resource_string(
|
||||
"letsencrypt.client.tests", 'testdata/rsa256_key.pem')
|
||||
|
||||
|
||||
def dir_setup(test_dir="debian_apache_2_4/two_vhost_80"):
|
||||
"""Setup the directories necessary for the configurator."""
|
||||
temp_dir = tempfile.mkdtemp("temp")
|
||||
|
|
@ -1,21 +1,26 @@
|
|||
"""Test auth_handler.py."""
|
||||
"""Tests for letsencrypt.client.auth_handler."""
|
||||
import unittest
|
||||
|
||||
import mock
|
||||
|
||||
from letsencrypt.client import errors
|
||||
from letsencrypt.client.tests import acme_util
|
||||
|
||||
|
||||
TRANSLATE = {"dvsni": "DvsniChall",
|
||||
"simpleHttps": "SimpleHttpsChall",
|
||||
"dns": "DnsChall",
|
||||
"recoveryToken": "RecTokenChall",
|
||||
"recoveryContact": "RecContactChall",
|
||||
"proofOfPossession": "PopChall"}
|
||||
TRANSLATE = {
|
||||
"dvsni": "DvsniChall",
|
||||
"simpleHttps": "SimpleHttpsChall",
|
||||
"dns": "DnsChall",
|
||||
"recoveryToken": "RecTokenChall",
|
||||
"recoveryContact": "RecContactChall",
|
||||
"proofOfPossession": "PopChall",
|
||||
}
|
||||
|
||||
|
||||
# pylint: disable=protected-access
|
||||
class SatisfyChallengesTest(unittest.TestCase):
|
||||
"""verify_identities test."""
|
||||
|
||||
def setUp(self):
|
||||
from letsencrypt.client.auth_handler import AuthHandler
|
||||
|
||||
|
|
@ -225,7 +230,7 @@ class SatisfyChallengesTest(unittest.TestCase):
|
|||
self.assertEqual(
|
||||
type(self.handler.client_c["4"][0].chall).__name__, "RecTokenChall")
|
||||
|
||||
def _get_exp_response(self, domain, path, challenges):
|
||||
def _get_exp_response(self, domain, path, challenges): # pylint: disable=no-self-use
|
||||
exp_resp = ["null"] * len(challenges)
|
||||
for i in path:
|
||||
exp_resp[i] = TRANSLATE[challenges[i]["type"]] + str(domain)
|
||||
|
|
@ -283,7 +288,6 @@ class GetAuthorizationsTest(unittest.TestCase):
|
|||
self.handler.dv_c[dom], self.handler.client_c[dom] = dv_c, c_c
|
||||
|
||||
def test_progress_failure(self):
|
||||
from letsencrypt.client.errors import LetsEncryptAuthHandlerError
|
||||
challenges = acme_util.get_challenges()
|
||||
self.handler.add_chall_msg(
|
||||
"0",
|
||||
|
|
@ -294,7 +298,7 @@ class GetAuthorizationsTest(unittest.TestCase):
|
|||
self.mock_sat_chall.side_effect = self._sat_failure
|
||||
|
||||
self.assertRaises(
|
||||
LetsEncryptAuthHandlerError, self.handler.get_authorizations)
|
||||
errors.LetsEncryptAuthHandlerError, self.handler.get_authorizations)
|
||||
|
||||
# Check to make sure program didn't loop
|
||||
self.assertEqual(self.mock_sat_chall.call_count, 1)
|
||||
|
|
@ -327,8 +331,6 @@ class GetAuthorizationsTest(unittest.TestCase):
|
|||
[mock.call("1"), mock.call("0")])
|
||||
|
||||
def _sat_incremental(self):
|
||||
from letsencrypt.client.errors import LetsEncryptAuthHandlerError
|
||||
|
||||
# Exact responses don't matter, just path/response match
|
||||
if self.iteration == 0:
|
||||
# Only solve one of "0" required challs
|
||||
|
|
@ -353,7 +355,7 @@ class GetAuthorizationsTest(unittest.TestCase):
|
|||
self.handler.responses["0"][3] = "finally!"
|
||||
|
||||
else:
|
||||
raise LetsEncryptAuthHandlerError(
|
||||
raise errors.LetsEncryptAuthHandlerError(
|
||||
"Failed incremental test: too many invocations")
|
||||
|
||||
def _test_finished(self):
|
||||
|
|
@ -408,12 +410,12 @@ class PathSatisfiedTest(unittest.TestCase):
|
|||
self.assertFalse(self.handler._path_satisfied(dom[i]))
|
||||
|
||||
|
||||
def gen_auth_resp(chall_list):
|
||||
def gen_auth_resp(chall_list): # pylint: disable=missing-docstring
|
||||
return ["%s%s" % (type(chall).__name__, chall.domain)
|
||||
for chall in chall_list]
|
||||
|
||||
|
||||
def gen_path(str_list, challenges):
|
||||
def gen_path(str_list, challenges): # pylint: disable=missing-docstring
|
||||
path = []
|
||||
for i, chall in enumerate(challenges):
|
||||
for str_chall in str_list:
|
||||
|
|
|
|||
|
|
@ -30,7 +30,8 @@ class CreateSigTest(unittest.TestCase):
|
|||
'AX9AFJHk-bCMQPJbSzXKjG6H1IWbvxjS2Ew',
|
||||
}
|
||||
|
||||
def _call(self, *args, **kwargs):
|
||||
@classmethod
|
||||
def _call(cls, *args, **kwargs):
|
||||
from letsencrypt.client.crypto_util import create_sig
|
||||
return create_sig(*args, **kwargs)
|
||||
|
||||
|
|
@ -50,7 +51,8 @@ class CreateSigTest(unittest.TestCase):
|
|||
class ValidCSRTest(unittest.TestCase):
|
||||
"""Tests for letsencrypt.client.crypto_util.valid_csr."""
|
||||
|
||||
def _call(self, csr):
|
||||
@classmethod
|
||||
def _call(cls, csr):
|
||||
from letsencrypt.client.crypto_util import valid_csr
|
||||
return valid_csr(csr)
|
||||
|
||||
|
|
@ -80,7 +82,8 @@ class ValidCSRTest(unittest.TestCase):
|
|||
class CSRMatchesPubkeyTest(unittest.TestCase):
|
||||
"""Tests for letsencrypt.client.crypto_util.csr_matches_pubkey."""
|
||||
|
||||
def _call_testdata(self, name, privkey):
|
||||
@classmethod
|
||||
def _call_testdata(cls, name, privkey):
|
||||
from letsencrypt.client.crypto_util import csr_matches_pubkey
|
||||
return csr_matches_pubkey(pkg_resources.resource_string(
|
||||
__name__, os.path.join('testdata', name)), privkey)
|
||||
|
|
@ -95,7 +98,7 @@ class CSRMatchesPubkeyTest(unittest.TestCase):
|
|||
class MakeKeyTest(unittest.TestCase):
|
||||
"""Tests for letsencrypt.client.crypto_util.make_key."""
|
||||
|
||||
def test_it(self):
|
||||
def test_it(self): # pylint: disable=no-self-use
|
||||
from letsencrypt.client.crypto_util import make_key
|
||||
M2Crypto.RSA.load_key_string(make_key(1024))
|
||||
M2Crypto.RSA.load_key_string(make_key(2048))
|
||||
|
|
@ -105,7 +108,8 @@ class MakeKeyTest(unittest.TestCase):
|
|||
class ValidPrivkeyTest(unittest.TestCase):
|
||||
"""Tests for letsencrypt.client.crypto_util.valid_privkey."""
|
||||
|
||||
def _call(self, privkey):
|
||||
@classmethod
|
||||
def _call(cls, privkey):
|
||||
from letsencrypt.client.crypto_util import valid_privkey
|
||||
return valid_privkey(privkey)
|
||||
|
||||
|
|
@ -122,7 +126,7 @@ class ValidPrivkeyTest(unittest.TestCase):
|
|||
class MakeSSCertTest(unittest.TestCase):
|
||||
"""Tests for letsencrypt.client.crypto_util.make_ss_cert."""
|
||||
|
||||
def test_it(self):
|
||||
def test_it(self): # pylint: disable=no-self-use
|
||||
from letsencrypt.client.crypto_util import make_ss_cert
|
||||
make_ss_cert(RSA256_KEY, ['example.com', 'www.example.com'])
|
||||
|
||||
|
|
|
|||
|
|
@ -100,26 +100,25 @@ class UniqueFileTest(unittest.TestCase):
|
|||
self.assertEqual(0o700, os.stat(self._call(0o700)[1]).st_mode & 0o777)
|
||||
self.assertEqual(0o100, os.stat(self._call(0o100)[1]).st_mode & 0o777)
|
||||
|
||||
def test_default_not_exists(self):
|
||||
self.assertEqual(self._call()[1], self.default_name)
|
||||
|
||||
def test_default_exists(self):
|
||||
name1 = self._call()[1] # create foo.txt
|
||||
name1 = self._call()[1] # create 0000_foo.txt
|
||||
name2 = self._call()[1]
|
||||
name3 = self._call()[1]
|
||||
|
||||
self.assertNotEqual(name1, name2)
|
||||
basename2 = os.path.basename(name2)
|
||||
self.assertEqual(os.path.dirname(name2), self.root_path)
|
||||
self.assertTrue(basename2.startswith('foo'))
|
||||
self.assertTrue(basename2.endswith('.txt'))
|
||||
|
||||
self.assertNotEqual(name1, name3)
|
||||
self.assertNotEqual(name2, name3)
|
||||
basename3 = os.path.basename(name3)
|
||||
|
||||
self.assertEqual(os.path.dirname(name1), self.root_path)
|
||||
self.assertEqual(os.path.dirname(name2), self.root_path)
|
||||
self.assertEqual(os.path.dirname(name3), self.root_path)
|
||||
self.assertTrue(basename3.startswith('foo'))
|
||||
self.assertTrue(basename3.endswith('.txt'))
|
||||
|
||||
basename1 = os.path.basename(name2)
|
||||
self.assertTrue(basename1.endswith('foo.txt'))
|
||||
basename2 = os.path.basename(name2)
|
||||
self.assertTrue(basename2.endswith('foo.txt'))
|
||||
basename3 = os.path.basename(name3)
|
||||
self.assertTrue(basename3.endswith('foo.txt'))
|
||||
|
||||
|
||||
# https://en.wikipedia.org/wiki/Base64#Examples
|
||||
|
|
@ -141,7 +140,8 @@ B64_URL_UNSAFE_EXAMPLES = {
|
|||
class JOSEB64EncodeTest(unittest.TestCase):
|
||||
"""Tests for letsencrypt.client.le_util.jose_b64encode."""
|
||||
|
||||
def _call(self, data):
|
||||
@classmethod
|
||||
def _call(cls, data):
|
||||
from letsencrypt.client.le_util import jose_b64encode
|
||||
return jose_b64encode(data)
|
||||
|
||||
|
|
@ -160,7 +160,8 @@ class JOSEB64EncodeTest(unittest.TestCase):
|
|||
class JOSEB64DecodeTest(unittest.TestCase):
|
||||
"""Tests for letsencrypt.client.le_util.jose_b64decode."""
|
||||
|
||||
def _call(self, data):
|
||||
@classmethod
|
||||
def _call(cls, data):
|
||||
from letsencrypt.client.le_util import jose_b64decode
|
||||
return jose_b64decode(data)
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import os
|
|||
import sys
|
||||
|
||||
import zope.component
|
||||
import zope.interface
|
||||
|
||||
from letsencrypt.client import CONFIG
|
||||
from letsencrypt.client import client
|
||||
|
|
@ -18,7 +19,7 @@ from letsencrypt.client import revoker
|
|||
from letsencrypt.client.apache import configurator
|
||||
|
||||
|
||||
def main():
|
||||
def main(): # pylint: disable=too-many-statements
|
||||
"""Command line argument parsing and main script execution."""
|
||||
if not os.geteuid() == 0:
|
||||
sys.exit(
|
||||
|
|
@ -41,7 +42,7 @@ def main():
|
|||
help="Revert configuration N number of checkpoints.")
|
||||
parser.add_argument("-B", "--keysize", dest="key_size", type=int,
|
||||
default=CONFIG.RSA_KEY_SIZE, metavar="N",
|
||||
help="RSA key shall be sized N bits. [%(default)s]")
|
||||
help="RSA key shall be sized N bits. [%(default)d]")
|
||||
parser.add_argument("-k", "--revoke", dest="revoke", action="store_true",
|
||||
help="Revoke a certificate.")
|
||||
parser.add_argument("-v", "--view-config-changes",
|
||||
|
|
@ -102,7 +103,7 @@ def main():
|
|||
sys.exit(1)
|
||||
|
||||
# Use the same object if possible
|
||||
if interfaces.IAuthenticator.providedBy(installer):
|
||||
if interfaces.IAuthenticator.providedBy(installer): # pylint: disable=no-member
|
||||
auth = installer
|
||||
else:
|
||||
auth = determine_authenticator()
|
||||
|
|
@ -181,9 +182,7 @@ def get_all_names(installer):
|
|||
def determine_authenticator():
|
||||
"""Returns a valid IAuthenticator."""
|
||||
try:
|
||||
if interfaces.IAuthenticator.implementedBy(
|
||||
configurator.ApacheConfigurator):
|
||||
return configurator.ApacheConfigurator()
|
||||
return configurator.ApacheConfigurator()
|
||||
except errors.LetsEncryptNoInstallationError:
|
||||
logging.info("Unable to determine a way to authenticate the server")
|
||||
|
||||
|
|
@ -191,9 +190,7 @@ def determine_authenticator():
|
|||
def determine_installer():
|
||||
"""Returns a valid installer if one exists."""
|
||||
try:
|
||||
if interfaces.IInstaller.implementedBy(
|
||||
configurator.ApacheConfigurator):
|
||||
return configurator.ApacheConfigurator()
|
||||
return configurator.ApacheConfigurator()
|
||||
except errors.LetsEncryptNoInstallationError:
|
||||
logging.info("Unable to find a way to install the certificate.")
|
||||
|
||||
|
|
|
|||
5
setup.py
5
setup.py
|
|
@ -5,7 +5,6 @@ from setuptools import setup
|
|||
install_requires = [
|
||||
'argparse',
|
||||
'jsonschema',
|
||||
'M2Crypto',
|
||||
'mock',
|
||||
'pycrypto',
|
||||
'python-augeas',
|
||||
|
|
@ -13,6 +12,9 @@ install_requires = [
|
|||
'requests',
|
||||
'zope.component',
|
||||
'zope.interface',
|
||||
# order of items in install_requires DOES matter and M2Crypto has
|
||||
# to go last, see #152
|
||||
'M2Crypto',
|
||||
]
|
||||
|
||||
docs_extras = [
|
||||
|
|
@ -24,6 +26,7 @@ testing_extras = [
|
|||
'nose',
|
||||
'nosexcover',
|
||||
'pylint<1.4', # py2.6 compat, c.f #97
|
||||
'astroid<1.3.0', # py2.6 compat, c.f. #187
|
||||
'tox',
|
||||
]
|
||||
|
||||
|
|
|
|||
2
tox.ini
2
tox.ini
|
|
@ -14,7 +14,7 @@ commands =
|
|||
[testenv:cover]
|
||||
commands =
|
||||
python setup.py dev
|
||||
python setup.py nosetests --with-coverage --cover-min-percentage=59
|
||||
python setup.py nosetests --with-coverage --cover-min-percentage=61
|
||||
|
||||
[testenv:lint]
|
||||
commands =
|
||||
|
|
|
|||
Loading…
Reference in a new issue