def setUp(self): kwargs = { "chall": acme_util.HTTP01, "uri": "uri", "status": messages.STATUS_INVALID, "error": messages.Error(typ="tls", detail="detail"), } self.http01 = achallenges.KeyAuthorizationAnnotatedChallenge( # pylint: disable=star-args challb=messages.ChallengeBody(**kwargs), domain="example.com", account_key="key") kwargs["chall"] = acme_util.TLSSNI01 self.tls_sni_same = achallenges.KeyAuthorizationAnnotatedChallenge( # pylint: disable=star-args challb=messages.ChallengeBody(**kwargs), domain="example.com", account_key="key") kwargs["error"] = messages.Error(typ="dnssec", detail="detail") self.tls_sni_diff = achallenges.KeyAuthorizationAnnotatedChallenge( # pylint: disable=star-args challb=messages.ChallengeBody(**kwargs), domain="foo.bar", account_key="key")
def test_perform2(self): domain = b'localhost' key = jose.JWK.load(test_util.load_vector('rsa512_key.pem')) http_01 = achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.HTTP01_P, domain=domain, account_key=key) tls_sni_01 = achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.TLSSNI01_P, domain=domain, account_key=key) self.auth.servers = mock.MagicMock() def _run(port, tls): # pylint: disable=unused-argument return "server{0}".format(port) self.auth.servers.run.side_effect = _run responses = self.auth.perform2([http_01, tls_sni_01]) self.assertTrue(isinstance(responses, list)) self.assertEqual(2, len(responses)) self.assertTrue(isinstance(responses[0], challenges.HTTP01Response)) self.assertTrue(isinstance(responses[1], challenges.TLSSNI01Response)) self.assertEqual(self.auth.servers.run.mock_calls, [ mock.call(4321, challenges.HTTP01), mock.call(1234, challenges.TLSSNI01), ]) self.assertEqual(self.auth.served, { "server1234": set([tls_sni_01]), "server4321": set([http_01]), }) self.assertEqual(1, len(self.auth.http_01_resources)) self.assertEqual(1, len(self.auth.certs)) self.assertEqual(list(self.auth.http_01_resources), [ acme_standalone.HTTP01RequestHandler.HTTP01Resource( acme_util.HTTP01, responses[0], mock.ANY) ])
def setUp(self): kwargs = { "chall": acme_util.HTTP01, "uri": "uri", "status": messages.STATUS_INVALID, "error": messages.Error(typ="urn:acme:error:tls", detail="detail"), } # Prevent future regressions if the error type changes self.assertTrue(kwargs["error"].description is not None) self.http01 = achallenges.KeyAuthorizationAnnotatedChallenge( # pylint: disable=star-args challb=messages.ChallengeBody(**kwargs), domain="example.com", account_key="key") kwargs["chall"] = acme_util.TLSSNI01 self.tls_sni_same = achallenges.KeyAuthorizationAnnotatedChallenge( # pylint: disable=star-args challb=messages.ChallengeBody(**kwargs), domain="example.com", account_key="key") kwargs["error"] = messages.Error(typ="dnssec", detail="detail") self.tls_sni_diff = achallenges.KeyAuthorizationAnnotatedChallenge( # pylint: disable=star-args challb=messages.ChallengeBody(**kwargs), domain="foo.bar", account_key="key")
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 achall1 = achallenges.KeyAuthorizationAnnotatedChallenge( challb=messages.ChallengeBody( chall=challenges.TLSSNI01(token="kNdwjwOeX0I_A8DXt9Msmg"), uri="https://ca.org/chall0_uri", status=messages.Status("pending"), ), domain="localhost", account_key=self.rsa512jwk) achall2 = achallenges.KeyAuthorizationAnnotatedChallenge( challb=messages.ChallengeBody( chall=challenges.TLSSNI01(token="m8TdO1qik4JVFtgPPurJmg"), uri="https://ca.org/chall1_uri", status=messages.Status("pending"), ), domain="example.com", account_key=self.rsa512jwk) dvsni_ret_val = [ achall1.response(self.rsa512jwk), achall2.response(self.rsa512jwk), ] mock_dvsni_perform.return_value = dvsni_ret_val responses = self.config.perform([achall1, achall2]) self.assertEqual(mock_dvsni_perform.call_count, 1) self.assertEqual(responses, dvsni_ret_val) self.assertEqual(mock_restart.call_count, 1)
class TLSSNI01Test(unittest.TestCase): """Tests for letsencrypt.plugins.common.TLSSNI01.""" auth_key = jose.JWKRSA.load(test_util.load_vector("rsa512_key.pem")) achalls = [ achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.chall_to_challb( challenges.TLSSNI01(token=b'token1'), "pending"), domain="encryption-example.demo", account_key=auth_key), achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.chall_to_challb( challenges.TLSSNI01(token=b'token2'), "pending"), domain="letsencrypt.demo", account_key=auth_key), ] def setUp(self): from letsencrypt.plugins.common import TLSSNI01 self.sni = TLSSNI01(configurator=mock.MagicMock()) def test_add_chall(self): self.sni.add_chall(self.achalls[0], 0) self.assertEqual(1, len(self.sni.achalls)) self.assertEqual([0], self.sni.indices) def test_setup_challenge_cert(self): # This is a helper function that can be used for handling # open context managers more elegantly. It avoids dealing with # __enter__ and __exit__ calls. # http://www.voidspace.org.uk/python/mock/helpers.html#mock.mock_open mock_open, mock_safe_open = mock.mock_open(), mock.mock_open() response = challenges.TLSSNI01Response() achall = mock.MagicMock() key = test_util.load_pyopenssl_private_key("rsa512_key.pem") achall.response_and_validation.return_value = (response, ( test_util.load_cert("cert.pem"), key)) with mock.patch("letsencrypt.plugins.common.open", mock_open, create=True): with mock.patch("letsencrypt.plugins.common.le_util.safe_open", mock_safe_open): # pylint: disable=protected-access self.assertEqual( response, self.sni._setup_challenge_cert(achall, "randomS1")) # pylint: disable=no-member mock_open.assert_called_once_with(self.sni.get_cert_path(achall), "wb") mock_open.return_value.write.assert_called_once_with( test_util.load_vector("cert.pem")) mock_safe_open.assert_called_once_with(self.sni.get_key_path(achall), "wb", chmod=0o400) mock_safe_open.return_value.write.assert_called_once_with( OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, key))
def challb_to_achall(challb, account_key, domain): """Converts a ChallengeBody object to an AnnotatedChallenge. :param .ChallengeBody challb: ChallengeBody :param .JWK account_key: Authorized Account Key :param str domain: Domain of the challb :returns: Appropriate AnnotatedChallenge :rtype: :class:`letsencrypt.achallenges.AnnotatedChallenge` """ chall = challb.chall logger.info("%s challenge for %s", chall.typ, domain) if isinstance(chall, challenges.KeyAuthorizationChallenge): return achallenges.KeyAuthorizationAnnotatedChallenge( challb=challb, domain=domain, account_key=account_key) elif isinstance(chall, challenges.DNS): return achallenges.DNS(challb=challb, domain=domain) elif isinstance(chall, challenges.RecoveryContact): return achallenges.RecoveryContact(challb=challb, domain=domain) elif isinstance(chall, challenges.ProofOfPossession): return achallenges.ProofOfPossession(challb=challb, domain=domain) else: raise errors.Error("Received unsupported challenge of type: %s", chall.typ)
def test_it(self): self.assertEqual( self._call(acme_util.HTTP01_P), achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.HTTP01_P, account_key="account_key", domain="domain"), )
class AuthenticatorTest(unittest.TestCase): """Tests for letsencrypt.plugins.webroot.Authenticator.""" achall = achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.HTTP01_P, domain=None, account_key=KEY) def setUp(self): from letsencrypt.plugins.webroot import Authenticator self.path = tempfile.mkdtemp() self.validation_path = os.path.join( self.path, ".well-known", "acme-challenge", "ZXZhR3hmQURzNnBTUmIyTEF2OUlaZjE3RHQzanV4R0orUEN0OTJ3citvQQ") self.config = mock.MagicMock(webroot_path=self.path) self.auth = Authenticator(self.config, "webroot") self.auth.prepare() def tearDown(self): shutil.rmtree(self.path) def test_more_info(self): more_info = self.auth.more_info() self.assertTrue(isinstance(more_info, str)) self.assertTrue(self.path in more_info) def test_add_parser_arguments(self): add = mock.MagicMock() self.auth.add_parser_arguments(add) self.assertEqual(1, add.call_count) def test_prepare_bad_root(self): self.config.webroot_path = os.path.join(self.path, "null") self.assertRaises(errors.PluginError, self.auth.prepare) def test_prepare_missing_root(self): self.config.webroot_path = None self.assertRaises(errors.PluginError, self.auth.prepare) def test_prepare_full_root_exists(self): # prepare() has already been called once in setUp() self.auth.prepare() # shouldn't raise any exceptions def test_prepare_reraises_other_errors(self): self.auth.full_path = os.path.join(self.path, "null") os.chmod(self.path, 0o000) self.assertRaises(errors.PluginError, self.auth.prepare) os.chmod(self.path, 0o700) def test_perform_cleanup(self): responses = self.auth.perform([self.achall]) self.assertEqual(1, len(responses)) self.assertTrue(os.path.exists(self.validation_path)) with open(self.validation_path) as validation_f: validation = validation_f.read() self.assertTrue( challenges.KeyAuthorizationChallengeResponse( key_authorization=validation).verify(self.achall.chall, KEY.public_key())) self.auth.cleanup([self.achall]) self.assertFalse(os.path.exists(self.validation_path))
def get_achalls(self): """Return testing achallenges.""" account_key = self.rsa512jwk achall1 = achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.chall_to_challb( challenges.TLSSNI01( token="jIq_Xy1mXGN37tb4L6Xj_es58fW571ZNyXekdZzhh7Q"), "pending"), domain="encryption-example.demo", account_key=account_key) achall2 = achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.chall_to_challb( challenges.TLSSNI01( token="uqnaPzxtrndteOqtrXb0Asl5gOJfWAnnx6QJyvcmlDU"), "pending"), domain="letsencrypt.demo", account_key=account_key) return account_key, achall1, achall2
def test_perform_missing_path(self): self.auth.prepare() missing_achall = achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.HTTP01_P, domain="thing2.com", account_key=KEY) self.assertRaises(errors.PluginError, self.auth.perform, [missing_achall]) self.auth.full_roots[self.achall.domain] = 'null' self.assertRaises(errors.PluginError, self.auth.perform, [self.achall])
def setUp(self): from letsencrypt.plugins.manual import Authenticator self.config = mock.MagicMock( http01_port=8080, manual_test_mode=False, manual_public_ip_logging_ok=False) self.auth = Authenticator(config=self.config, name="manual") self.achalls = [achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.HTTP01_P, domain="foo.com", account_key=KEY)] config_test_mode = mock.MagicMock( http01_port=8080, manual_test_mode=True) self.auth_test_mode = Authenticator( config=config_test_mode, name="manual")
def _create_achalls(plugin): """Returns a list of annotated challenges to test on plugin""" achalls = list() names = plugin.get_testable_domain_names() for domain in names: prefs = plugin.get_chall_pref(domain) for chall_type in prefs: if chall_type == challenges.TLSSNI01: chall = challenges.TLSSNI01( token=os.urandom(challenges.TLSSNI01.TOKEN_SIZE)) challb = acme_util.chall_to_challb(chall, messages.STATUS_PENDING) achall = achallenges.KeyAuthorizationAnnotatedChallenge( challb=challb, domain=domain, account_key=util.JWK) achalls.append(achall) return achalls
class WebrootActionTest(unittest.TestCase): """Tests for webroot argparse actions.""" achall = achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.HTTP01_P, domain="thing.com", account_key=KEY) def setUp(self): from letsencrypt.plugins.webroot import Authenticator self.path = tempfile.mkdtemp() self.parser = argparse.ArgumentParser() self.parser.add_argument("-d", "--domains", action="append", default=[]) Authenticator.inject_parser_options(self.parser, "webroot") def test_webroot_map_action(self): args = self.parser.parse_args( ["--webroot-map", '{{"thing.com":"{0}"}}'.format(self.path)]) self.assertEqual(args.webroot_map["thing.com"], self.path) def test_domain_before_webroot(self): args = self.parser.parse_args("-d {0} -w {1}".format( self.achall.domain, self.path).split()) config = self._get_config_after_perform(args) self.assertEqual(config.webroot_map[self.achall.domain], self.path) def test_domain_before_webroot_error(self): self.assertRaises(errors.PluginError, self.parser.parse_args, "-d foo -w bar -w baz".split()) self.assertRaises(errors.PluginError, self.parser.parse_args, "-d foo -w bar -d baz -w qux".split()) def test_multiwebroot(self): args = self.parser.parse_args("-w {0} -d {1} -w {2} -d bar".format( self.path, self.achall.domain, tempfile.mkdtemp()).split()) self.assertEqual(args.webroot_map[self.achall.domain], self.path) config = self._get_config_after_perform(args) self.assertEqual(config.webroot_map[self.achall.domain], self.path) def _get_config_after_perform(self, config): from letsencrypt.plugins.webroot import Authenticator auth = Authenticator(config, "webroot") auth.perform([self.achall]) return auth.config
class AuthenticatorTest(unittest.TestCase): """Tests for letsencrypt.plugins.webroot.Authenticator.""" achall = achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.HTTP01_P, domain="thing.com", account_key=KEY) def setUp(self): from letsencrypt.plugins.webroot import Authenticator self.path = tempfile.mkdtemp() self.validation_path = os.path.join( self.path, ".well-known", "acme-challenge", "ZXZhR3hmQURzNnBTUmIyTEF2OUlaZjE3RHQzanV4R0orUEN0OTJ3citvQQ") self.config = mock.MagicMock(webroot_path=self.path, webroot_map={"thing.com": self.path}) self.auth = Authenticator(self.config, "webroot") def tearDown(self): shutil.rmtree(self.path) def test_more_info(self): more_info = self.auth.more_info() self.assertTrue(isinstance(more_info, str)) self.assertTrue(self.path in more_info) def test_add_parser_arguments(self): add = mock.MagicMock() self.auth.add_parser_arguments(add) self.assertEqual(0, add.call_count) # args moved to cli.py! def test_prepare_bad_root(self): self.config.webroot_path = os.path.join(self.path, "null") self.config.webroot_map["thing.com"] = self.config.webroot_path self.assertRaises(errors.PluginError, self.auth.prepare) def test_prepare_missing_root(self): self.config.webroot_path = None self.config.webroot_map = {} self.assertRaises(errors.PluginError, self.auth.prepare) def test_prepare_full_root_exists(self): # prepare() has already been called once in setUp() self.auth.prepare() # shouldn't raise any exceptions def test_prepare_reraises_other_errors(self): self.auth.full_path = os.path.join(self.path, "null") os.chmod(self.path, 0o000) self.assertRaises(errors.PluginError, self.auth.prepare) os.chmod(self.path, 0o700) @mock.patch("letsencrypt.plugins.webroot.os.chown") def test_failed_chown_eacces(self, mock_chown): mock_chown.side_effect = OSError(errno.EACCES, "msg") self.auth.prepare() # exception caught and logged @mock.patch("letsencrypt.plugins.webroot.os.chown") def test_failed_chown_not_eacces(self, mock_chown): mock_chown.side_effect = OSError() self.assertRaises(errors.PluginError, self.auth.prepare) def test_prepare_permissions(self): self.auth.prepare() # Remove exec bit from permission check, so that it # matches the file self.auth.perform([self.achall]) path_permissions = stat.S_IMODE(os.stat(self.validation_path).st_mode) self.assertEqual(path_permissions, 0o644) # Check permissions of the directories for dirpath, dirnames, _ in os.walk(self.path): for directory in dirnames: full_path = os.path.join(dirpath, directory) dir_permissions = stat.S_IMODE(os.stat(full_path).st_mode) self.assertEqual(dir_permissions, 0o755) parent_gid = os.stat(self.path).st_gid parent_uid = os.stat(self.path).st_uid self.assertEqual(os.stat(self.validation_path).st_gid, parent_gid) self.assertEqual(os.stat(self.validation_path).st_uid, parent_uid) def test_perform_cleanup(self): self.auth.prepare() responses = self.auth.perform([self.achall]) self.assertEqual(1, len(responses)) self.assertTrue(os.path.exists(self.validation_path)) with open(self.validation_path) as validation_f: validation = validation_f.read() self.assertTrue( challenges.KeyAuthorizationChallengeResponse( key_authorization=validation).verify(self.achall.chall, KEY.public_key())) self.auth.cleanup([self.achall]) self.assertFalse(os.path.exists(self.validation_path))
class TlsSniPerformTest(util.NginxTest): """Test the NginxTlsSni01 challenge.""" account_key = common_test.TLSSNI01Test.auth_key achalls = [ achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.chall_to_challb( challenges.TLSSNI01(token="kNdwjwOeX0I_A8DXt9Msmg"), "pending"), domain="www.example.com", account_key=account_key), achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.chall_to_challb( challenges.TLSSNI01( token="\xba\xa9\xda?<m\xaewmx\xea\xad\xadv\xf4\x02\xc9y" "\x80\xe2_X\t\xe7\xc7\xa4\t\xca\xf7&\x945" ), "pending"), domain="blah", account_key=account_key), achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.chall_to_challb( challenges.TLSSNI01( token="\x8c\x8a\xbf_-f\\cw\xee\xd6\xf8/\xa5\xe3\xfd" "\xeb9\xf1\xf5\xb9\xefVM\xc9w\xa4u\x9c\xe1\x87\xb4" ), "pending"), domain="www.example.org", account_key=account_key), ] def setUp(self): super(TlsSniPerformTest, self).setUp() config = util.get_nginx_configurator( self.config_path, self.config_dir, self.work_dir) from letsencrypt_nginx import tls_sni_01 self.sni = tls_sni_01.NginxTlsSni01(config) def tearDown(self): shutil.rmtree(self.temp_dir) shutil.rmtree(self.config_dir) shutil.rmtree(self.work_dir) @mock.patch("letsencrypt_nginx.configurator" ".NginxConfigurator.choose_vhost") def test_perform(self, mock_choose): self.sni.add_chall(self.achalls[1]) mock_choose.return_value = None result = self.sni.perform() self.assertTrue(result is None) def test_perform0(self): responses = self.sni.perform() self.assertEqual([], responses) @mock.patch("letsencrypt_nginx.configurator.NginxConfigurator.save") def test_perform1(self, mock_save): self.sni.add_chall(self.achalls[0]) response = self.achalls[0].response(self.account_key) mock_setup_cert = mock.MagicMock(return_value=response) # pylint: disable=protected-access self.sni._setup_challenge_cert = mock_setup_cert responses = self.sni.perform() mock_setup_cert.assert_called_once_with(self.achalls[0]) self.assertEqual([response], responses) self.assertEqual(mock_save.call_count, 2) # Make sure challenge config is included in main config http = self.sni.configurator.parser.parsed[ self.sni.configurator.parser.loc["root"]][-1] self.assertTrue( util.contains_at_depth(http, ['include', self.sni.challenge_conf], 1)) def test_perform2(self): acme_responses = [] for achall in self.achalls: self.sni.add_chall(achall) acme_responses.append(achall.response(self.account_key)) mock_setup_cert = mock.MagicMock(side_effect=acme_responses) # pylint: disable=protected-access self.sni._setup_challenge_cert = mock_setup_cert sni_responses = self.sni.perform() self.assertEqual(mock_setup_cert.call_count, 3) for index, achall in enumerate(self.achalls): self.assertEqual( mock_setup_cert.call_args_list[index], mock.call(achall)) http = self.sni.configurator.parser.parsed[ self.sni.configurator.parser.loc["root"]][-1] self.assertTrue(['include', self.sni.challenge_conf] in http[1]) self.assertTrue( util.contains_at_depth(http, ['server_name', 'blah'], 3)) self.assertEqual(len(sni_responses), 3) for i in xrange(3): self.assertEqual(sni_responses[i], acme_responses[i]) def test_mod_config(self): self.sni.add_chall(self.achalls[0]) self.sni.add_chall(self.achalls[2]) v_addr1 = [obj.Addr("69.50.225.155", "9000", True, False), obj.Addr("127.0.0.1", "", False, False)] v_addr2 = [obj.Addr("myhost", "", False, True)] ll_addr = [v_addr1, v_addr2] self.sni._mod_config(ll_addr) # pylint: disable=protected-access self.sni.configurator.save() self.sni.configurator.parser.load() http = self.sni.configurator.parser.parsed[ self.sni.configurator.parser.loc["root"]][-1] self.assertTrue(['include', self.sni.challenge_conf] in http[1]) vhosts = self.sni.configurator.parser.get_vhosts() vhs = [vh for vh in vhosts if vh.filep == self.sni.challenge_conf] for vhost in vhs: if vhost.addrs == set(v_addr1): response = self.achalls[0].response(self.account_key) else: response = self.achalls[2].response(self.account_key) self.assertEqual(vhost.addrs, set(v_addr2)) self.assertEqual(vhost.names, set([response.z_domain])) self.assertEqual(len(vhs), 2) def test_mod_config_fail(self): root = self.sni.configurator.parser.loc["root"] self.sni.configurator.parser.parsed[root] = [['include', 'foo.conf']] # pylint: disable=protected-access self.assertRaises( errors.MisconfigurationError, self.sni._mod_config, [])
def test_unexpected(self): unexpected = achallenges.KeyAuthorizationAnnotatedChallenge( challb=None, domain="0", account_key="dummy_key") self.assertRaises(errors.ContAuthError, self.auth.cleanup, [unexpected])
def test_unexpected(self): self.assertRaises(errors.ContAuthError, self.auth.perform, [ achallenges.KeyAuthorizationAnnotatedChallenge( challb=None, domain="0", account_key="invalid_key") ])
class AuthenticatorTest(unittest.TestCase): """Tests for letsencrypt.plugins.webroot.Authenticator.""" achall = achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.HTTP01_P, domain="thing.com", account_key=KEY) def setUp(self): from letsencrypt.plugins.webroot import Authenticator self.path = tempfile.mkdtemp() self.root_challenge_path = os.path.join(self.path, ".well-known", "acme-challenge") self.validation_path = os.path.join( self.root_challenge_path, "ZXZhR3hmQURzNnBTUmIyTEF2OUlaZjE3RHQzanV4R0orUEN0OTJ3citvQQ") self.config = mock.MagicMock(webroot_path=self.path, webroot_map={"thing.com": self.path}) self.auth = Authenticator(self.config, "webroot") def tearDown(self): shutil.rmtree(self.path) def test_more_info(self): more_info = self.auth.more_info() self.assertTrue(isinstance(more_info, str)) self.assertTrue(self.path in more_info) def test_add_parser_arguments(self): add = mock.MagicMock() self.auth.add_parser_arguments(add) self.assertEqual(2, add.call_count) def test_prepare(self): self.auth.prepare() # shouldn't raise any exceptions @mock.patch("letsencrypt.plugins.webroot.zope.component.getUtility") def test_webroot_from_list(self, mock_get_utility): self.config.webroot_path = [] self.config.webroot_map = {"otherthing.com": self.path} mock_display = mock_get_utility() mock_display.menu.return_value = ( display_util.OK, 1, ) self.auth.perform([self.achall]) self.assertTrue(mock_display.menu.called) for call in mock_display.menu.call_args_list: self.assertTrue(self.achall.domain in call[0][0]) self.assertTrue( all(webroot in call[0][1] for webroot in six.itervalues(self.config.webroot_map))) self.assertEqual(self.config.webroot_map[self.achall.domain], self.path) @mock.patch("letsencrypt.plugins.webroot.zope.component.getUtility") def test_webroot_from_list_help_and_cancel(self, mock_get_utility): self.config.webroot_path = [] self.config.webroot_map = {"otherthing.com": self.path} mock_display = mock_get_utility() mock_display.menu.side_effect = ( (display_util.HELP, -1), (display_util.CANCEL, -1), ) self.assertRaises(errors.PluginError, self.auth.perform, [self.achall]) self.assertTrue(mock_display.notification.called) self.assertTrue(mock_display.menu.called) for call in mock_display.menu.call_args_list: self.assertTrue(self.achall.domain in call[0][0]) self.assertTrue( all(webroot in call[0][1] for webroot in six.itervalues(self.config.webroot_map))) @mock.patch("letsencrypt.plugins.webroot.zope.component.getUtility") def test_new_webroot(self, mock_get_utility): self.config.webroot_path = [] self.config.webroot_map = {} imaginary_dir = os.path.join(os.sep, "imaginary", "dir") mock_display = mock_get_utility() mock_display.menu.return_value = ( display_util.OK, 0, ) mock_display.directory_select.side_effect = ( ( display_util.HELP, -1, ), ( display_util.CANCEL, -1, ), ( display_util.OK, imaginary_dir, ), ( display_util.OK, self.path, ), ) self.auth.perform([self.achall]) self.assertTrue(mock_display.notification.called) for call in mock_display.notification.call_args_list: self.assertTrue(imaginary_dir in call[0][0] or display_util.DSELECT_HELP == call[0][0]) self.assertTrue(mock_display.directory_select.called) for call in mock_display.directory_select.call_args_list: self.assertTrue(self.achall.domain in call[0][0]) def test_perform_missing_root(self): self.config.webroot_path = None self.config.webroot_map = {} self.assertRaises(errors.PluginError, self.auth.perform, []) def test_perform_reraises_other_errors(self): self.auth.full_path = os.path.join(self.path, "null") permission_canary = os.path.join(self.path, "rnd") with open(permission_canary, "w") as f: f.write("thingimy") os.chmod(self.path, 0o000) try: open(permission_canary, "r") print("Warning, running tests as root skips permissions tests...") except IOError: # ok, permissions work, test away... self.assertRaises(errors.PluginError, self.auth.perform, []) os.chmod(self.path, 0o700) @mock.patch("letsencrypt.plugins.webroot.os.chown") def test_failed_chown_eacces(self, mock_chown): mock_chown.side_effect = OSError(errno.EACCES, "msg") self.auth.perform([self.achall]) # exception caught and logged @mock.patch("letsencrypt.plugins.webroot.os.chown") def test_failed_chown_not_eacces(self, mock_chown): mock_chown.side_effect = OSError() self.assertRaises(errors.PluginError, self.auth.perform, []) def test_perform_permissions(self): self.auth.prepare() # Remove exec bit from permission check, so that it # matches the file self.auth.perform([self.achall]) path_permissions = stat.S_IMODE(os.stat(self.validation_path).st_mode) self.assertEqual(path_permissions, 0o644) # Check permissions of the directories for dirpath, dirnames, _ in os.walk(self.path): for directory in dirnames: full_path = os.path.join(dirpath, directory) dir_permissions = stat.S_IMODE(os.stat(full_path).st_mode) self.assertEqual(dir_permissions, 0o755) parent_gid = os.stat(self.path).st_gid parent_uid = os.stat(self.path).st_uid self.assertEqual(os.stat(self.validation_path).st_gid, parent_gid) self.assertEqual(os.stat(self.validation_path).st_uid, parent_uid) def test_perform_cleanup(self): self.auth.prepare() responses = self.auth.perform([self.achall]) self.assertEqual(1, len(responses)) self.assertTrue(os.path.exists(self.validation_path)) with open(self.validation_path) as validation_f: validation = validation_f.read() self.assertTrue( challenges.KeyAuthorizationChallengeResponse( key_authorization=validation).verify(self.achall.chall, KEY.public_key())) self.auth.cleanup([self.achall]) self.assertFalse(os.path.exists(self.validation_path)) self.assertFalse(os.path.exists(self.root_challenge_path)) def test_cleanup_leftovers(self): self.auth.prepare() self.auth.perform([self.achall]) leftover_path = os.path.join(self.root_challenge_path, 'leftover') os.mkdir(leftover_path) self.auth.cleanup([self.achall]) self.assertFalse(os.path.exists(self.validation_path)) self.assertTrue(os.path.exists(self.root_challenge_path)) os.rmdir(leftover_path) @mock.patch('os.rmdir') def test_cleanup_permission_denied(self, mock_rmdir): self.auth.prepare() self.auth.perform([self.achall]) os_error = OSError() os_error.errno = errno.EACCES mock_rmdir.side_effect = os_error self.auth.cleanup([self.achall]) self.assertFalse(os.path.exists(self.validation_path)) self.assertTrue(os.path.exists(self.root_challenge_path)) @mock.patch('os.rmdir') def test_cleanup_oserror(self, mock_rmdir): self.auth.prepare() self.auth.perform([self.achall]) os_error = OSError() os_error.errno = errno.ENOENT mock_rmdir.side_effect = os_error self.assertRaises(OSError, self.auth.cleanup, [self.achall]) self.assertFalse(os.path.exists(self.validation_path)) self.assertTrue(os.path.exists(self.root_challenge_path))