From 6c8eb8be17741ed13d8a24afbeac56851f290aa5 Mon Sep 17 00:00:00 2001 From: James Kasten Date: Thu, 19 Feb 2015 04:13:39 -0800 Subject: [PATCH] Revisions through running/testing --- letsencrypt/client/apache/configurator.py | 12 +++- letsencrypt/client/client.py | 59 +++++++++++++++---- letsencrypt/client/display/ops.py | 36 +++++++---- letsencrypt/client/display/revocation.py | 4 +- letsencrypt/client/display/util.py | 30 ++++++---- letsencrypt/client/interfaces.py | 7 +++ letsencrypt/client/revoker.py | 40 +++++++++---- .../client/standalone_authenticator.py | 17 ++++-- letsencrypt/client/tests/display/ops_test.py | 34 +++++++++-- .../client/tests/display/revocation_test.py | 32 +++++++--- letsencrypt/client/tests/display/util_test.py | 17 +++++- letsencrypt/client/tests/revoker_test.py | 14 ++++- .../tests/standalone_authenticator_test.py | 12 ++++ letsencrypt/scripts/main.py | 29 +++++---- 14 files changed, 257 insertions(+), 86 deletions(-) diff --git a/letsencrypt/client/apache/configurator.py b/letsencrypt/client/apache/configurator.py index 54eeb3fc1..1bfa83613 100644 --- a/letsencrypt/client/apache/configurator.py +++ b/letsencrypt/client/apache/configurator.py @@ -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 diff --git a/letsencrypt/client/client.py b/letsencrypt/client/client.py index 01969b748..76665c327 100644 --- a/letsencrypt/client/client.py +++ b/letsencrypt/client/client.py @@ -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): diff --git a/letsencrypt/client/display/ops.py b/letsencrypt/client/display/ops.py index 205f3fb08..34a036c71 100644 --- a/letsencrypt/client/display/ops.py +++ b/letsencrypt/client/display/ops.py @@ -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]) diff --git a/letsencrypt/client/display/revocation.py b/letsencrypt/client/display/revocation.py index 76666ddf6..8db334731 100644 --- a/letsencrypt/client/display/revocation.py +++ b/letsencrypt/client/display/revocation.py @@ -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: diff --git a/letsencrypt/client/display/util.py b/letsencrypt/client/display/util.py index 0b5e7c7d6..04db3ebb2 100644 --- a/letsencrypt/client/display/util.py +++ b/letsencrypt/client/display/util.py @@ -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. diff --git a/letsencrypt/client/interfaces.py b/letsencrypt/client/interfaces.py index 771ef4676..e6ed243c4 100644 --- a/letsencrypt/client/interfaces.py +++ b/letsencrypt/client/interfaces.py @@ -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. diff --git a/letsencrypt/client/revoker.py b/letsencrypt/client/revoker.py index 77f262e6f..73ac779c9 100644 --- a/letsencrypt/client/revoker.py +++ b/letsencrypt/client/revoker.py @@ -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): diff --git a/letsencrypt/client/standalone_authenticator.py b/letsencrypt/client/standalone_authenticator.py index e2b1d7872..2c870068f 100755 --- a/letsencrypt/client/standalone_authenticator.py +++ b/letsencrypt/client/standalone_authenticator.py @@ -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.") diff --git a/letsencrypt/client/tests/display/ops_test.py b/letsencrypt/client/tests/display/ops_test.py index 7b4a6d8b5..247a8c3e8 100644 --- a/letsencrypt/client/tests/display/ops_test.py +++ b/letsencrypt/client/tests/display/ops_test.py @@ -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): diff --git a/letsencrypt/client/tests/display/revocation_test.py b/letsencrypt/client/tests/display/revocation_test.py index 4e8591272..ecf247757 100644 --- a/letsencrypt/client/tests/display/revocation_test.py +++ b/letsencrypt/client/tests/display/revocation_test.py @@ -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() diff --git a/letsencrypt/client/tests/display/util_test.py b/letsencrypt/client/tests/display/util_test.py index b355f0fc0..89dc3cfe3 100644 --- a/letsencrypt/client/tests/display/util_test.py +++ b/letsencrypt/client/tests/display/util_test.py @@ -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")) diff --git a/letsencrypt/client/tests/revoker_test.py b/letsencrypt/client/tests/revoker_test.py index a347970c9..f5c9feace 100644 --- a/letsencrypt/client/tests/revoker_test.py +++ b/letsencrypt/client/tests/revoker_test.py @@ -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 diff --git a/letsencrypt/client/tests/standalone_authenticator_test.py b/letsencrypt/client/tests/standalone_authenticator_test.py index 60a1ba600..955afc0d6 100644 --- a/letsencrypt/client/tests/standalone_authenticator_test.py +++ b/letsencrypt/client/tests/standalone_authenticator_test.py @@ -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() diff --git a/letsencrypt/scripts/main.py b/letsencrypt/scripts/main.py index 8e2589f15..9ec42bd44 100755 --- a/letsencrypt/scripts/main.py +++ b/letsencrypt/scripts/main.py @@ -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)