From 647caba164a28c2b3bdb8d8fab18f4a357910fbd Mon Sep 17 00:00:00 2001 From: James Kasten Date: Fri, 24 Jul 2015 03:22:35 -0700 Subject: [PATCH] 100% configurator coverage --- .../letsencrypt_apache/configurator.py | 131 ++++----- .../tests/configurator_test.py | 275 ++++++++++++++++-- letsencrypt/cli.py | 2 +- letsencrypt/errors.py | 3 + 4 files changed, 317 insertions(+), 94 deletions(-) diff --git a/letsencrypt-apache/letsencrypt_apache/configurator.py b/letsencrypt-apache/letsencrypt_apache/configurator.py index 915c129cf..59b000a46 100644 --- a/letsencrypt-apache/letsencrypt_apache/configurator.py +++ b/letsencrypt-apache/letsencrypt_apache/configurator.py @@ -88,6 +88,9 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): private_ips_regex = re.compile( r"(^127\.0\.0\.1)|(^10\.)|(^172\.1[6-9]\.)|" r"(^172\.2[0-9]\.)|(^172\.3[0-1]\.)|(^192\.168\.)") + hostname_regex = re.compile( + r"^(([a-z0-9]|[a-z0-9][a-z0-9\-]*[a-z0-9])\.)*[a-z]+$", re.IGNORECASE) + @classmethod def add_parser_arguments(cls, add): @@ -121,8 +124,8 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): # Add name_server association dict self.assoc = dict() - # Add number of outstanding challenges - self._chall_out = 0 + # Outstanding challenges + self._chall_out = set() # These will be set in the prepare function self.parser = None @@ -147,7 +150,10 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): # Set Version if self.version is None: - self.version = self.get_version() # pragma: no cover + self.version = self.get_version() + if self.version < (2, 2): + raise errors.NotSupportedError( + "Apache Version %s not supported.", str(self.version)) # Get all of the available vhosts self.vhosts = self.get_virtual_hosts() @@ -208,11 +214,12 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): self.aug.set(path["chain_path"][-1], chain_path) # Save notes about the transaction that took place - self.save_notes += ("Changed vhost at %s with addresses of %s\n" % + self.save_notes += ("Changed vhost at %s with addresses of %s\n" + "\tSSLCertificateFile %s\n" + "\tSSLCertificateKeyFile %s\n" % (vhost.filep, - ", ".join(str(addr) for addr in vhost.addrs))) - self.save_notes += "\tSSLCertificateFile %s\n" % cert_path - self.save_notes += "\tSSLCertificateKeyFile %s\n" % key_path + ", ".join(str(addr) for addr in vhost.addrs), + cert_path, key_path)) if chain_path is not None: self.save_notes += "\tSSLCertificateChainFile %s\n" % chain_path @@ -285,7 +292,8 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): points = 1 else: # No points given if names can't be found. - continue + # This gets hit but doesn't register + continue # pragma: no cover if vhost.ssl: points += 2 @@ -309,19 +317,6 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): addr.get_addr() == "_default_" for addr in vh.addrs )] - def create_dn_server_assoc(self, domain, vhost): - """Create an association between a domain name and virtual host. - - Helps to choose an appropriate vhost - - :param str domain: domain name to associate - - :param vhost: virtual host to associate with domain - :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` - - """ - self.assoc[domain] = vhost - def get_all_names(self): """Returns all names found in the Apache Configuration. @@ -334,10 +329,14 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): for vhost in self.vhosts: all_names.update(vhost.get_names()) + for addr in vhost.addrs: - name = self.get_name_from_ip(addr) - if name: - all_names.add(name) + if ApacheConfigurator.hostname_regex.match(addr.get_addr()): + all_names.add(addr.get_addr()) + else: + name = self.get_name_from_ip(addr) + if name: + all_names.add(name) return all_names @@ -460,14 +459,17 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): """ loc = parser.get_aug_path(self.parser.loc["name"]) - if addr.get_port == "443": + + if addr.get_port() == "443": path = self.parser.add_dir_to_ifmodssl( loc, "NameVirtualHost", [str(addr)]) else: path = self.parser.add_dir(loc, "NameVirtualHost", [str(addr)]) - self.save_notes += "Setting %s to be NameBasedVirtualHost\n" % addr - self.save_notes += "\tDirective added to %s\n" % path + msg = ("Setting %s to be NameBasedVirtualHost\n" + "\tDirective added to %s\n" % (addr, path)) + logger.debug(msg) + self.save_notes += msg def prepare_server_https(self, port): """Prepare the server for HTTPS. @@ -515,17 +517,6 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): if self.version >= (2, 4): return - # TODO: Review this 3-year old demo code - # Check for NameVirtualHost - # First see if any of the vhost addresses is a _default_ addr - for addr in addrs: - if addr.get_addr() == "_default_": - if not self.is_name_vhost(default_addr): - logger.debug("Setting all VirtualHosts on %s to be " - "name based vhosts", default_addr) - self.add_name_vhost(default_addr) - - # No default addresses... so set each one individually for addr in addrs: if not self.is_name_vhost(addr): logger.debug("Setting VirtualHost at %s to be a name " @@ -661,9 +652,11 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): need_to_save = False # See if the exact address appears in any other vhost + # Remember 1.1.1.1:* == 1.1.1.1 -> hence any() for addr in vhost.addrs: for test_vh in self.vhosts: - if (vhost.filep != test_vh.filep and addr in test_vh.addrs and + if (vhost.filep != test_vh.filep and + any(test_addr == addr for test_addr in test_vh.addrs) and not self.is_name_vhost(addr)): self.add_name_vhost(addr) logger.info("Enabling NameVirtualHosts on %s", addr) @@ -739,7 +732,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): "Unable to create one as intended addresses conflict; " "Current configuration does not support automated " "redirection") - self._create_redirect_vhost(redirect_addrs) + self._create_redirect_vhost(ssl_vhost) else: # Check if redirection already exists self._verify_no_redirects(general_vh) @@ -817,6 +810,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): serveralias = "" servername = "" + if ssl_vhost.name is not None: servername = "ServerName " + ssl_vhost.name if ssl_vhost.aliases: @@ -833,9 +827,10 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): "ErrorLog /var/log/apache2/redirect.error.log\n" "LogLevel warn\n" "\n" - % (" ".join(self._get_redirect_addrs(ssl_vhost)), - servername, serveralias, - " ".join(constants.REWRITE_HTTPS_ARGS))) + % ( + " ".join(str(addr) for addr in self._get_redirect_addrs(ssl_vhost)), + servername, serveralias, + " ".join(constants.REWRITE_HTTPS_ARGS))) def _write_out_redirect(self, ssl_vhost, text): # This is the default name @@ -845,7 +840,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): if ssl_vhost.name is not None: # make sure servername doesn't exceed filename length restriction if len(ssl_vhost.name) < (255 - (len(redirect_filename) + 1)): - redirect_filename = "le-redirect-%s.conf" % ssl_vhost.servername + redirect_filename = "le-redirect-%s.conf" % ssl_vhost.name redirect_filepath = os.path.join( self.parser.root, "sites-available", redirect_filename) @@ -900,9 +895,9 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): for vhost in self.vhosts: if vhost.ssl: cert_path = self.parser.find_dir( - "SSLCertificateFile", None, vhost.path) + "SSLCertificateFile", None, vhost.path, exclude=False) key_path = self.parser.find_dir( - "SSLCertificateKeyFile", None, vhost.path) + "SSLCertificateKeyFile", None, vhost.path, exclude=False) if cert_path and key_path: cert = os.path.abspath(self.parser.get_arg(cert_path[-1])) @@ -940,12 +935,9 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): :param vhost: vhost to enable :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` - :returns: Success - :rtype: bool - """ if self.is_site_enabled(vhost.filep): - return True + return if vhost.ssl: # TODO: Make this based on addresses @@ -961,8 +953,10 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): vhost.enabled = True logger.info("Enabling available site: %s", vhost.filep) self.save_notes += "Enabled site %s\n" % vhost.filep - return True - return False + else: + raise errors.MisconfigurationError( + "Unsupported filesystem layout. " + "sites-available/enabled expected.") def enable_mod(self, mod_name): """Enables module in Apache. @@ -976,7 +970,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): if (not os.path.isdir(os.path.join(self.parser.root, "mods-available")) or not os.path.isdir( os.path.join(self.parser.root, "mods-enabled"))): - raise errors.MisconfigurationError( + raise errors.NotSupportedError( "Unsupported directory layout. You may try to enable mod %s " "and try again." % mod_name) @@ -1001,7 +995,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): elif mod_name == "rewrite": self._enable_mod_debian_files(["rewrite.load"], "rewrite_module") else: - raise NotImplementedError + raise errors.NotSupportedError def _enable_mod_debian_files(self, filenames, mod_name): """Move over all required files into mods-enabled.""" @@ -1011,7 +1005,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): # Check to see all files are available. for filename in filenames: if not os.path.isfile(os.path.join(mods_available, filename)): - raise errors.MisconfigurationError( + raise errors.NoInstallationError( "Unable to enable module. Required files missing from " "mods-available. %s" % str(filenames)) @@ -1029,6 +1023,8 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): def restart(self): """Restarts apache server. + .. todo:: This function will be converted to using reload + :returns: Success :rtype: bool @@ -1053,28 +1049,11 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): raise errors.PluginError("Unable to run apache2ctl") if proc.returncode != 0: - print proc.returncode # Enter recovery routine... logger.error("Apache Configtest failed\n%s\n%s", stdout, stderr) raise errors.MisconfigurationError( "Apache Configtest failure:\n%s\n%s" % (stdout, stderr)) - def verify_setup(self): - """Verify the setup to ensure safe operating environment. - - Make sure that files/directories are setup with appropriate permissions - Aim for defensive coding... make sure all input files - have permissions of root - - """ - uid = os.geteuid() - le_util.make_or_verify_dir( - self.config.config_dir, core_constants.CONFIG_DIRS_MODE, uid) - le_util.make_or_verify_dir( - self.config.work_dir, core_constants.CONFIG_DIRS_MODE, uid) - le_util.make_or_verify_dir( - self.config.backup_dir, core_constants.CONFIG_DIRS_MODE, uid) - def get_version(self): """Return version of Apache Server. @@ -1129,7 +1108,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): outstanding challenges will have to be designed better. """ - self._chall_out += len(achalls) + self._chall_out.update(achalls) responses = [None] * len(achalls) apache_dvsni = dvsni.ApacheDvsni(self) @@ -1157,10 +1136,10 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): def cleanup(self, achalls): """Revert all challenges.""" - self._chall_out -= len(achalls) + self._chall_out.difference_update(achalls) # If all of the challenges have been finished, clean up everything - if self._chall_out <= 0: + if not self._chall_out: self.revert_challenge_config() self.restart() @@ -1192,7 +1171,7 @@ def apache_restart(apache_init_script): except (OSError, ValueError): logger.fatal( "Unable to restart the Apache process with %s", apache_init_script) - raise errors.PluginError( + raise errors.MisconfigurationError( "Unable to restart Apache process with %s" % apache_init_script) stdout, stderr = proc.communicate() diff --git a/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py b/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py index c2e7d2916..233b20b42 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py +++ b/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py @@ -1,6 +1,7 @@ """Test for letsencrypt_apache.configurator.""" import os import shutil +import socket import unittest import mock @@ -36,6 +37,15 @@ class TwoVhost80Test(util.ApacheTest): shutil.rmtree(self.config_dir) shutil.rmtree(self.work_dir) + @mock.patch("letsencrypt_apache.parser.ApacheParser") + def test_prepare_version(self, mock_parser): + self.config.version = None + self.config.config_test = mock.Mock() + self.config.get_version = mock.Mock(return_value=(1, 1)) + + self.assertRaises( + errors.NotSupportedError, self.config.prepare) + def test_add_parser_arguments(self): from letsencrypt_apache.configurator import ApacheConfigurator # Weak test.. @@ -46,6 +56,31 @@ class TwoVhost80Test(util.ApacheTest): self.assertEqual(names, set( ["letsencrypt.demo", "encryption-example.demo", "ip-172-30-0-17"])) + @mock.patch("letsencrypt_apache.configurator.socket.gethostbyaddr") + def test_get_all_names_addrs(self, mock_gethost): + mock_gethost.side_effect = [("google.com","",""), socket.error] + vh = obj.VirtualHost( + "fp", "ap", + set([obj.Addr(("8.8.8.8", "443")), + obj.Addr(("zombo.com",)), + obj.Addr(("192.168.1.2"))]), + True, False) + self.config.vhosts.append(vh) + + names = self.config.get_all_names() + self.assertEqual(len(names), 5) + self.assertTrue("zombo.com" in names) + self.assertTrue("google.com" in names) + self.assertTrue("letsencrypt.demo" in names) + + def test_add_servernames_alias(self): + self.config.parser.add_dir( + self.vh_truth[2].path, "ServerAlias", ["*.le.co"]) + self.config._add_servernames(self.vh_truth[2]) # pylint: disable=protected-access + + self.assertEqual( + self.vh_truth[2].get_names(), set(["*.le.co", "ip-172-30-0-17"])) + def test_get_virtual_hosts(self): """Make sure all vhosts are being properly found. @@ -88,6 +123,13 @@ class TwoVhost80Test(util.ApacheTest): self.assertTrue( self.config._find_best_vhost("does-not-exist.com") is None) + def test_find_best_vhost_variety(self): + ssl_vh = obj.VirtualHost( + "fp", "ap", set([obj.Addr(("*", "443")), obj.Addr(("zombo.com",))]), + True, False) + self.config.vhosts.append(ssl_vh) + self.assertEqual(self.config._find_best_vhost("zombo.com"), ssl_vh) + def test_find_best_vhost_default(self): # Assume only the two default vhosts. self.config.vhosts = [vh for vh in self.config.vhosts @@ -129,6 +171,27 @@ class TwoVhost80Test(util.ApacheTest): self.assertTrue("ssl_module" in self.config.parser.modules) self.assertTrue("mod_ssl.c" in self.config.parser.modules) + def test_enable_mod_unsupported_dirs(self): + os.removedirs(os.path.join(self.config.parser.root, "mods-enabled")) + self.assertRaises( + errors.NotSupportedError, self.config.enable_mod, "ssl") + + def test_enable_mod_unsupported_mod(self): + self.assertRaises( + errors.NotSupportedError, self.config.enable_mod, "unknown") + + def test_enable_mod_not_installed(self): + os.remove(os.path.join( + self.config.parser.root, "mods-available", "ssl.load")) + self.assertRaises( + errors.NoInstallationError, self.config.enable_mod, "ssl") + + def test_enable_mod_files_already_exist(self): + path = os.path.join(self.config.parser.root, "mods-enabled", "ssl.load") + open(path, "w").close() + self.assertRaises( + errors.PluginError, self.config.enable_mod, "ssl") + @mock.patch("letsencrypt_apache.parser.subprocess.Popen") def test_enable_site(self, mock_popen): mock_popen().returncode = 0 @@ -139,6 +202,15 @@ class TwoVhost80Test(util.ApacheTest): self.config.enable_site(self.vh_truth[1]) self.assertTrue(self.vh_truth[1].enabled) + # Go again to make sure nothing fails + self.config.enable_site(self.vh_truth[1]) + + def test_enable_site_failure(self): + self.assertRaises( + errors.MisconfigurationError, + self.config.enable_site, + obj.VirtualHost("asdf", "afsaf", set(), False, False)) + @mock.patch("letsencrypt_apache.parser.subprocess.Popen") def test_deploy_cert(self, mock_popen): mock_popen().returncode = 0 @@ -204,8 +276,11 @@ class TwoVhost80Test(util.ApacheTest): def test_add_name_vhost(self): self.config.add_name_vhost(obj.Addr.fromstring("*:443")) + self.config.add_name_vhost(obj.Addr.fromstring("*:80")) self.assertTrue(self.config.parser.find_dir( - "NameVirtualHost", "*:443")) + "NameVirtualHost", "*:443", exclude=False)) + self.assertTrue(self.config.parser.find_dir( + "NameVirtualHost", "*:80")) def test_prepare_server_https(self): self.config.parser.modules.add("ssl_module") @@ -218,7 +293,8 @@ class TwoVhost80Test(util.ApacheTest): self.config.parser.add_dir_to_ifmodssl = mock_add_dir self.config.prepare_server_https("443") - self.assertTrue(mock_add_dir.called) + self.config.prepare_server_https("8080") + self.assertEqual(mock_add_dir.call_count, 2) def test_make_vhost_ssl(self): ssl_vhost = self.config.make_vhost_ssl(self.vh_truth[0]) @@ -246,26 +322,37 @@ class TwoVhost80Test(util.ApacheTest): self.assertEqual(len(self.config.vhosts), 5) + def test_make_vhost_ssl_extra_vhs(self): + self.config.aug.match = mock.Mock(return_value=["p1", "p2"]) + self.assertRaises( + errors.PluginError, self.config.make_vhost_ssl, self.vh_truth[0]) + + def test_make_vhost_ssl_bad_write(self): + mock_open = mock.mock_open() + # This calls open + self.config.reverter.register_file_creation = mock.Mock() + mock_open.side_effect = IOError + with mock.patch("__builtin__.open", mock_open): + self.assertRaises( + errors.PluginError, + self.config.make_vhost_ssl, self.vh_truth[0]) + + def test_get_ssl_vhost_path(self): + self.assertTrue( + self.config._get_ssl_vhost_path("example_path").endswith(".conf")) + + def test_add_name_vhost_if_necessary(self): + self.config.save = mock.Mock() + self.config.version = (2, 2) + self.config._add_name_vhost_if_necessary(self.vh_truth[0]) + self.assertTrue(self.config.save.called) + @mock.patch("letsencrypt_apache.configurator.dvsni.ApacheDvsni.perform") @mock.patch("letsencrypt_apache.configurator.ApacheConfigurator.restart") 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 - auth_key = le_util.Key(self.rsa256_file, self.rsa256_pem) - achall1 = achallenges.DVSNI( - challb=acme_util.chall_to_challb( - challenges.DVSNI( - r="jIq_Xy1mXGN37tb4L6Xj_es58fW571ZNyXekdZzhh7Q", - nonce="37bc5eb75d3e00a19b4f6355845e5a18"), - "pending"), - domain="encryption-example.demo", key=auth_key) - achall2 = achallenges.DVSNI( - challb=acme_util.chall_to_challb( - challenges.DVSNI( - r="uqnaPzxtrndteOqtrXb0Asl5gOJfWAnnx6QJyvcmlDU", - nonce="59ed014cac95f77057b1d7a1b2c596ba"), - "pending"), - domain="letsencrypt.demo", key=auth_key) + auth_key, achall1, achall2 = self.get_achalls() dvsni_ret_val = [ challenges.DVSNIResponse(s="randomS1"), @@ -280,6 +367,31 @@ class TwoVhost80Test(util.ApacheTest): self.assertEqual(mock_restart.call_count, 1) + @mock.patch("letsencrypt_apache.configurator.ApacheConfigurator.restart") + def test_cleanup(self, mock_restart): + auth_key, achall1, achall2 = self.get_achalls() + + self.config._chall_out.add(achall1) + self.config._chall_out.add(achall2) + + self.config.cleanup([achall1]) + self.assertFalse(mock_restart.called) + + self.config.cleanup([achall2]) + self.assertTrue(mock_restart.called) + + @mock.patch("letsencrypt_apache.configurator.ApacheConfigurator.restart") + def test_cleanup_no_errors(self, mock_restart): + auth_key, achall1, achall2 = self.get_achalls() + + self.config._chall_out.add(achall1) + + self.config.cleanup([achall2]) + self.assertFalse(mock_restart.called) + + self.config.cleanup([achall1, achall2]) + self.assertTrue(mock_restart.called) + @mock.patch("letsencrypt_apache.configurator.subprocess.Popen") def test_get_version(self, mock_popen): mock_popen().communicate.return_value = ( @@ -301,7 +413,79 @@ class TwoVhost80Test(util.ApacheTest): mock_popen.side_effect = OSError("Can't find program") self.assertRaises(errors.PluginError, self.config.get_version) + @mock.patch("letsencrypt_apache.configurator.subprocess.Popen") + def test_restart(self, mock_popen): + """These will be changed soon enough with reload.""" + mock_popen().returncode = 0 + mock_popen().communicate.return_value = ("", "") + + self.config.restart() + + @mock.patch("letsencrypt_apache.configurator.subprocess.Popen") + def test_restart_bad_process(self, mock_popen): + mock_popen.side_effect = OSError + + self.assertRaises(errors.MisconfigurationError, self.config.restart) + + @mock.patch("letsencrypt_apache.configurator.subprocess.Popen") + def test_restart_failure(self, mock_popen): + mock_popen().communicate.return_value = ("", "") + mock_popen.returncode=1 + + self.assertRaises(errors.MisconfigurationError, self.config.restart) + + @mock.patch("letsencrypt_apache.configurator.subprocess.Popen") + def test_config_test(self, mock_popen): + mock_popen().communicate.return_value = ("a", "b") + mock_popen().returncode = 0 + + self.config.config_test() + + @mock.patch("letsencrypt_apache.configurator.subprocess.Popen") + def test_config_test_bad_process(self, mock_popen): + mock_popen.side_effect = ValueError + + self.assertRaises(errors.PluginError, self.config.config_test) + + @mock.patch("letsencrypt_apache.configurator.subprocess.Popen") + def test_config_test_failure(self, mock_popen): + mock_popen().communicate.return_value = ("", "") + mock_popen().returncode = -1 + + self.assertRaises(errors.MisconfigurationError, self.config.config_test) + + + def test_get_all_certs_keys(self): + c_k = self.config.get_all_certs_keys() + + self.assertEqual(len(c_k), 1) + cert, key, path = next(iter(c_k)) + self.assertTrue("cert" in cert) + self.assertTrue("key" in key) + self.assertTrue("default-ssl.conf" in path) + + def test_get_all_certs_keys_malformed_conf(self): + self.config.parser.find_dir = mock.Mock(side_effect=[["path"], []]) + c_k = self.config.get_all_certs_keys() + + self.assertFalse(c_k) + + def test_more_info(self): + self.assertTrue(self.config.more_info()) + + def test_get_chall_pref(self): + self.assertTrue(isinstance(self.config.get_chall_pref(""), list)) + + def test_temp_install(self): + from letsencrypt_apache.configurator import temp_install + path = os.path.join(self.work_dir, "test_it") + temp_install(path) + self.assertTrue(os.path.isfile(path)) + # TEST ENHANCEMENTS + def test_supported_enhancements(self): + self.assertTrue(isinstance(self.config.supported_enhancements(), list)) + def test_enhance_unknown_enhancement(self): self.assertRaises( errors.PluginError, @@ -329,6 +513,17 @@ class TwoVhost80Test(util.ApacheTest): self.assertTrue("rewrite_module" in self.config.parser.modules) + def test_redirect_with_conflict(self): + self.config.parser.modules.add("rewrite_module") + ssl_vh = obj.VirtualHost( + "fp", "ap", set([obj.Addr(("*", "443")), obj.Addr(("zombo.com",))]), + True, False) + # No names ^ this guy should conflict. + + # pylint: disable=protected-access + self.assertRaises( + errors.PluginError, self.config._enable_redirect, ssl_vh, "") + def test_redirect_twice(self): # Skip the enable mod self.config.parser.modules.add("rewrite_module") @@ -346,6 +541,15 @@ class TwoVhost80Test(util.ApacheTest): self.assertRaises( errors.PluginError, self.config.enhance, "letsencrypt.demo", "redirect") + def test_unknown_rewrite2(self): + # Skip the enable mod + self.config.parser.modules.add("rewrite_module") + self.config.parser.add_dir( + self.vh_truth[3].path, "RewriteRule", ["Unknown", "2", "3"]) + self.config.save() + self.assertRaises( + errors.PluginError, + self.config.enhance, "letsencrypt.demo", "redirect") def test_unknown_redirect(self): # Skip the enable mod @@ -357,6 +561,43 @@ class TwoVhost80Test(util.ApacheTest): errors.PluginError, self.config.enhance, "letsencrypt.demo", "redirect") + def test_create_own_redirect(self): + self.config.parser.modules.add("rewrite_module") + # For full testing... give names... + self.vh_truth[1].name = "default.com" + self.vh_truth[1].aliases = set(["yes.default.com"]) + + self.config._enable_redirect(self.vh_truth[1], "") # pylint: disable=protected-access + self.assertEqual(len(self.config.vhosts), 5) + + def get_achalls(self): + auth_key = le_util.Key(self.rsa256_file, self.rsa256_pem) + achall1 = achallenges.DVSNI( + challb=acme_util.chall_to_challb( + challenges.DVSNI( + r="jIq_Xy1mXGN37tb4L6Xj_es58fW571ZNyXekdZzhh7Q", + nonce="37bc5eb75d3e00a19b4f6355845e5a18"), + "pending"), + domain="encryption-example.demo", key=auth_key) + achall2 = achallenges.DVSNI( + challb=acme_util.chall_to_challb( + challenges.DVSNI( + r="uqnaPzxtrndteOqtrXb0Asl5gOJfWAnnx6QJyvcmlDU", + nonce="59ed014cac95f77057b1d7a1b2c596ba"), + "pending"), + domain="letsencrypt.demo", key=auth_key) + + return auth_key, achall1, achall2 + + def test_make_addrs_sni_ready(self): + self.config.version = (2, 2) + self.config.make_addrs_sni_ready( + set([obj.Addr.fromstring("*:443"), obj.Addr.fromstring("*:80")])) + self.assertTrue(self.config.parser.find_dir( + "NameVirtualHost", "*:80", exclude=False)) + self.assertTrue(self.config.parser.find_dir( + "NameVirtualHost", "*:443", exclude=False)) + if __name__ == "__main__": unittest.main() # pragma: no cover diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 0797f23b4..b25ef0760 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -172,7 +172,7 @@ def run(args, config, plugins): authenticator = display_ops.pick_authenticator( config, args.authenticator, plugins) else: - # TODO: this assume that user doesn't want to pick authenticator + # TODO: this assumes that user doesn't want to pick authenticator # and installer separately... authenticator = installer = display_ops.pick_configurator( config, args.configurator, plugins) diff --git a/letsencrypt/errors.py b/letsencrypt/errors.py index e1cae19c7..5cc45f000 100644 --- a/letsencrypt/errors.py +++ b/letsencrypt/errors.py @@ -65,6 +65,9 @@ class NoInstallationError(PluginError): class MisconfigurationError(PluginError): """Let's Encrypt Misconfiguration error.""" +class NotSupportedError(PluginError): + """Let's Encrypt Plugin function not supported error.""" + class RevokerError(Error): """Let's Encrypt Revoker error."""