mirror of
https://github.com/certbot/certbot.git
synced 2026-06-05 14:54:24 -04:00
Revisions through running/testing
This commit is contained in:
parent
08fc0852d7
commit
6c8eb8be17
14 changed files with 257 additions and 86 deletions
|
|
@ -942,9 +942,15 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
|
||||
return tuple([int(i) for i in matches[0].split('.')])
|
||||
|
||||
def __str__(self):
|
||||
return "Apache version %s" % ".".join(self.get_version())
|
||||
|
||||
def more_info(self):
|
||||
"""Human-readable string to help understand the module"""
|
||||
return (
|
||||
"Configures Apache to authenticate and install HTTPS.{0}"
|
||||
"Server root: {root}{0}"
|
||||
"Version: {version}".format(
|
||||
os.linesep, root=self.parser.loc["root"],
|
||||
version=".".join(str(i) for i in self.version))
|
||||
)
|
||||
|
||||
###########################################################################
|
||||
# Challenges Section
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ from letsencrypt.client import le_util
|
|||
from letsencrypt.client import network
|
||||
from letsencrypt.client import reverter
|
||||
from letsencrypt.client import revoker
|
||||
from letsencrypt.client import standalone_authenticator
|
||||
|
||||
from letsencrypt.client.apache import configurator
|
||||
from letsencrypt.client.display import ops
|
||||
|
|
@ -102,7 +103,8 @@ class Client(object):
|
|||
cert_file, chain_file = self.save_certificate(
|
||||
certificate_msg, self.config.cert_path, self.config.chain_path)
|
||||
|
||||
revoker.Revoker.store_cert_key(cert_file, self.authkey.file, False)
|
||||
revoker.Revoker.store_cert_key(
|
||||
cert_file, self.authkey.file, self.config)
|
||||
|
||||
return cert_file, chain_file
|
||||
|
||||
|
|
@ -350,16 +352,53 @@ def determine_authenticator(config):
|
|||
:param config: Configuration.
|
||||
:type config: :class:`letsencrypt.client.interfaces.IConfig`
|
||||
|
||||
:returns: Valid Authenticator object or None
|
||||
|
||||
"""
|
||||
auths = []
|
||||
try:
|
||||
auths.append(configurator.ApacheConfigurator(config))
|
||||
except errors.LetsEncryptNoInstallationError:
|
||||
logging.info("Unable to determine a way to authenticate the server")
|
||||
if len(auths) > 1:
|
||||
return ops.choose_authenticator(auths)
|
||||
elif len(auths) == 1:
|
||||
return auths[0]
|
||||
# list of (Description, Known Authenticator classes, init arguments)
|
||||
all_auths = [
|
||||
("Apache Web Server", configurator.ApacheConfigurator, config),
|
||||
("Standalone Authenticator",
|
||||
standalone_authenticator.StandaloneAuthenticator),
|
||||
]
|
||||
|
||||
# Available Authenticator objects
|
||||
avail_auths = []
|
||||
# Error messages for misconfigured authenticators
|
||||
errs = {}
|
||||
|
||||
for pot_auth in all_auths:
|
||||
try:
|
||||
# I do not think this a great solution but haven't come up with
|
||||
# anything better yet...
|
||||
if len(pot_auth) == 2:
|
||||
# pylint: disable=no-value-for-parameter
|
||||
avail_auths.append((pot_auth[0], pot_auth[1]()))
|
||||
elif len(pot_auth) == 3:
|
||||
avail_auths.append((pot_auth[0], pot_auth[1](pot_auth[2])))
|
||||
else:
|
||||
errors.LetsEncryptClientError(
|
||||
"IAuthenticator: Number of parameters not supported")
|
||||
except errors.LetsEncryptMisconfigurationError as err:
|
||||
errs[pot_auth[1]] = err
|
||||
avail_auths.append((pot_auth[0], pot_auth[1]))
|
||||
except errors.LetsEncryptNoInstallationError:
|
||||
pass
|
||||
|
||||
if len(avail_auths) > 1:
|
||||
auth = ops.choose_authenticator(avail_auths, errs)
|
||||
elif len(avail_auths) == 1:
|
||||
auth = avail_auths[0]
|
||||
else:
|
||||
auth = None
|
||||
|
||||
if auth in errs:
|
||||
logging.error("Please fix the configuration for the Authenticator. "
|
||||
"The following error message was received: "
|
||||
"%s", errs[auth])
|
||||
sys.exit(1)
|
||||
|
||||
return auth
|
||||
|
||||
|
||||
def determine_installer(config):
|
||||
|
|
|
|||
|
|
@ -11,24 +11,38 @@ from letsencrypt.client.display import util as display_util
|
|||
util = zope.component.getUtility # pylint: disable=invalid-name
|
||||
|
||||
|
||||
def choose_authenticator(auths):
|
||||
def choose_authenticator(auths, errs):
|
||||
"""Allow the user to choose their authenticator.
|
||||
|
||||
:param list auths: Where each is a
|
||||
:class:`letsencrypt.client.interfaces.IAuthenticator` object
|
||||
:param list auths: Where each is a tuple of the form
|
||||
('description', 'IAuthenticator') where IAuthenticator is a
|
||||
:class:`letsencrypt.client.interfaces.IAuthenticator` object or class
|
||||
:param dict errs: Mapping IAuthenticator objects to error messages
|
||||
|
||||
:returns: Authenticator selected
|
||||
:rtype: :class:`letsencrypt.client.interfaces.IAuthenticator`
|
||||
|
||||
"""
|
||||
code, index = util(interfaces.IDisplay).menu(
|
||||
"How would you like to authenticate with the Let's Encrypt CA?",
|
||||
[str(auth) for auth in auths])
|
||||
descs = [auth[0] for auth in auths]
|
||||
iauths = [auth[1] for auth in auths]
|
||||
|
||||
while True:
|
||||
code, index = util(interfaces.IDisplay).menu(
|
||||
"How would you like to authenticate with the Let's Encrypt CA?",
|
||||
descs, help_label="More Info")
|
||||
|
||||
if code == display_util.OK:
|
||||
return iauths[index]
|
||||
elif code == display_util.HELP:
|
||||
if iauths[index] in errs:
|
||||
msg = "Reported Error: %s" % errs[iauths[index]]
|
||||
else:
|
||||
msg = iauths[index].more_info()
|
||||
util(interfaces.IDisplay).notification(
|
||||
msg, height=display_util.HEIGHT)
|
||||
else:
|
||||
sys.exit(0)
|
||||
|
||||
if code == display_util.OK:
|
||||
return auths[index]
|
||||
else:
|
||||
sys.exit(0)
|
||||
|
||||
def choose_names(installer):
|
||||
"""Display screen to select domains to validate.
|
||||
|
|
@ -116,7 +130,7 @@ def _gen_https_names(domains):
|
|||
return "https://{dom[0]} and https://{dom[1]}".format(dom=domains)
|
||||
elif len(domains) > 2:
|
||||
return "{0}{1}{2}".format(
|
||||
", ".join("https://" + dom for dom in domains[:-1]),
|
||||
", ".join("https://%s" % dom for dom in domains[:-1]),
|
||||
", and https://",
|
||||
domains[-1])
|
||||
|
||||
|
|
|
|||
|
|
@ -20,10 +20,8 @@ def choose_certs(certs):
|
|||
"""
|
||||
while True:
|
||||
code, selection = _display_certs(certs)
|
||||
|
||||
if code == display_util.OK:
|
||||
if confirm_revocation(certs[selection]):
|
||||
return selection
|
||||
return selection
|
||||
elif code == display_util.HELP:
|
||||
more_info_cert(certs[selection])
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -93,10 +93,10 @@ class NcursesDisplay(object):
|
|||
help_button=help_button, help_label=help_label,
|
||||
width=self.width, height=self.height)
|
||||
|
||||
if code == OK:
|
||||
return code, int(tag) - 1
|
||||
if code == CANCEL:
|
||||
return code, -1
|
||||
|
||||
return code, -1
|
||||
return code, int(tag) - 1
|
||||
|
||||
def input(self, message):
|
||||
"""Display an input box to the user.
|
||||
|
|
@ -108,7 +108,7 @@ class NcursesDisplay(object):
|
|||
`string` - input entered by the user
|
||||
|
||||
"""
|
||||
return self.dialog.inputbox(message)
|
||||
return self.dialog.inputbox(message, width=self.width)
|
||||
|
||||
def yesno(self, message, yes_label="Yes", no_label="No"):
|
||||
"""Display a Yes/No dialog box
|
||||
|
|
@ -232,12 +232,19 @@ class FileDisplay(object):
|
|||
self.outfile.write("{0}{frame}{msg}{0}{frame}".format(
|
||||
os.linesep, frame=side_frame, msg=message))
|
||||
|
||||
ans = raw_input("{yes}/{no}: ".format(
|
||||
yes=_parens_around_char(yes_label),
|
||||
no=_parens_around_char(no_label)))
|
||||
while True:
|
||||
ans = raw_input("{yes}/{no}: ".format(
|
||||
yes=_parens_around_char(yes_label),
|
||||
no=_parens_around_char(no_label)))
|
||||
|
||||
return (ans.startswith(yes_label[0].lower()) or
|
||||
ans.startswith(yes_label[0].upper()))
|
||||
# Couldn't get pylint indentation right with elif
|
||||
# elif doesn't matter in this situation
|
||||
if (ans.startswith(yes_label[0].lower()) or
|
||||
ans.startswith(yes_label[0].upper())):
|
||||
return True
|
||||
if (ans.startswith(no_label[0].lower()) or
|
||||
ans.startswith(no_label[0].upper())):
|
||||
return False
|
||||
|
||||
def checklist(self, message, tags):
|
||||
"""Display a checklist.
|
||||
|
|
@ -275,7 +282,7 @@ class FileDisplay(object):
|
|||
:param list indices: input
|
||||
:param list tags: Original tags of the checklist
|
||||
|
||||
:returns: tags the user selected
|
||||
:returns: valid tags the user selected
|
||||
:rtype: :class:`list` of :class:`str`
|
||||
|
||||
"""
|
||||
|
|
@ -387,7 +394,8 @@ def separate_list_input(input_):
|
|||
|
||||
"""
|
||||
no_commas = input_.replace(",", " ")
|
||||
return [string for string in no_commas.split()]
|
||||
# Each string is naturally unicode, this causes problems with M2Crypto SANs
|
||||
return [str(string) for string in no_commas.split()]
|
||||
|
||||
def _parens_around_char(label):
|
||||
"""Place parens around first character of label.
|
||||
|
|
|
|||
|
|
@ -57,6 +57,13 @@ class IAuthenticator(zope.interface.Interface):
|
|||
|
||||
"""
|
||||
|
||||
def more_info(self):
|
||||
"""Human-readable string to help the user.
|
||||
|
||||
Should describe the steps taken and any relevant info to help the user
|
||||
decide which Authenticator to use.
|
||||
|
||||
"""
|
||||
|
||||
class IConfig(zope.interface.Interface):
|
||||
"""Let's Encrypt user-supplied configuration.
|
||||
|
|
|
|||
|
|
@ -105,19 +105,19 @@ class Revoker(object):
|
|||
if certs:
|
||||
selection = revocation.choose_certs(certs)
|
||||
|
||||
self._safe_revoke([certs[selection]])
|
||||
# This is safer than using remove as Revoker.Certs only check
|
||||
# the DER value of the cert. There could potentially be multiple
|
||||
# backup certs with the same value.
|
||||
del certs[selection]
|
||||
revoked_certs = self._safe_revoke([certs[selection]])
|
||||
# Since we are currently only revoking one cert at a time...
|
||||
if revoked_certs:
|
||||
# This is safer than using remove as Revoker.Certs only
|
||||
# check the DER value of the cert. There could potentially
|
||||
# be multiple backup certs with the same value.
|
||||
del certs[selection]
|
||||
else:
|
||||
logging.info(
|
||||
"There are not any trusted Let's Encrypt "
|
||||
"certificates for this server.")
|
||||
return
|
||||
|
||||
|
||||
|
||||
def _populate_saved_certs(self, csha1_vhlist):
|
||||
# pylint: disable=no-self-use
|
||||
"""Populate a list of all the saved certs.
|
||||
|
|
@ -174,6 +174,9 @@ class Revoker(object):
|
|||
:param certs: certs intended to be revoked
|
||||
:type certs: :class:`list` of :class:`letsencrypt.client.revoker.Cert`
|
||||
|
||||
:returns: certs successfully revoked
|
||||
:rtype: :class:`list` of :class:`letsencrypt.client.revoker.Cert`
|
||||
|
||||
"""
|
||||
success_list = []
|
||||
try:
|
||||
|
|
@ -192,6 +195,8 @@ class Revoker(object):
|
|||
if success_list:
|
||||
self._remove_certs_keys(success_list)
|
||||
|
||||
return success_list
|
||||
|
||||
def _acme_revoke(self, cert):
|
||||
"""Revoke the certificate with the ACME server.
|
||||
|
||||
|
|
@ -290,9 +295,9 @@ class Revoker(object):
|
|||
cls._catalog_files(
|
||||
config.cert_key_backup, cert_path, key_path, list_path)
|
||||
|
||||
|
||||
@classmethod
|
||||
def _catalog_files(cls, backup_dir, cert_path, key_path, list_path):
|
||||
idx = 0
|
||||
if os.path.isfile(list_path):
|
||||
with open(list_path, "r+b") as csvfile:
|
||||
csvreader = csv.reader(csvfile)
|
||||
|
|
@ -309,8 +314,8 @@ class Revoker(object):
|
|||
with open(list_path, "wb") as csvfile:
|
||||
csvwriter = csv.writer(csvfile)
|
||||
# You must move the files before appending the row
|
||||
cls._copy_files(backup_dir, "0", cert_path, key_path)
|
||||
csvwriter.writerow(["0", cert_path, key_path])
|
||||
cls._copy_files(backup_dir, idx, cert_path, key_path)
|
||||
csvwriter.writerow([str(idx), cert_path, key_path])
|
||||
|
||||
@classmethod
|
||||
def _copy_files(cls, backup_dir, idx, cert_path, key_path):
|
||||
|
|
@ -481,8 +486,21 @@ class Cert(object):
|
|||
text.append("Not Before: %s" % str(self.get_not_before()))
|
||||
text.append("Not After: %s" % str(self.get_not_after()))
|
||||
text.append("Serial Number: %s" % self.get_serial())
|
||||
text.append("SHA1: %s" % self.get_fingerprint())
|
||||
text.append("SHA1: %s%s" % (self.get_fingerprint(), os.linesep))
|
||||
text.append("Installed: %s" % self.get_installed_msg())
|
||||
|
||||
if self.orig is not None:
|
||||
if self.orig.status == "":
|
||||
text.append("Path: %s" % self.orig.path)
|
||||
else:
|
||||
text.append("Orig Path: %s (%s)" % self.orig)
|
||||
if self.orig_key is not None:
|
||||
if self.orig_key.status == "":
|
||||
text.append("Auth Key Path: %s" % self.orig_key.path)
|
||||
else:
|
||||
text.append("Orig Auth Key Path: %s (%s)" % self.orig_key)
|
||||
|
||||
text.append("")
|
||||
return os.linesep.join(text)
|
||||
|
||||
def pretty_print(self):
|
||||
|
|
|
|||
|
|
@ -149,14 +149,14 @@ class StandaloneAuthenticator(object):
|
|||
if self.subproc_state == "ready":
|
||||
return True
|
||||
elif self.subproc_state == "inuse":
|
||||
display.generic_notification(
|
||||
display.notification(
|
||||
"Could not bind TCP port {0} because it is already in "
|
||||
"use by another process on this system (such as a web "
|
||||
"server). Please stop the program in question and then "
|
||||
"try again.".format(port))
|
||||
return False
|
||||
elif self.subproc_state == "cantbind":
|
||||
display.generic_notification(
|
||||
display.notification(
|
||||
"Could not bind TCP port {0} because you don't have "
|
||||
"the appropriate permissions (for example, you "
|
||||
"aren't running this program as "
|
||||
|
|
@ -164,7 +164,7 @@ class StandaloneAuthenticator(object):
|
|||
return False
|
||||
time.sleep(0.1)
|
||||
|
||||
display.generic_notification(
|
||||
display.notification(
|
||||
"Subprocess unexpectedly timed out while trying to bind TCP "
|
||||
"port {0}.".format(port))
|
||||
|
||||
|
|
@ -291,7 +291,7 @@ class StandaloneAuthenticator(object):
|
|||
if listeners:
|
||||
pid, name = listeners[0].split("/")
|
||||
display = zope.component.getUtility(interfaces.IDisplay)
|
||||
display.generic_notification(
|
||||
display.notification(
|
||||
"The program {0} (process ID {1}) is already listening "
|
||||
"on TCP port {2}. This will prevent us from binding to "
|
||||
"that port. Please stop the {0} program temporarily "
|
||||
|
|
@ -406,3 +406,12 @@ class StandaloneAuthenticator(object):
|
|||
# TODO: restore original signal handlers in parent process
|
||||
# by resetting their actions to SIG_DFL
|
||||
# print "TCP listener subprocess has been told to shut down"
|
||||
|
||||
def more_info(self): # pylint: disable=no-self-use
|
||||
"""Human-readable string that describes the Authenticator."""
|
||||
return ("The Standalone Authenticator uses PyOpenSSL to listen "
|
||||
"on port 443 and perform DVSNI challenges. Once a certificate"
|
||||
"is attained, it will be saved in the "
|
||||
"(TODO) current working directory.{0}{0}"
|
||||
"Port 443 must be open in order to use the "
|
||||
"Standalone Authenticator.")
|
||||
|
|
|
|||
|
|
@ -12,25 +12,49 @@ class ChooseAuthenticatorTest(unittest.TestCase):
|
|||
"""Test choose_authenticator function."""
|
||||
def setUp(self):
|
||||
zope.component.provideUtility(display_util.FileDisplay(sys.stdout))
|
||||
self.mock_apache = mock.Mock()
|
||||
self.mock_stand = mock.Mock()
|
||||
self.mock_apache().more_info.return_value = "Apache Info"
|
||||
self.mock_stand().more_info.return_value = "Standalone Info"
|
||||
|
||||
self.auths = [
|
||||
("Apache Tag", self.mock_apache),
|
||||
("Standalone Tag", self.mock_stand)
|
||||
]
|
||||
|
||||
self.errs = {self.mock_apache: "This is an error message."}
|
||||
|
||||
@classmethod
|
||||
def _call(cls, auths):
|
||||
def _call(cls, auths, errs):
|
||||
from letsencrypt.client.display.ops import choose_authenticator
|
||||
return choose_authenticator(auths)
|
||||
return choose_authenticator(auths, errs)
|
||||
|
||||
@mock.patch("letsencrypt.client.display.ops.util")
|
||||
def test_successful_choice(self, mock_util):
|
||||
mock_util().menu.return_value = (display_util.OK, 0)
|
||||
|
||||
ret = self._call(["authenticator1", "auth2"])
|
||||
ret = self._call(self.auths, {})
|
||||
|
||||
self.assertEqual(ret, "authenticator1")
|
||||
self.assertEqual(ret, self.mock_apache)
|
||||
|
||||
@mock.patch("letsencrypt.client.display.ops.util")
|
||||
def test_more_info(self, mock_util):
|
||||
mock_util().menu.side_effect = [
|
||||
(display_util.HELP, 0),
|
||||
(display_util.HELP, 1),
|
||||
(display_util.OK, 1),
|
||||
]
|
||||
|
||||
ret = self._call(self.auths, self.errs)
|
||||
|
||||
self.assertEqual(mock_util().notification.call_count, 2)
|
||||
self.assertEqual(ret, self.mock_stand)
|
||||
|
||||
@mock.patch("letsencrypt.client.display.ops.util")
|
||||
def test_no_choice(self, mock_util):
|
||||
mock_util().menu.return_value = (display_util.CANCEL, 0)
|
||||
|
||||
self.assertRaises(SystemExit, self._call, ["authenticator1"])
|
||||
self.assertRaises(SystemExit, self._call, self.auths, {})
|
||||
|
||||
|
||||
class GenHttpsNamesTest(unittest.TestCase):
|
||||
|
|
|
|||
|
|
@ -29,8 +29,7 @@ class ChooseCertsTest(unittest.TestCase):
|
|||
return choose_certs(certs)
|
||||
|
||||
@mock.patch("letsencrypt.client.display.revocation.util")
|
||||
def test_confirm_revocation(self, mock_util):
|
||||
mock_util().yesno.return_value = True
|
||||
def test_revocation(self, mock_util):
|
||||
mock_util().menu.return_value = (display_util.OK, 0)
|
||||
|
||||
choice = self._call(self.certs)
|
||||
|
|
@ -38,12 +37,8 @@ class ChooseCertsTest(unittest.TestCase):
|
|||
self.assertTrue(self.certs[choice] == self.cert0)
|
||||
|
||||
@mock.patch("letsencrypt.client.display.revocation.util")
|
||||
def test_confirm_cancel(self, mock_util):
|
||||
mock_util().yesno.return_value = False
|
||||
mock_util().menu.side_effect = [
|
||||
(display_util.OK, 0),
|
||||
(display_util.CANCEL, -1)
|
||||
]
|
||||
def test_cancel(self, mock_util):
|
||||
mock_util().menu.return_value = (display_util.CANCEL, -1)
|
||||
|
||||
self.assertRaises(SystemExit, self._call, self.certs)
|
||||
|
||||
|
|
@ -53,7 +48,6 @@ class ChooseCertsTest(unittest.TestCase):
|
|||
(display_util.HELP, 1),
|
||||
(display_util.OK, 1),
|
||||
]
|
||||
mock_util().yesno.return_value = True
|
||||
|
||||
choice = self._call(self.certs)
|
||||
|
||||
|
|
@ -79,5 +73,25 @@ class SuccessRevocationTest(unittest.TestCase):
|
|||
|
||||
self.assertEqual(mock_util().notification.call_count, 1)
|
||||
|
||||
|
||||
class ConfirmRevocationTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
from letsencrypt.client.revoker import Cert
|
||||
self.cert = Cert(pkg_resources.resource_filename(
|
||||
"letsencrypt.client.tests", os.path.join("testdata", "cert.pem")))
|
||||
|
||||
@classmethod
|
||||
def _call(cls, cert):
|
||||
from letsencrypt.client.display.revocation import confirm_revocation
|
||||
return confirm_revocation(cert)
|
||||
|
||||
@mock.patch("letsencrypt.client.display.revocation.util")
|
||||
def test_confirm_revocation(self, mock_util):
|
||||
mock_util().yesno.return_value = True
|
||||
self.assertTrue(self._call(self.cert))
|
||||
|
||||
mock_util().yesno.return_value = False
|
||||
self.assertFalse(self._call(self.cert))
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
|
|
|||
|
|
@ -91,6 +91,14 @@ class NcursesDisplayTest(DisplayT):
|
|||
|
||||
self.assertEqual(ret, (display_util.OK, 0))
|
||||
|
||||
@mock.patch("letsencrypt.client.display.util.dialog.Dialog.menu")
|
||||
def test_menu_desc_only_help(self, mock_menu):
|
||||
mock_menu.return_value = (display_util.HELP, "2")
|
||||
|
||||
ret = self.displayer.menu("Message", self.tags, help_label="More Info")
|
||||
|
||||
self.assertEqual(ret, (display_util.HELP, 1))
|
||||
|
||||
@mock.patch("letsencrypt.client.display.util.dialog.Dialog.menu")
|
||||
def test_menu_desc_only_cancel(self, mock_menu):
|
||||
mock_menu.return_value = (display_util.CANCEL, "")
|
||||
|
|
@ -103,7 +111,7 @@ class NcursesDisplayTest(DisplayT):
|
|||
"dialog.Dialog.inputbox")
|
||||
def test_input(self, mock_input):
|
||||
self.displayer.input("message")
|
||||
mock_input.assert_called_with("message")
|
||||
self.assertEqual(mock_input.call_count, 1)
|
||||
|
||||
@mock.patch("letsencrypt.client.display.util.dialog.Dialog.yesno")
|
||||
def test_yesno(self, mock_yesno):
|
||||
|
|
@ -182,8 +190,13 @@ class FileOutputDisplayTest(DisplayT):
|
|||
self.assertTrue(self.displayer.yesno("message"))
|
||||
with mock.patch("__builtin__.raw_input", return_value="y"):
|
||||
self.assertTrue(self.displayer.yesno("message"))
|
||||
with mock.patch("__builtin__.raw_input", return_value="cancel"):
|
||||
with mock.patch("__builtin__.raw_input", side_effect=["maybe", "y"]):
|
||||
self.assertTrue(self.displayer.yesno("message"))
|
||||
with mock.patch("__builtin__.raw_input", return_value="No"):
|
||||
self.assertFalse(self.displayer.yesno("message"))
|
||||
with mock.patch("__builtin__.raw_input", side_effect=["cancel", "n"]):
|
||||
self.assertFalse(self.displayer.yesno("message"))
|
||||
|
||||
with mock.patch("__builtin__.raw_input", return_value="a"):
|
||||
self.assertTrue(self.displayer.yesno("msg", yes_label="Agree"))
|
||||
|
||||
|
|
|
|||
|
|
@ -309,11 +309,21 @@ class CertTest(unittest.TestCase):
|
|||
self.assertEqual(self.certs[0].orig.status, "")
|
||||
self.assertEqual(self.certs[0].orig_key.status, "")
|
||||
|
||||
def test_print(self):
|
||||
"""Just make sure there aren't any errors."""
|
||||
def test_print_meta(self):
|
||||
"""Just make sure there aren't any major errors."""
|
||||
self.certs[0].add_meta(
|
||||
0, self.paths[0], self.key_path, self.paths[0], self.key_path)
|
||||
# Changed path and deleted file
|
||||
self.certs[1].add_meta(
|
||||
1, self.paths[0], "/not/a/path", self.paths[1], self.key_path)
|
||||
self.assertTrue(self.certs[0].pretty_print())
|
||||
self.assertTrue(self.certs[1].pretty_print())
|
||||
|
||||
def test_print_no_meta(self):
|
||||
self.assertTrue(self.certs[0].pretty_print())
|
||||
self.assertTrue(self.certs[1].pretty_print())
|
||||
|
||||
|
||||
def create_revoker_certs():
|
||||
"""Create a few revoker.Cert objects."""
|
||||
from letsencrypt.client.revoker import Cert
|
||||
|
|
|
|||
|
|
@ -542,5 +542,17 @@ class CleanupTest(unittest.TestCase):
|
|||
self.assertRaises(ValueError, self.authenticator.cleanup, [chall])
|
||||
|
||||
|
||||
class MoreInfoTest(unittest.TestCase):
|
||||
"""Tests for more_info() method. (trivially)"""
|
||||
def setUp(self):
|
||||
from letsencrypt.client.standalone_authenticator import \
|
||||
StandaloneAuthenticator
|
||||
self.authenticator = StandaloneAuthenticator()
|
||||
|
||||
def test_chall_pref(self):
|
||||
"""Make sure exceptions aren't raised."""
|
||||
self.authenticator.more_info()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ import letsencrypt
|
|||
|
||||
from letsencrypt.client import configuration
|
||||
from letsencrypt.client import client
|
||||
from letsencrypt.client import errors
|
||||
from letsencrypt.client import interfaces
|
||||
from letsencrypt.client import le_util
|
||||
from letsencrypt.client import log
|
||||
|
|
@ -116,12 +115,14 @@ def main(): # pylint: disable=too-many-branches
|
|||
displayer = display_util.NcursesDisplay()
|
||||
else:
|
||||
displayer = display_util.FileDisplay(sys.stdout)
|
||||
|
||||
zope.component.provideUtility(displayer)
|
||||
|
||||
if args.view_config_changes:
|
||||
client.view_config_changes(config)
|
||||
sys.exit()
|
||||
|
||||
# TODO: if revoke, rev_cert...
|
||||
if args.revoke:
|
||||
client.revoke(config, args.no_confirm, args.rev_cert, args.rev_key)
|
||||
sys.exit()
|
||||
|
|
@ -135,32 +136,30 @@ def main(): # pylint: disable=too-many-branches
|
|||
|
||||
# Make sure we actually get an installer that is functioning properly
|
||||
# before we begin to try to use it.
|
||||
try:
|
||||
auth = client.determine_authenticator(config)
|
||||
except errors.LetsEncryptMisconfigurationError as err:
|
||||
logging.fatal("Please fix your configuration before proceeding.%s"
|
||||
"The Authenticator exited with the following message: "
|
||||
"%s", os.linesep, err)
|
||||
sys.exit(1)
|
||||
auth = client.determine_authenticator(config)
|
||||
if auth is None:
|
||||
logging.critical("Unable to find a way to authenticate the server.")
|
||||
sys.exit(4)
|
||||
|
||||
# Use the same object if possible
|
||||
if interfaces.IInstaller.providedBy(auth): # pylint: disable=no-member
|
||||
installer = auth
|
||||
else:
|
||||
installer = client.determine_installer(config)
|
||||
# This is simple and avoids confusion right now.
|
||||
installer = None
|
||||
|
||||
doms = ops.choose_names(installer) if args.domains is None else args.domains
|
||||
|
||||
# Prepare for init of Client
|
||||
if args.privkey is None:
|
||||
privkey = client.init_key(args.rsa_key_size, config.key_dir)
|
||||
if args.authkey is None:
|
||||
authkey = client.init_key(args.rsa_key_size, config.key_dir)
|
||||
else:
|
||||
privkey = le_util.Key(args.authkey[0], args.authkey[1])
|
||||
authkey = le_util.Key(args.authkey[0], args.authkey[1])
|
||||
|
||||
acme = client.Client(config, privkey, auth, installer)
|
||||
acme = client.Client(config, authkey, auth, installer)
|
||||
|
||||
# Validate the key and csr
|
||||
client.validate_key_csr(privkey)
|
||||
client.validate_key_csr(authkey)
|
||||
|
||||
# This more closely mimics the capabilities of the CLI
|
||||
# It should be possible for reconfig only, install-only, no-install
|
||||
|
|
@ -170,7 +169,7 @@ def main(): # pylint: disable=too-many-branches
|
|||
if auth is not None:
|
||||
cert_file, chain_file = acme.obtain_certificate(doms)
|
||||
if installer is not None and cert_file is not None:
|
||||
acme.deploy_certificate(doms, privkey, cert_file, chain_file)
|
||||
acme.deploy_certificate(doms, authkey, cert_file, chain_file)
|
||||
if installer is not None:
|
||||
acme.enhance_config(doms, args.redirect)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue