mirror of
https://github.com/certbot/certbot.git
synced 2026-08-03 03:41:53 +02:00
Final cleanup for revoker/display
This commit is contained in:
@@ -320,6 +320,7 @@ def init_key(key_size, key_dir):
|
|||||||
|
|
||||||
return le_util.Key(key_filename, key_pem)
|
return le_util.Key(key_filename, key_pem)
|
||||||
|
|
||||||
|
|
||||||
def init_csr(privkey, names, cert_dir):
|
def init_csr(privkey, names, cert_dir):
|
||||||
"""Initialize a CSR with the given private key.
|
"""Initialize a CSR with the given private key.
|
||||||
|
|
||||||
@@ -344,6 +345,7 @@ def init_csr(privkey, names, cert_dir):
|
|||||||
|
|
||||||
return le_util.CSR(csr_filename, csr_der, "der")
|
return le_util.CSR(csr_filename, csr_der, "der")
|
||||||
|
|
||||||
|
|
||||||
# This should be controlled by commandline parameters
|
# This should be controlled by commandline parameters
|
||||||
def determine_authenticator(all_auths):
|
def determine_authenticator(all_auths):
|
||||||
"""Returns a valid IAuthenticator.
|
"""Returns a valid IAuthenticator.
|
||||||
|
|||||||
@@ -161,4 +161,3 @@ def make_ss_cert(key_str, domains, not_before=None,
|
|||||||
assert m2_cert.verify()
|
assert m2_cert.verify()
|
||||||
# print check_purpose(,0
|
# print check_purpose(,0
|
||||||
return m2_cert.as_pem()
|
return m2_cert.as_pem()
|
||||||
|
|
||||||
|
|||||||
@@ -109,12 +109,14 @@ def _choose_names_manually():
|
|||||||
def success_installation(domains):
|
def success_installation(domains):
|
||||||
"""Display a box confirming the installation of HTTPS.
|
"""Display a box confirming the installation of HTTPS.
|
||||||
|
|
||||||
|
.. todo:: This should be centered on the screen
|
||||||
|
|
||||||
:param list domains: domain names which were enabled
|
:param list domains: domain names which were enabled
|
||||||
|
|
||||||
"""
|
"""
|
||||||
util(interfaces.IDisplay).notification(
|
util(interfaces.IDisplay).notification(
|
||||||
"Congratulations! You have successfully enabled "
|
"Congratulations! You have successfully enabled "
|
||||||
"%s!" % _gen_https_names(domains), pause=True)
|
"%s!" % _gen_https_names(domains), pause=False)
|
||||||
|
|
||||||
|
|
||||||
def _gen_https_names(domains):
|
def _gen_https_names(domains):
|
||||||
|
|||||||
@@ -143,6 +143,7 @@ class NcursesDisplay(object):
|
|||||||
return self.dialog.checklist(
|
return self.dialog.checklist(
|
||||||
message, width=self.width, height=self.height, choices=choices)
|
message, width=self.width, height=self.height, choices=choices)
|
||||||
|
|
||||||
|
|
||||||
class FileDisplay(object):
|
class FileDisplay(object):
|
||||||
"""File-based display."""
|
"""File-based display."""
|
||||||
|
|
||||||
@@ -305,7 +306,6 @@ class FileDisplay(object):
|
|||||||
# Transform indices to appropriate tags
|
# Transform indices to appropriate tags
|
||||||
return [tags[index-1] for index in indices]
|
return [tags[index-1] for index in indices]
|
||||||
|
|
||||||
|
|
||||||
def _print_menu(self, message, choices):
|
def _print_menu(self, message, choices):
|
||||||
"""Print a menu on the screen.
|
"""Print a menu on the screen.
|
||||||
|
|
||||||
@@ -400,6 +400,7 @@ def separate_list_input(input_):
|
|||||||
# Each string is naturally unicode, this causes problems with M2Crypto SANs
|
# Each string is naturally unicode, this causes problems with M2Crypto SANs
|
||||||
return [str(string) for string in no_commas.split()]
|
return [str(string) for string in no_commas.split()]
|
||||||
|
|
||||||
|
|
||||||
def _parens_around_char(label):
|
def _parens_around_char(label):
|
||||||
"""Place parens around first character of label.
|
"""Place parens around first character of label.
|
||||||
|
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ class IAuthenticator(zope.interface.Interface):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class IConfig(zope.interface.Interface):
|
class IConfig(zope.interface.Interface):
|
||||||
"""Let's Encrypt user-supplied configuration.
|
"""Let's Encrypt user-supplied configuration.
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ Key = collections.namedtuple("Key", "file pem")
|
|||||||
# Note: form is the type of data, "pem" or "der"
|
# Note: form is the type of data, "pem" or "der"
|
||||||
CSR = collections.namedtuple("CSR", "file data form")
|
CSR = collections.namedtuple("CSR", "file data form")
|
||||||
|
|
||||||
|
|
||||||
def make_or_verify_dir(directory, mode=0o755, uid=0):
|
def make_or_verify_dir(directory, mode=0o755, uid=0):
|
||||||
"""Make sure directory exists with proper permissions.
|
"""Make sure directory exists with proper permissions.
|
||||||
|
|
||||||
|
|||||||
@@ -40,6 +40,8 @@ class Revoker(object):
|
|||||||
:ivar config: Configuration.
|
:ivar config: Configuration.
|
||||||
:type config: :class:`~letsencrypt.client.interfaces.IConfig`
|
:type config: :class:`~letsencrypt.client.interfaces.IConfig`
|
||||||
|
|
||||||
|
:ivar bool no_confirm: Whether or not to ask for confirmation for revocation
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def __init__(self, installer, config, no_confirm=False):
|
def __init__(self, installer, config, no_confirm=False):
|
||||||
self.network = network.Network(config.server)
|
self.network = network.Network(config.server)
|
||||||
@@ -62,25 +64,31 @@ class Revoker(object):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
certs = []
|
certs = []
|
||||||
|
clean_pem = Crypto.PublicKey.RSA.importKey(authkey.pem).exportKey("PEM")
|
||||||
with open(self.list_path, "rb") as csvfile:
|
with open(self.list_path, "rb") as csvfile:
|
||||||
csvreader = csv.reader(csvfile)
|
csvreader = csv.reader(csvfile)
|
||||||
for row in csvreader:
|
for row in csvreader:
|
||||||
# idx, cert, key
|
# idx, cert, key
|
||||||
# Add all keys that match to marked list
|
# Add all keys that match to marked list
|
||||||
# TODO: This doesn't account for padding in the file that might
|
|
||||||
# differ. This should only consider the key material.
|
|
||||||
# Note: The key can be different than the pub key found in the
|
# Note: The key can be different than the pub key found in the
|
||||||
# certificate.
|
# certificate.
|
||||||
_, b_k = self._row_to_backup(row)
|
_, b_k = self._row_to_backup(row)
|
||||||
if authkey.pem == open(b_k).read():
|
if clean_pem == Crypto.PublicKey.RSA.importKey(
|
||||||
|
open(b_k).read()).exportKey("PEM"):
|
||||||
certs.append(Cert.fromrow(row, self.config.cert_key_backup))
|
certs.append(Cert.fromrow(row, self.config.cert_key_backup))
|
||||||
|
|
||||||
if certs:
|
if certs:
|
||||||
self._safe_revoke(certs)
|
self._safe_revoke(certs)
|
||||||
|
else:
|
||||||
|
logging.info("No certificates using the authorized key were found.")
|
||||||
|
|
||||||
def revoke_from_cert(self, cert_path):
|
def revoke_from_cert(self, cert_path):
|
||||||
"""Revoke a certificate by specifying a file path.
|
"""Revoke a certificate by specifying a file path.
|
||||||
|
|
||||||
|
.. todo:: Add the ability to revoke the certificate even if the cert
|
||||||
|
is not stored locally. A path to the auth key will need to be
|
||||||
|
attained from the user.
|
||||||
|
|
||||||
:param str cert_path: path to ACME certificate in pem form
|
:param str cert_path: path to ACME certificate in pem form
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@@ -94,6 +102,9 @@ class Revoker(object):
|
|||||||
|
|
||||||
if cert == cert_to_revoke:
|
if cert == cert_to_revoke:
|
||||||
self._safe_revoke([cert])
|
self._safe_revoke([cert])
|
||||||
|
return
|
||||||
|
|
||||||
|
logging.info("Associated ACME certificate was not found.")
|
||||||
|
|
||||||
def revoke_from_menu(self):
|
def revoke_from_menu(self):
|
||||||
"""List trusted Let's Encrypt certificates."""
|
"""List trusted Let's Encrypt certificates."""
|
||||||
@@ -128,6 +139,9 @@ class Revoker(object):
|
|||||||
Namely, additional certs/keys may exist. There should never be any
|
Namely, additional certs/keys may exist. There should never be any
|
||||||
certs/keys in the LIST that don't exist in the directory however.
|
certs/keys in the LIST that don't exist in the directory however.
|
||||||
|
|
||||||
|
:param dict csha1_vhlist: map from cert sha1 fingerprints to a list
|
||||||
|
of it's installed location paths.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
certs = []
|
certs = []
|
||||||
with open(self.list_path, "rb") as csvfile:
|
with open(self.list_path, "rb") as csvfile:
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ class DetermineAuthenticatorTest(unittest.TestCase):
|
|||||||
|
|
||||||
@mock.patch("letsencrypt.client.client.logging")
|
@mock.patch("letsencrypt.client.client.logging")
|
||||||
@mock.patch("letsencrypt.client.client.ops.choose_authenticator")
|
@mock.patch("letsencrypt.client.client.ops.choose_authenticator")
|
||||||
def test_misconfigured(self, mock_choose, mock_log):
|
def test_misconfigured(self, mock_choose, mock_log): # pylint: disable=unused-argument
|
||||||
self.mock_apache.side_effect = errors.LetsEncryptMisconfigurationError
|
self.mock_apache.side_effect = errors.LetsEncryptMisconfigurationError
|
||||||
mock_choose.return_value = self.mock_apache
|
mock_choose.return_value = self.mock_apache
|
||||||
|
|
||||||
@@ -61,6 +61,7 @@ class DetermineAuthenticatorTest(unittest.TestCase):
|
|||||||
self._call,
|
self._call,
|
||||||
[("desc", self.mock_apache, "1", "2", "3", "4", "5")])
|
[("desc", self.mock_apache, "1", "2", "3", "4", "5")])
|
||||||
|
|
||||||
|
|
||||||
class RollbackTest(unittest.TestCase):
|
class RollbackTest(unittest.TestCase):
|
||||||
"""Test the rollback function."""
|
"""Test the rollback function."""
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ class ChooseCertsTest(unittest.TestCase):
|
|||||||
self.assertTrue(self.certs[choice] == self.cert1)
|
self.assertTrue(self.certs[choice] == self.cert1)
|
||||||
self.assertEqual(mock_util().notification.call_count, 1)
|
self.assertEqual(mock_util().notification.call_count, 1)
|
||||||
|
|
||||||
|
|
||||||
class SuccessRevocationTest(unittest.TestCase):
|
class SuccessRevocationTest(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
from letsencrypt.client.revoker import Cert
|
from letsencrypt.client.revoker import Cert
|
||||||
|
|||||||
@@ -67,7 +67,6 @@ class NcursesDisplayTest(DisplayT):
|
|||||||
|
|
||||||
ret = self.displayer.menu("Message", self.choices)
|
ret = self.displayer.menu("Message", self.choices)
|
||||||
|
|
||||||
|
|
||||||
mock_menu.assert_called_with(
|
mock_menu.assert_called_with(
|
||||||
"Message", choices=self.choices, ok_label="OK",
|
"Message", choices=self.choices, ok_label="OK",
|
||||||
cancel_label="Cancel",
|
cancel_label="Cancel",
|
||||||
@@ -82,7 +81,6 @@ class NcursesDisplayTest(DisplayT):
|
|||||||
|
|
||||||
ret = self.displayer.menu("Message", self.tags, help_label="More Info")
|
ret = self.displayer.menu("Message", self.tags, help_label="More Info")
|
||||||
|
|
||||||
|
|
||||||
mock_menu.assert_called_with(
|
mock_menu.assert_called_with(
|
||||||
"Message", choices=self.tags_choices, ok_label="OK",
|
"Message", choices=self.tags_choices, ok_label="OK",
|
||||||
cancel_label="Cancel",
|
cancel_label="Cancel",
|
||||||
|
|||||||
@@ -77,6 +77,26 @@ class RevokerTest(RevokerBase):
|
|||||||
|
|
||||||
self.assertEqual(mock_net.call_count, 2)
|
self.assertEqual(mock_net.call_count, 2)
|
||||||
|
|
||||||
|
@mock.patch("letsencrypt.client.revoker.network."
|
||||||
|
"Network.send_and_receive_expected")
|
||||||
|
@mock.patch("letsencrypt.client.revoker.revocation")
|
||||||
|
def test_revoke_by_wrong_key(self, mock_display, mock_net):
|
||||||
|
mock_display().confirm_revocation.return_value = True
|
||||||
|
|
||||||
|
key_path = pkg_resources.resource_filename(
|
||||||
|
"letsencrypt.client.tests", os.path.join(
|
||||||
|
"testdata", "rsa256_key.pem"))
|
||||||
|
|
||||||
|
wrong_key = le_util.Key(key_path, open(key_path).read())
|
||||||
|
self.revoker.revoke_from_key(wrong_key)
|
||||||
|
|
||||||
|
# Nothing was removed
|
||||||
|
self.assertEqual(len(self._get_rows()), 2)
|
||||||
|
# No revocation went through
|
||||||
|
self.assertEqual(mock_net.call_count, 0)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@mock.patch("letsencrypt.client.revoker.network."
|
@mock.patch("letsencrypt.client.revoker.network."
|
||||||
"Network.send_and_receive_expected")
|
"Network.send_and_receive_expected")
|
||||||
@mock.patch("letsencrypt.client.revoker.revocation")
|
@mock.patch("letsencrypt.client.revoker.revocation")
|
||||||
@@ -95,6 +115,26 @@ class RevokerTest(RevokerBase):
|
|||||||
|
|
||||||
self.assertEqual(mock_net.call_count, 1)
|
self.assertEqual(mock_net.call_count, 1)
|
||||||
|
|
||||||
|
@mock.patch("letsencrypt.client.revoker.network."
|
||||||
|
"Network.send_and_receive_expected")
|
||||||
|
@mock.patch("letsencrypt.client.revoker.revocation")
|
||||||
|
def test_revoke_by_cert_not_found(self, mock_display, mock_net):
|
||||||
|
mock_display().confirm_revocation.return_value = True
|
||||||
|
|
||||||
|
self.revoker.revoke_from_cert(self.paths[0])
|
||||||
|
self.revoker.revoke_from_cert(self.paths[0])
|
||||||
|
|
||||||
|
row0 = self.certs[0].get_row()
|
||||||
|
row1 = self.certs[1].get_row()
|
||||||
|
|
||||||
|
# Same check as last time... just reversed.
|
||||||
|
self.assertEqual(self._get_rows(), [row1])
|
||||||
|
|
||||||
|
self.assertTrue(self._backups_exist(row1))
|
||||||
|
self.assertFalse(self._backups_exist(row0))
|
||||||
|
|
||||||
|
self.assertEqual(mock_net.call_count, 1)
|
||||||
|
|
||||||
@mock.patch("letsencrypt.client.revoker.network."
|
@mock.patch("letsencrypt.client.revoker.network."
|
||||||
"Network.send_and_receive_expected")
|
"Network.send_and_receive_expected")
|
||||||
@mock.patch("letsencrypt.client.revoker.revocation")
|
@mock.patch("letsencrypt.client.revoker.revocation")
|
||||||
@@ -228,6 +268,7 @@ class RevokerInstallerTest(RevokerBase):
|
|||||||
# pylint: disable=protected-access
|
# pylint: disable=protected-access
|
||||||
self.assertEqual(revoker._get_installed_locations(), {})
|
self.assertEqual(revoker._get_installed_locations(), {})
|
||||||
|
|
||||||
|
|
||||||
class RevokerClassMethodsTest(RevokerBase):
|
class RevokerClassMethodsTest(RevokerBase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(RevokerClassMethodsTest, self).setUp()
|
super(RevokerClassMethodsTest, self).setUp()
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ from letsencrypt.client import client
|
|||||||
from letsencrypt.client import interfaces
|
from letsencrypt.client import interfaces
|
||||||
from letsencrypt.client import le_util
|
from letsencrypt.client import le_util
|
||||||
from letsencrypt.client import log
|
from letsencrypt.client import log
|
||||||
from letsencrypt.client import standalone_authenticator
|
from letsencrypt.client import standalone_authenticator as standalone
|
||||||
from letsencrypt.client.apache import configurator
|
from letsencrypt.client.apache import configurator
|
||||||
from letsencrypt.client.display import util as display_util
|
from letsencrypt.client.display import util as display_util
|
||||||
from letsencrypt.client.display import ops
|
from letsencrypt.client.display import ops
|
||||||
@@ -124,8 +124,7 @@ def main(): # pylint: disable=too-many-branches
|
|||||||
client.view_config_changes(config)
|
client.view_config_changes(config)
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
# TODO: if revoke, rev_cert...
|
if args.revoke or args.rev_cert or args.rev_key:
|
||||||
if args.revoke:
|
|
||||||
client.revoke(config, args.no_confirm, args.rev_cert, args.rev_key)
|
client.revoke(config, args.no_confirm, args.rev_cert, args.rev_key)
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
@@ -139,8 +138,7 @@ def main(): # pylint: disable=too-many-branches
|
|||||||
# list of (Description, Known Authenticator classes, init arguments)
|
# list of (Description, Known Authenticator classes, init arguments)
|
||||||
all_auths = [
|
all_auths = [
|
||||||
("Apache Web Server", configurator.ApacheConfigurator, config),
|
("Apache Web Server", configurator.ApacheConfigurator, config),
|
||||||
("Standalone Authenticator",
|
("Standalone Authenticator", standalone.StandaloneAuthenticator),
|
||||||
standalone_authenticator.StandaloneAuthenticator),
|
|
||||||
]
|
]
|
||||||
auth = client.determine_authenticator(all_auths)
|
auth = client.determine_authenticator(all_auths)
|
||||||
if auth is None:
|
if auth is None:
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ setenv =
|
|||||||
basepython = python2.7
|
basepython = python2.7
|
||||||
commands =
|
commands =
|
||||||
pip install -e .[testing]
|
pip install -e .[testing]
|
||||||
python setup.py nosetests --with-coverage --cover-min-percentage=83
|
python setup.py nosetests --with-coverage --cover-min-percentage=84
|
||||||
|
|
||||||
[testenv:lint]
|
[testenv:lint]
|
||||||
# recent versions of pylint do not support Python 2.6 (#97, #187)
|
# recent versions of pylint do not support Python 2.6 (#97, #187)
|
||||||
|
|||||||
Reference in New Issue
Block a user