mirror of
https://github.com/certbot/certbot.git
synced 2026-07-31 18:34:41 +02:00
s/letsencrypt/certbot
This commit is contained in:
@@ -1 +0,0 @@
|
||||
"""Let's Encrypt Tests"""
|
||||
@@ -1,185 +0,0 @@
|
||||
"""Tests for letsencrypt.account."""
|
||||
import datetime
|
||||
import os
|
||||
import shutil
|
||||
import stat
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
import mock
|
||||
import pytz
|
||||
|
||||
from acme import jose
|
||||
from acme import messages
|
||||
|
||||
from letsencrypt import errors
|
||||
|
||||
from letsencrypt.tests import test_util
|
||||
|
||||
|
||||
KEY = jose.JWKRSA.load(test_util.load_vector("rsa512_key_2.pem"))
|
||||
|
||||
|
||||
class AccountTest(unittest.TestCase):
|
||||
"""Tests for letsencrypt.account.Account."""
|
||||
|
||||
def setUp(self):
|
||||
from letsencrypt.account import Account
|
||||
self.regr = mock.MagicMock()
|
||||
self.meta = Account.Meta(
|
||||
creation_host="test.letsencrypt.org",
|
||||
creation_dt=datetime.datetime(
|
||||
2015, 7, 4, 14, 4, 10, tzinfo=pytz.UTC))
|
||||
self.acc = Account(self.regr, KEY, self.meta)
|
||||
|
||||
with mock.patch("letsencrypt.account.socket") as mock_socket:
|
||||
mock_socket.getfqdn.return_value = "test.letsencrypt.org"
|
||||
with mock.patch("letsencrypt.account.datetime") as mock_dt:
|
||||
mock_dt.datetime.now.return_value = self.meta.creation_dt
|
||||
self.acc_no_meta = Account(self.regr, KEY)
|
||||
|
||||
def test_init(self):
|
||||
self.assertEqual(self.regr, self.acc.regr)
|
||||
self.assertEqual(KEY, self.acc.key)
|
||||
self.assertEqual(self.meta, self.acc_no_meta.meta)
|
||||
|
||||
def test_id(self):
|
||||
self.assertEqual(
|
||||
self.acc.id, "bca5889f66457d5b62fbba7b25f9ab6f")
|
||||
|
||||
def test_slug(self):
|
||||
self.assertEqual(
|
||||
self.acc.slug, "test.letsencrypt.org@2015-07-04T14:04:10Z (bca5)")
|
||||
|
||||
def test_repr(self):
|
||||
self.assertEqual(
|
||||
repr(self.acc),
|
||||
"<Account(bca5889f66457d5b62fbba7b25f9ab6f)>")
|
||||
|
||||
|
||||
class ReportNewAccountTest(unittest.TestCase):
|
||||
"""Tests for letsencrypt.account.report_new_account."""
|
||||
|
||||
def setUp(self):
|
||||
self.config = mock.MagicMock(config_dir="/etc/letsencrypt")
|
||||
reg = messages.Registration.from_data(email="rhino@jungle.io")
|
||||
self.acc = mock.MagicMock(regr=messages.RegistrationResource(
|
||||
uri=None, new_authzr_uri=None, body=reg))
|
||||
|
||||
def _call(self):
|
||||
from letsencrypt.account import report_new_account
|
||||
report_new_account(self.acc, self.config)
|
||||
|
||||
@mock.patch("letsencrypt.account.zope.component.queryUtility")
|
||||
def test_no_reporter(self, mock_zope):
|
||||
mock_zope.return_value = None
|
||||
self._call()
|
||||
|
||||
@mock.patch("letsencrypt.account.zope.component.queryUtility")
|
||||
def test_it(self, mock_zope):
|
||||
self._call()
|
||||
call_list = mock_zope().add_message.call_args_list
|
||||
self.assertTrue(self.config.config_dir in call_list[0][0][0])
|
||||
self.assertTrue(
|
||||
", ".join(self.acc.regr.body.emails) in call_list[1][0][0])
|
||||
|
||||
|
||||
class AccountMemoryStorageTest(unittest.TestCase):
|
||||
"""Tests for letsencrypt.account.AccountMemoryStorage."""
|
||||
|
||||
def setUp(self):
|
||||
from letsencrypt.account import AccountMemoryStorage
|
||||
self.storage = AccountMemoryStorage()
|
||||
|
||||
def test_it(self):
|
||||
account = mock.Mock(id="x")
|
||||
self.assertEqual([], self.storage.find_all())
|
||||
self.assertRaises(errors.AccountNotFound, self.storage.load, "x")
|
||||
self.storage.save(account)
|
||||
self.assertEqual([account], self.storage.find_all())
|
||||
self.assertEqual(account, self.storage.load("x"))
|
||||
self.storage.save(account)
|
||||
self.assertEqual([account], self.storage.find_all())
|
||||
|
||||
|
||||
class AccountFileStorageTest(unittest.TestCase):
|
||||
"""Tests for letsencrypt.account.AccountFileStorage."""
|
||||
|
||||
def setUp(self):
|
||||
self.tmp = tempfile.mkdtemp()
|
||||
self.config = mock.MagicMock(
|
||||
accounts_dir=os.path.join(self.tmp, "accounts"))
|
||||
from letsencrypt.account import AccountFileStorage
|
||||
self.storage = AccountFileStorage(self.config)
|
||||
|
||||
from letsencrypt.account import Account
|
||||
self.acc = Account(
|
||||
regr=messages.RegistrationResource(
|
||||
uri=None, new_authzr_uri=None, body=messages.Registration()),
|
||||
key=KEY)
|
||||
|
||||
def tearDown(self):
|
||||
shutil.rmtree(self.tmp)
|
||||
|
||||
def test_init_creates_dir(self):
|
||||
self.assertTrue(os.path.isdir(self.config.accounts_dir))
|
||||
|
||||
def test_save_and_restore(self):
|
||||
self.storage.save(self.acc)
|
||||
account_path = os.path.join(self.config.accounts_dir, self.acc.id)
|
||||
self.assertTrue(os.path.exists(account_path))
|
||||
for file_name in "regr.json", "meta.json", "private_key.json":
|
||||
self.assertTrue(os.path.exists(
|
||||
os.path.join(account_path, file_name)))
|
||||
self.assertEqual("0400", oct(os.stat(os.path.join(
|
||||
account_path, "private_key.json"))[stat.ST_MODE] & 0o777))
|
||||
|
||||
# restore
|
||||
self.assertEqual(self.acc, self.storage.load(self.acc.id))
|
||||
|
||||
def test_find_all(self):
|
||||
self.storage.save(self.acc)
|
||||
self.assertEqual([self.acc], self.storage.find_all())
|
||||
|
||||
def test_find_all_none_empty_list(self):
|
||||
self.assertEqual([], self.storage.find_all())
|
||||
|
||||
def test_find_all_accounts_dir_absent(self):
|
||||
os.rmdir(self.config.accounts_dir)
|
||||
self.assertEqual([], self.storage.find_all())
|
||||
|
||||
def test_find_all_load_skips(self):
|
||||
self.storage.load = mock.MagicMock(
|
||||
side_effect=["x", errors.AccountStorageError, "z"])
|
||||
with mock.patch("letsencrypt.account.os.listdir") as mock_listdir:
|
||||
mock_listdir.return_value = ["x", "y", "z"]
|
||||
self.assertEqual(["x", "z"], self.storage.find_all())
|
||||
|
||||
def test_load_non_existent_raises_error(self):
|
||||
self.assertRaises(errors.AccountNotFound, self.storage.load, "missing")
|
||||
|
||||
def test_load_id_mismatch_raises_error(self):
|
||||
self.storage.save(self.acc)
|
||||
shutil.move(os.path.join(self.config.accounts_dir, self.acc.id),
|
||||
os.path.join(self.config.accounts_dir, "x" + self.acc.id))
|
||||
self.assertRaises(errors.AccountStorageError, self.storage.load,
|
||||
"x" + self.acc.id)
|
||||
|
||||
def test_load_ioerror(self):
|
||||
self.storage.save(self.acc)
|
||||
mock_open = mock.mock_open()
|
||||
mock_open.side_effect = IOError
|
||||
with mock.patch("__builtin__.open", mock_open):
|
||||
self.assertRaises(
|
||||
errors.AccountStorageError, self.storage.load, self.acc.id)
|
||||
|
||||
def test_save_ioerrors(self):
|
||||
mock_open = mock.mock_open()
|
||||
mock_open.side_effect = IOError # TODO: [None, None, IOError]
|
||||
with mock.patch("__builtin__.open", mock_open):
|
||||
self.assertRaises(
|
||||
errors.AccountStorageError, self.storage.save, self.acc)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main() # pragma: no cover
|
||||
@@ -1,89 +0,0 @@
|
||||
"""ACME utilities for testing."""
|
||||
import datetime
|
||||
import itertools
|
||||
|
||||
from acme import challenges
|
||||
from acme import jose
|
||||
from acme import messages
|
||||
|
||||
from letsencrypt.tests import test_util
|
||||
|
||||
|
||||
KEY = test_util.load_rsa_private_key('rsa512_key.pem')
|
||||
|
||||
# Challenges
|
||||
HTTP01 = challenges.HTTP01(
|
||||
token="evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ+PCt92wr+oA")
|
||||
TLSSNI01 = challenges.TLSSNI01(
|
||||
token=jose.b64decode(b"evaGxfADs6pSRb2LAv9IZf17Dt3juxGJyPCt92wrDoA"))
|
||||
DNS = challenges.DNS(token="17817c66b60ce2e4012dfad92657527a")
|
||||
|
||||
CHALLENGES = [HTTP01, TLSSNI01, DNS]
|
||||
|
||||
|
||||
def gen_combos(challbs):
|
||||
"""Generate natural combinations for challbs."""
|
||||
# completing a single DV challenge satisfies the CA
|
||||
return tuple((i,) for i, _ in enumerate(challbs))
|
||||
|
||||
|
||||
def chall_to_challb(chall, status): # pylint: disable=redefined-outer-name
|
||||
"""Return ChallengeBody from Challenge."""
|
||||
kwargs = {
|
||||
"chall": chall,
|
||||
"uri": chall.typ + "_uri",
|
||||
"status": status,
|
||||
}
|
||||
|
||||
if status == messages.STATUS_VALID:
|
||||
kwargs.update({"validated": datetime.datetime.now()})
|
||||
|
||||
return messages.ChallengeBody(**kwargs) # pylint: disable=star-args
|
||||
|
||||
|
||||
# Pending ChallengeBody objects
|
||||
TLSSNI01_P = chall_to_challb(TLSSNI01, messages.STATUS_PENDING)
|
||||
HTTP01_P = chall_to_challb(HTTP01, messages.STATUS_PENDING)
|
||||
DNS_P = chall_to_challb(DNS, messages.STATUS_PENDING)
|
||||
|
||||
CHALLENGES_P = [HTTP01_P, TLSSNI01_P, DNS_P]
|
||||
|
||||
|
||||
def gen_authzr(authz_status, domain, challs, statuses, combos=True):
|
||||
"""Generate an authorization resource.
|
||||
|
||||
:param authz_status: Status object
|
||||
:type authz_status: :class:`acme.messages.Status`
|
||||
:param list challs: Challenge objects
|
||||
:param list statuses: status of each challenge object
|
||||
:param bool combos: Whether or not to add combinations
|
||||
|
||||
"""
|
||||
# pylint: disable=redefined-outer-name
|
||||
challbs = tuple(
|
||||
chall_to_challb(chall, status)
|
||||
for chall, status in itertools.izip(challs, statuses)
|
||||
)
|
||||
authz_kwargs = {
|
||||
"identifier": messages.Identifier(
|
||||
typ=messages.IDENTIFIER_FQDN, value=domain),
|
||||
"challenges": challbs,
|
||||
}
|
||||
if combos:
|
||||
authz_kwargs.update({"combinations": gen_combos(challbs)})
|
||||
if authz_status == messages.STATUS_VALID:
|
||||
authz_kwargs.update({
|
||||
"status": authz_status,
|
||||
"expires": datetime.datetime.now() + datetime.timedelta(days=31),
|
||||
})
|
||||
else:
|
||||
authz_kwargs.update({
|
||||
"status": authz_status,
|
||||
})
|
||||
|
||||
# pylint: disable=star-args
|
||||
return messages.AuthorizationResource(
|
||||
uri="https://trusted.ca/new-authz-resource",
|
||||
new_cert_uri="https://trusted.ca/new-cert",
|
||||
body=messages.Authorization(**authz_kwargs)
|
||||
)
|
||||
@@ -1,422 +0,0 @@
|
||||
"""Tests for letsencrypt.auth_handler."""
|
||||
import functools
|
||||
import logging
|
||||
import unittest
|
||||
|
||||
import mock
|
||||
|
||||
from acme import challenges
|
||||
from acme import client as acme_client
|
||||
from acme import messages
|
||||
|
||||
from letsencrypt import achallenges
|
||||
from letsencrypt import errors
|
||||
from letsencrypt import le_util
|
||||
|
||||
from letsencrypt.tests import acme_util
|
||||
|
||||
|
||||
class ChallengeFactoryTest(unittest.TestCase):
|
||||
# pylint: disable=protected-access
|
||||
|
||||
def setUp(self):
|
||||
from letsencrypt.auth_handler import AuthHandler
|
||||
|
||||
# Account is mocked...
|
||||
self.handler = AuthHandler(None, None, mock.Mock(key="mock_key"))
|
||||
|
||||
self.dom = "test"
|
||||
self.handler.authzr[self.dom] = acme_util.gen_authzr(
|
||||
messages.STATUS_PENDING, self.dom, acme_util.CHALLENGES,
|
||||
[messages.STATUS_PENDING] * 6, False)
|
||||
|
||||
def test_all(self):
|
||||
achalls = self.handler._challenge_factory(
|
||||
self.dom, range(0, len(acme_util.CHALLENGES)))
|
||||
|
||||
self.assertEqual(
|
||||
[achall.chall for achall in achalls], acme_util.CHALLENGES)
|
||||
|
||||
def test_one_tls_sni(self):
|
||||
achalls = self.handler._challenge_factory(self.dom, [1])
|
||||
|
||||
self.assertEqual(
|
||||
[achall.chall for achall in achalls], [acme_util.TLSSNI01])
|
||||
|
||||
def test_unrecognized(self):
|
||||
self.handler.authzr["failure.com"] = acme_util.gen_authzr(
|
||||
messages.STATUS_PENDING, "failure.com",
|
||||
[mock.Mock(chall="chall", typ="unrecognized")],
|
||||
[messages.STATUS_PENDING])
|
||||
|
||||
self.assertRaises(
|
||||
errors.Error, self.handler._challenge_factory, "failure.com", [0])
|
||||
|
||||
|
||||
class GetAuthorizationsTest(unittest.TestCase):
|
||||
"""get_authorizations test.
|
||||
|
||||
This tests everything except for all functions under _poll_challenges.
|
||||
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
from letsencrypt.auth_handler import AuthHandler
|
||||
|
||||
self.mock_auth = mock.MagicMock(name="ApacheConfigurator")
|
||||
|
||||
self.mock_auth.get_chall_pref.return_value = [challenges.TLSSNI01]
|
||||
|
||||
self.mock_auth.perform.side_effect = gen_auth_resp
|
||||
|
||||
self.mock_account = mock.Mock(key=le_util.Key("file_path", "PEM"))
|
||||
self.mock_net = mock.MagicMock(spec=acme_client.Client)
|
||||
|
||||
self.handler = AuthHandler(
|
||||
self.mock_auth, self.mock_net, self.mock_account)
|
||||
|
||||
logging.disable(logging.CRITICAL)
|
||||
|
||||
def tearDown(self):
|
||||
logging.disable(logging.NOTSET)
|
||||
|
||||
@mock.patch("letsencrypt.auth_handler.AuthHandler._poll_challenges")
|
||||
def test_name1_tls_sni_01_1(self, mock_poll):
|
||||
self.mock_net.request_domain_challenges.side_effect = functools.partial(
|
||||
gen_dom_authzr, challs=acme_util.CHALLENGES)
|
||||
|
||||
mock_poll.side_effect = self._validate_all
|
||||
|
||||
authzr = self.handler.get_authorizations(["0"])
|
||||
|
||||
self.assertEqual(self.mock_net.answer_challenge.call_count, 1)
|
||||
|
||||
self.assertEqual(mock_poll.call_count, 1)
|
||||
chall_update = mock_poll.call_args[0][0]
|
||||
self.assertEqual(chall_update.keys(), ["0"])
|
||||
self.assertEqual(len(chall_update.values()), 1)
|
||||
|
||||
self.assertEqual(self.mock_auth.cleanup.call_count, 1)
|
||||
# Test if list first element is TLSSNI01, use typ because it is an achall
|
||||
self.assertEqual(
|
||||
self.mock_auth.cleanup.call_args[0][0][0].typ, "tls-sni-01")
|
||||
|
||||
self.assertEqual(len(authzr), 1)
|
||||
|
||||
@mock.patch("letsencrypt.auth_handler.AuthHandler._poll_challenges")
|
||||
def test_name1_tls_sni_01_1_http_01_1_dns_1(self, mock_poll):
|
||||
self.mock_net.request_domain_challenges.side_effect = functools.partial(
|
||||
gen_dom_authzr, challs=acme_util.CHALLENGES, combos=False)
|
||||
|
||||
mock_poll.side_effect = self._validate_all
|
||||
self.mock_auth.get_chall_pref.return_value.append(challenges.HTTP01)
|
||||
self.mock_auth.get_chall_pref.return_value.append(challenges.DNS)
|
||||
|
||||
authzr = self.handler.get_authorizations(["0"])
|
||||
|
||||
self.assertEqual(self.mock_net.answer_challenge.call_count, 3)
|
||||
|
||||
self.assertEqual(mock_poll.call_count, 1)
|
||||
chall_update = mock_poll.call_args[0][0]
|
||||
self.assertEqual(chall_update.keys(), ["0"])
|
||||
self.assertEqual(len(chall_update.values()), 1)
|
||||
|
||||
self.assertEqual(self.mock_auth.cleanup.call_count, 1)
|
||||
# Test if list first element is TLSSNI01, use typ because it is an achall
|
||||
for achall in self.mock_auth.cleanup.call_args[0][0]:
|
||||
self.assertTrue(achall.typ in ["tls-sni-01", "http-01", "dns"])
|
||||
|
||||
# Length of authorizations list
|
||||
self.assertEqual(len(authzr), 1)
|
||||
|
||||
@mock.patch("letsencrypt.auth_handler.AuthHandler._poll_challenges")
|
||||
def test_name3_tls_sni_01_3(self, mock_poll):
|
||||
self.mock_net.request_domain_challenges.side_effect = functools.partial(
|
||||
gen_dom_authzr, challs=acme_util.CHALLENGES)
|
||||
|
||||
mock_poll.side_effect = self._validate_all
|
||||
|
||||
authzr = self.handler.get_authorizations(["0", "1", "2"])
|
||||
|
||||
self.assertEqual(self.mock_net.answer_challenge.call_count, 3)
|
||||
|
||||
# Check poll call
|
||||
self.assertEqual(mock_poll.call_count, 1)
|
||||
chall_update = mock_poll.call_args[0][0]
|
||||
self.assertEqual(len(chall_update.keys()), 3)
|
||||
self.assertTrue("0" in chall_update.keys())
|
||||
self.assertEqual(len(chall_update["0"]), 1)
|
||||
self.assertTrue("1" in chall_update.keys())
|
||||
self.assertEqual(len(chall_update["1"]), 1)
|
||||
self.assertTrue("2" in chall_update.keys())
|
||||
self.assertEqual(len(chall_update["2"]), 1)
|
||||
|
||||
self.assertEqual(self.mock_auth.cleanup.call_count, 1)
|
||||
|
||||
self.assertEqual(len(authzr), 3)
|
||||
|
||||
def test_perform_failure(self):
|
||||
self.mock_net.request_domain_challenges.side_effect = functools.partial(
|
||||
gen_dom_authzr, challs=acme_util.CHALLENGES)
|
||||
self.mock_auth.perform.side_effect = errors.AuthorizationError
|
||||
|
||||
self.assertRaises(
|
||||
errors.AuthorizationError, self.handler.get_authorizations, ["0"])
|
||||
|
||||
def test_no_domains(self):
|
||||
self.assertRaises(errors.AuthorizationError, self.handler.get_authorizations, [])
|
||||
|
||||
def _validate_all(self, unused_1, unused_2):
|
||||
for dom in self.handler.authzr.keys():
|
||||
azr = self.handler.authzr[dom]
|
||||
self.handler.authzr[dom] = acme_util.gen_authzr(
|
||||
messages.STATUS_VALID,
|
||||
dom,
|
||||
[challb.chall for challb in azr.body.challenges],
|
||||
[messages.STATUS_VALID] * len(azr.body.challenges),
|
||||
azr.body.combinations)
|
||||
|
||||
|
||||
class PollChallengesTest(unittest.TestCase):
|
||||
# pylint: disable=protected-access
|
||||
"""Test poll challenges."""
|
||||
|
||||
def setUp(self):
|
||||
from letsencrypt.auth_handler import challb_to_achall
|
||||
from letsencrypt.auth_handler import AuthHandler
|
||||
|
||||
# Account and network are mocked...
|
||||
self.mock_net = mock.MagicMock()
|
||||
self.handler = AuthHandler(
|
||||
None, self.mock_net, mock.Mock(key="mock_key"))
|
||||
|
||||
self.doms = ["0", "1", "2"]
|
||||
self.handler.authzr[self.doms[0]] = acme_util.gen_authzr(
|
||||
messages.STATUS_PENDING, self.doms[0],
|
||||
[acme_util.HTTP01, acme_util.TLSSNI01],
|
||||
[messages.STATUS_PENDING] * 2, False)
|
||||
|
||||
self.handler.authzr[self.doms[1]] = acme_util.gen_authzr(
|
||||
messages.STATUS_PENDING, self.doms[1],
|
||||
acme_util.CHALLENGES, [messages.STATUS_PENDING] * 3, False)
|
||||
|
||||
self.handler.authzr[self.doms[2]] = acme_util.gen_authzr(
|
||||
messages.STATUS_PENDING, self.doms[2],
|
||||
acme_util.CHALLENGES, [messages.STATUS_PENDING] * 3, False)
|
||||
|
||||
self.chall_update = {}
|
||||
for dom in self.doms:
|
||||
self.chall_update[dom] = [
|
||||
challb_to_achall(challb, mock.Mock(key="dummy_key"), dom)
|
||||
for challb in self.handler.authzr[dom].body.challenges]
|
||||
|
||||
@mock.patch("letsencrypt.auth_handler.time")
|
||||
def test_poll_challenges(self, unused_mock_time):
|
||||
self.mock_net.poll.side_effect = self._mock_poll_solve_one_valid
|
||||
self.handler._poll_challenges(self.chall_update, False)
|
||||
|
||||
for authzr in self.handler.authzr.values():
|
||||
self.assertEqual(authzr.body.status, messages.STATUS_VALID)
|
||||
|
||||
@mock.patch("letsencrypt.auth_handler.time")
|
||||
def test_poll_challenges_failure_best_effort(self, unused_mock_time):
|
||||
self.mock_net.poll.side_effect = self._mock_poll_solve_one_invalid
|
||||
self.handler._poll_challenges(self.chall_update, True)
|
||||
|
||||
for authzr in self.handler.authzr.values():
|
||||
self.assertEqual(authzr.body.status, messages.STATUS_PENDING)
|
||||
|
||||
@mock.patch("letsencrypt.auth_handler.time")
|
||||
@mock.patch("letsencrypt.auth_handler.zope.component.getUtility")
|
||||
def test_poll_challenges_failure(self, unused_mock_time, unused_mock_zope):
|
||||
self.mock_net.poll.side_effect = self._mock_poll_solve_one_invalid
|
||||
self.assertRaises(
|
||||
errors.AuthorizationError, self.handler._poll_challenges,
|
||||
self.chall_update, False)
|
||||
|
||||
@mock.patch("letsencrypt.auth_handler.time")
|
||||
def test_unable_to_find_challenge_status(self, unused_mock_time):
|
||||
from letsencrypt.auth_handler import challb_to_achall
|
||||
self.mock_net.poll.side_effect = self._mock_poll_solve_one_valid
|
||||
self.chall_update[self.doms[0]].append(
|
||||
challb_to_achall(acme_util.DNS_P, "key", self.doms[0]))
|
||||
self.assertRaises(
|
||||
errors.AuthorizationError, self.handler._poll_challenges,
|
||||
self.chall_update, False)
|
||||
|
||||
def test_verify_authzr_failure(self):
|
||||
self.assertRaises(
|
||||
errors.AuthorizationError, self.handler.verify_authzr_complete)
|
||||
|
||||
def _mock_poll_solve_one_valid(self, authzr):
|
||||
# Pending here because my dummy script won't change the full status.
|
||||
# Basically it didn't raise an error and it stopped earlier than
|
||||
# Making all challenges invalid which would make mock_poll_solve_one
|
||||
# change authzr to invalid
|
||||
return self._mock_poll_solve_one_chall(authzr, messages.STATUS_VALID)
|
||||
|
||||
def _mock_poll_solve_one_invalid(self, authzr):
|
||||
return self._mock_poll_solve_one_chall(authzr, messages.STATUS_INVALID)
|
||||
|
||||
def _mock_poll_solve_one_chall(self, authzr, desired_status):
|
||||
# pylint: disable=no-self-use
|
||||
"""Dummy method that solves one chall at a time to desired_status.
|
||||
|
||||
When all are solved.. it changes authzr.status to desired_status
|
||||
|
||||
"""
|
||||
new_challbs = authzr.body.challenges
|
||||
for challb in authzr.body.challenges:
|
||||
if challb.status != desired_status:
|
||||
new_challbs = tuple(
|
||||
challb_temp if challb_temp != challb
|
||||
else acme_util.chall_to_challb(challb.chall, desired_status)
|
||||
for challb_temp in authzr.body.challenges
|
||||
)
|
||||
break
|
||||
|
||||
if all(test_challb.status == desired_status
|
||||
for test_challb in new_challbs):
|
||||
status_ = desired_status
|
||||
else:
|
||||
status_ = authzr.body.status
|
||||
|
||||
new_authzr = messages.AuthorizationResource(
|
||||
uri=authzr.uri,
|
||||
new_cert_uri=authzr.new_cert_uri,
|
||||
body=messages.Authorization(
|
||||
identifier=authzr.body.identifier,
|
||||
challenges=new_challbs,
|
||||
combinations=authzr.body.combinations,
|
||||
status=status_,
|
||||
),
|
||||
)
|
||||
return (new_authzr, "response")
|
||||
|
||||
|
||||
class ChallbToAchallTest(unittest.TestCase):
|
||||
"""Tests for letsencrypt.auth_handler.challb_to_achall."""
|
||||
|
||||
def _call(self, challb):
|
||||
from letsencrypt.auth_handler import challb_to_achall
|
||||
return challb_to_achall(challb, "account_key", "domain")
|
||||
|
||||
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 GenChallengePathTest(unittest.TestCase):
|
||||
"""Tests for letsencrypt.auth_handler.gen_challenge_path.
|
||||
|
||||
.. todo:: Add more tests for dumb_path... depending on what we want to do.
|
||||
|
||||
"""
|
||||
def setUp(self):
|
||||
logging.disable(logging.fatal)
|
||||
|
||||
def tearDown(self):
|
||||
logging.disable(logging.NOTSET)
|
||||
|
||||
@classmethod
|
||||
def _call(cls, challbs, preferences, combinations):
|
||||
from letsencrypt.auth_handler import gen_challenge_path
|
||||
return gen_challenge_path(challbs, preferences, combinations)
|
||||
|
||||
def test_common_case(self):
|
||||
"""Given TLSSNI01 and HTTP01 with appropriate combos."""
|
||||
challbs = (acme_util.TLSSNI01_P, acme_util.HTTP01_P)
|
||||
prefs = [challenges.TLSSNI01, challenges.HTTP01]
|
||||
combos = ((0,), (1,))
|
||||
|
||||
# Smart then trivial dumb path test
|
||||
self.assertEqual(self._call(challbs, prefs, combos), (0,))
|
||||
self.assertTrue(self._call(challbs, prefs, None))
|
||||
# Rearrange order...
|
||||
self.assertEqual(self._call(challbs[::-1], prefs, combos), (1,))
|
||||
self.assertTrue(self._call(challbs[::-1], prefs, None))
|
||||
|
||||
def test_not_supported(self):
|
||||
challbs = (acme_util.DNS_P, acme_util.TLSSNI01_P)
|
||||
prefs = [challenges.TLSSNI01]
|
||||
combos = ((0, 1),)
|
||||
|
||||
# smart path fails because no challs in perfs satisfies combos
|
||||
self.assertRaises(
|
||||
errors.AuthorizationError, self._call, challbs, prefs, combos)
|
||||
# dumb path fails because all challbs are not supported
|
||||
self.assertRaises(
|
||||
errors.AuthorizationError, self._call, challbs, prefs, None)
|
||||
|
||||
|
||||
class ReportFailedChallsTest(unittest.TestCase):
|
||||
"""Tests for letsencrypt.auth_handler._report_failed_challs."""
|
||||
# pylint: disable=protected-access
|
||||
|
||||
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")
|
||||
|
||||
@mock.patch("letsencrypt.auth_handler.zope.component.getUtility")
|
||||
def test_same_error_and_domain(self, mock_zope):
|
||||
from letsencrypt import auth_handler
|
||||
|
||||
auth_handler._report_failed_challs([self.http01, self.tls_sni_same])
|
||||
call_list = mock_zope().add_message.call_args_list
|
||||
self.assertTrue(len(call_list) == 1)
|
||||
self.assertTrue("Domain: example.com\nType: tls\nDetail: detail" in call_list[0][0][0])
|
||||
|
||||
@mock.patch("letsencrypt.auth_handler.zope.component.getUtility")
|
||||
def test_different_errors_and_domains(self, mock_zope):
|
||||
from letsencrypt import auth_handler
|
||||
|
||||
auth_handler._report_failed_challs([self.http01, self.tls_sni_diff])
|
||||
self.assertTrue(mock_zope().add_message.call_count == 2)
|
||||
|
||||
|
||||
def gen_auth_resp(chall_list):
|
||||
"""Generate a dummy authorization response."""
|
||||
return ["%s%s" % (chall.__class__.__name__, chall.domain)
|
||||
for chall in chall_list]
|
||||
|
||||
|
||||
def gen_dom_authzr(domain, unused_new_authzr_uri, challs, combos=True):
|
||||
"""Generates new authzr for domains."""
|
||||
return acme_util.gen_authzr(
|
||||
messages.STATUS_PENDING, domain, challs,
|
||||
[messages.STATUS_PENDING] * len(challs), combos)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main() # pragma: no cover
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,474 +0,0 @@
|
||||
"""Tests for letsencrypt.client."""
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
import OpenSSL
|
||||
import mock
|
||||
|
||||
from acme import jose
|
||||
|
||||
from letsencrypt import account
|
||||
from letsencrypt import errors
|
||||
from letsencrypt import le_util
|
||||
|
||||
from letsencrypt.tests import test_util
|
||||
|
||||
|
||||
KEY = test_util.load_vector("rsa512_key.pem")
|
||||
CSR_SAN = test_util.load_vector("csr-san.der")
|
||||
|
||||
|
||||
class ConfigHelper(object):
|
||||
"""Creates a dummy object to imitate a namespace object
|
||||
|
||||
Example: cfg = ConfigHelper(redirect=True, hsts=False, uir=False)
|
||||
will result in: cfg.redirect=True, cfg.hsts=False, etc.
|
||||
"""
|
||||
def __init__(self, **kwds):
|
||||
self.__dict__.update(kwds)
|
||||
|
||||
class RegisterTest(unittest.TestCase):
|
||||
"""Tests for letsencrypt.client.register."""
|
||||
|
||||
def setUp(self):
|
||||
self.config = mock.MagicMock(rsa_key_size=1024, register_unsafely_without_email=False)
|
||||
self.account_storage = account.AccountMemoryStorage()
|
||||
self.tos_cb = mock.MagicMock()
|
||||
|
||||
def _call(self):
|
||||
from letsencrypt.client import register
|
||||
return register(self.config, self.account_storage, self.tos_cb)
|
||||
|
||||
def test_no_tos(self):
|
||||
with mock.patch("letsencrypt.client.acme_client.Client") as mock_client:
|
||||
mock_client.register().terms_of_service = "http://tos"
|
||||
with mock.patch("letsencrypt.account.report_new_account"):
|
||||
self.tos_cb.return_value = False
|
||||
self.assertRaises(errors.Error, self._call)
|
||||
|
||||
self.tos_cb.return_value = True
|
||||
self._call()
|
||||
|
||||
self.tos_cb = None
|
||||
self._call()
|
||||
|
||||
def test_it(self):
|
||||
with mock.patch("letsencrypt.client.acme_client.Client"):
|
||||
with mock.patch("letsencrypt.account.report_new_account"):
|
||||
self._call()
|
||||
|
||||
@mock.patch("letsencrypt.account.report_new_account")
|
||||
@mock.patch("letsencrypt.client.display_ops.get_email")
|
||||
def test_email_retry(self, _rep, mock_get_email):
|
||||
from acme import messages
|
||||
msg = "DNS problem: NXDOMAIN looking up MX for example.com"
|
||||
mx_err = messages.Error(detail=msg, typ="urn:acme:error:invalidEmail")
|
||||
with mock.patch("letsencrypt.client.acme_client.Client") as mock_client:
|
||||
mock_client().register.side_effect = [mx_err, mock.MagicMock()]
|
||||
self._call()
|
||||
self.assertEqual(mock_get_email.call_count, 1)
|
||||
|
||||
def test_needs_email(self):
|
||||
self.config.email = None
|
||||
self.assertRaises(errors.Error, self._call)
|
||||
|
||||
@mock.patch("letsencrypt.client.logger")
|
||||
def test_without_email(self, mock_logger):
|
||||
with mock.patch("letsencrypt.client.acme_client.Client"):
|
||||
with mock.patch("letsencrypt.account.report_new_account"):
|
||||
self.config.email = None
|
||||
self.config.register_unsafely_without_email = True
|
||||
self.config.dry_run = False
|
||||
self._call()
|
||||
mock_logger.warn.assert_called_once_with(mock.ANY)
|
||||
|
||||
def test_unsupported_error(self):
|
||||
from acme import messages
|
||||
msg = "Test"
|
||||
mx_err = messages.Error(detail=msg, typ="malformed", title="title")
|
||||
with mock.patch("letsencrypt.client.acme_client.Client") as mock_client:
|
||||
mock_client().register.side_effect = [mx_err, mock.MagicMock()]
|
||||
self.assertRaises(messages.Error, self._call)
|
||||
|
||||
class ClientTest(unittest.TestCase):
|
||||
"""Tests for letsencrypt.client.Client."""
|
||||
|
||||
def setUp(self):
|
||||
self.config = mock.MagicMock(
|
||||
no_verify_ssl=False, config_dir="/etc/letsencrypt", allow_subset_of_names=False)
|
||||
# pylint: disable=star-args
|
||||
self.account = mock.MagicMock(**{"key.pem": KEY})
|
||||
self.eg_domains = ["example.com", "www.example.com"]
|
||||
|
||||
from letsencrypt.client import Client
|
||||
with mock.patch("letsencrypt.client.acme_client.Client") as acme:
|
||||
self.acme_client = acme
|
||||
self.acme = acme.return_value = mock.MagicMock()
|
||||
self.client = Client(
|
||||
config=self.config, account_=self.account,
|
||||
auth=None, installer=None)
|
||||
|
||||
def test_init_acme_verify_ssl(self):
|
||||
net = self.acme_client.call_args[1]["net"]
|
||||
self.assertTrue(net.verify_ssl)
|
||||
|
||||
def _mock_obtain_certificate(self):
|
||||
self.client.auth_handler = mock.MagicMock()
|
||||
self.client.auth_handler.get_authorizations.return_value = [None]
|
||||
self.acme.request_issuance.return_value = mock.sentinel.certr
|
||||
self.acme.fetch_chain.return_value = mock.sentinel.chain
|
||||
|
||||
def _check_obtain_certificate(self):
|
||||
self.client.auth_handler.get_authorizations.assert_called_once_with(
|
||||
self.eg_domains,
|
||||
self.config.allow_subset_of_names)
|
||||
|
||||
authzr = self.client.auth_handler.get_authorizations()
|
||||
|
||||
self.acme.request_issuance.assert_called_once_with(
|
||||
jose.ComparableX509(OpenSSL.crypto.load_certificate_request(
|
||||
OpenSSL.crypto.FILETYPE_ASN1, CSR_SAN)),
|
||||
authzr)
|
||||
|
||||
self.acme.fetch_chain.assert_called_once_with(mock.sentinel.certr)
|
||||
|
||||
# FIXME move parts of this to test_cli.py...
|
||||
@mock.patch("letsencrypt.client.logger")
|
||||
def test_obtain_certificate_from_csr(self, mock_logger):
|
||||
self._mock_obtain_certificate()
|
||||
from letsencrypt import cli
|
||||
test_csr = le_util.CSR(form="der", file=None, data=CSR_SAN)
|
||||
mock_parsed_args = mock.MagicMock()
|
||||
# The CLI should believe that this is a certonly request, because
|
||||
# a CSR would not be allowed with other kinds of requests!
|
||||
mock_parsed_args.verb = "certonly"
|
||||
with mock.patch("letsencrypt.client.le_util.CSR") as mock_CSR:
|
||||
mock_CSR.return_value = test_csr
|
||||
mock_parsed_args.domains = self.eg_domains[:]
|
||||
mock_parser = mock.MagicMock(cli.HelpfulArgumentParser)
|
||||
cli.HelpfulArgumentParser.handle_csr(mock_parser, mock_parsed_args)
|
||||
|
||||
# Now provoke an inconsistent domains error...
|
||||
mock_parsed_args.domains.append("hippopotamus.io")
|
||||
self.assertRaises(errors.ConfigurationError,
|
||||
cli.HelpfulArgumentParser.handle_csr, mock_parser, mock_parsed_args)
|
||||
|
||||
authzr = self.client.auth_handler.get_authorizations(self.eg_domains, False)
|
||||
|
||||
self.assertEqual(
|
||||
(mock.sentinel.certr, mock.sentinel.chain),
|
||||
self.client.obtain_certificate_from_csr(
|
||||
self.eg_domains,
|
||||
test_csr,
|
||||
authzr=authzr))
|
||||
# and that the cert was obtained correctly
|
||||
self._check_obtain_certificate()
|
||||
|
||||
# Test for authzr=None
|
||||
self.assertEqual(
|
||||
(mock.sentinel.certr, mock.sentinel.chain),
|
||||
self.client.obtain_certificate_from_csr(
|
||||
self.eg_domains,
|
||||
test_csr,
|
||||
authzr=None))
|
||||
|
||||
self.client.auth_handler.get_authorizations.assert_called_with(
|
||||
self.eg_domains)
|
||||
|
||||
# Test for no auth_handler
|
||||
self.client.auth_handler = None
|
||||
self.assertRaises(
|
||||
errors.Error,
|
||||
self.client.obtain_certificate_from_csr,
|
||||
self.eg_domains,
|
||||
test_csr)
|
||||
mock_logger.warning.assert_called_once_with(mock.ANY)
|
||||
|
||||
@mock.patch("letsencrypt.client.crypto_util")
|
||||
def test_obtain_certificate(self, mock_crypto_util):
|
||||
self._mock_obtain_certificate()
|
||||
|
||||
csr = le_util.CSR(form="der", file=None, data=CSR_SAN)
|
||||
mock_crypto_util.init_save_csr.return_value = csr
|
||||
mock_crypto_util.init_save_key.return_value = mock.sentinel.key
|
||||
domains = ["example.com", "www.example.com"]
|
||||
|
||||
# return_value is essentially set to (None, None) in
|
||||
# _mock_obtain_certificate(), which breaks this test.
|
||||
# Thus fixed by the next line.
|
||||
|
||||
authzr = []
|
||||
|
||||
for domain in domains:
|
||||
authzr.append(
|
||||
mock.MagicMock(
|
||||
body=mock.MagicMock(
|
||||
identifier=mock.MagicMock(
|
||||
value=domain))))
|
||||
|
||||
self.client.auth_handler.get_authorizations.return_value = authzr
|
||||
|
||||
self.assertEqual(
|
||||
self.client.obtain_certificate(domains),
|
||||
(mock.sentinel.certr, mock.sentinel.chain, mock.sentinel.key, csr))
|
||||
|
||||
mock_crypto_util.init_save_key.assert_called_once_with(
|
||||
self.config.rsa_key_size, self.config.key_dir)
|
||||
mock_crypto_util.init_save_csr.assert_called_once_with(
|
||||
mock.sentinel.key, domains, self.config.csr_dir)
|
||||
self._check_obtain_certificate()
|
||||
|
||||
def test_save_certificate(self):
|
||||
certs = ["matching_cert.pem", "cert.pem", "cert-san.pem"]
|
||||
tmp_path = tempfile.mkdtemp()
|
||||
os.chmod(tmp_path, 0o755) # TODO: really??
|
||||
|
||||
certr = mock.MagicMock(body=test_util.load_comparable_cert(certs[0]))
|
||||
chain_cert = [test_util.load_comparable_cert(certs[1]),
|
||||
test_util.load_comparable_cert(certs[2])]
|
||||
candidate_cert_path = os.path.join(tmp_path, "certs", "cert.pem")
|
||||
candidate_chain_path = os.path.join(tmp_path, "chains", "chain.pem")
|
||||
candidate_fullchain_path = os.path.join(tmp_path, "chains", "fullchain.pem")
|
||||
|
||||
cert_path, chain_path, fullchain_path = self.client.save_certificate(
|
||||
certr, chain_cert, candidate_cert_path, candidate_chain_path,
|
||||
candidate_fullchain_path)
|
||||
|
||||
self.assertEqual(os.path.dirname(cert_path),
|
||||
os.path.dirname(candidate_cert_path))
|
||||
self.assertEqual(os.path.dirname(chain_path),
|
||||
os.path.dirname(candidate_chain_path))
|
||||
self.assertEqual(os.path.dirname(fullchain_path),
|
||||
os.path.dirname(candidate_fullchain_path))
|
||||
|
||||
with open(cert_path, "r") as cert_file:
|
||||
cert_contents = cert_file.read()
|
||||
self.assertEqual(cert_contents, test_util.load_vector(certs[0]))
|
||||
|
||||
with open(chain_path, "r") as chain_file:
|
||||
chain_contents = chain_file.read()
|
||||
self.assertEqual(chain_contents, test_util.load_vector(certs[1]) +
|
||||
test_util.load_vector(certs[2]))
|
||||
|
||||
shutil.rmtree(tmp_path)
|
||||
|
||||
def test_deploy_certificate_success(self):
|
||||
self.assertRaises(errors.Error, self.client.deploy_certificate,
|
||||
["foo.bar"], "key", "cert", "chain", "fullchain")
|
||||
|
||||
installer = mock.MagicMock()
|
||||
self.client.installer = installer
|
||||
|
||||
self.client.deploy_certificate(
|
||||
["foo.bar"], "key", "cert", "chain", "fullchain")
|
||||
installer.deploy_cert.assert_called_once_with(
|
||||
cert_path=os.path.abspath("cert"),
|
||||
chain_path=os.path.abspath("chain"),
|
||||
domain='foo.bar',
|
||||
fullchain_path='fullchain',
|
||||
key_path=os.path.abspath("key"))
|
||||
self.assertEqual(installer.save.call_count, 2)
|
||||
installer.restart.assert_called_once_with()
|
||||
|
||||
def test_deploy_certificate_failure(self):
|
||||
installer = mock.MagicMock()
|
||||
self.client.installer = installer
|
||||
|
||||
installer.deploy_cert.side_effect = errors.PluginError
|
||||
self.assertRaises(errors.PluginError, self.client.deploy_certificate,
|
||||
["foo.bar"], "key", "cert", "chain", "fullchain")
|
||||
installer.recovery_routine.assert_called_once_with()
|
||||
|
||||
def test_deploy_certificate_save_failure(self):
|
||||
installer = mock.MagicMock()
|
||||
self.client.installer = installer
|
||||
|
||||
installer.save.side_effect = errors.PluginError
|
||||
self.assertRaises(errors.PluginError, self.client.deploy_certificate,
|
||||
["foo.bar"], "key", "cert", "chain", "fullchain")
|
||||
installer.recovery_routine.assert_called_once_with()
|
||||
|
||||
@mock.patch("letsencrypt.client.zope.component.getUtility")
|
||||
def test_deploy_certificate_restart_failure(self, mock_get_utility):
|
||||
installer = mock.MagicMock()
|
||||
installer.restart.side_effect = [errors.PluginError, None]
|
||||
self.client.installer = installer
|
||||
|
||||
self.assertRaises(errors.PluginError, self.client.deploy_certificate,
|
||||
["foo.bar"], "key", "cert", "chain", "fullchain")
|
||||
self.assertEqual(mock_get_utility().add_message.call_count, 1)
|
||||
installer.rollback_checkpoints.assert_called_once_with()
|
||||
self.assertEqual(installer.restart.call_count, 2)
|
||||
|
||||
@mock.patch("letsencrypt.client.zope.component.getUtility")
|
||||
def test_deploy_certificate_restart_failure2(self, mock_get_utility):
|
||||
installer = mock.MagicMock()
|
||||
installer.restart.side_effect = errors.PluginError
|
||||
installer.rollback_checkpoints.side_effect = errors.ReverterError
|
||||
self.client.installer = installer
|
||||
|
||||
self.assertRaises(errors.PluginError, self.client.deploy_certificate,
|
||||
["foo.bar"], "key", "cert", "chain", "fullchain")
|
||||
self.assertEqual(mock_get_utility().add_message.call_count, 1)
|
||||
installer.rollback_checkpoints.assert_called_once_with()
|
||||
self.assertEqual(installer.restart.call_count, 1)
|
||||
|
||||
@mock.patch("letsencrypt.client.enhancements")
|
||||
def test_enhance_config(self, mock_enhancements):
|
||||
config = ConfigHelper(redirect=True, hsts=False, uir=False)
|
||||
self.assertRaises(errors.Error,
|
||||
self.client.enhance_config, ["foo.bar"], config)
|
||||
|
||||
mock_enhancements.ask.return_value = True
|
||||
installer = mock.MagicMock()
|
||||
self.client.installer = installer
|
||||
installer.supported_enhancements.return_value = ["redirect"]
|
||||
|
||||
self.client.enhance_config(["foo.bar"], config)
|
||||
installer.enhance.assert_called_once_with("foo.bar", "redirect", None)
|
||||
self.assertEqual(installer.save.call_count, 1)
|
||||
installer.restart.assert_called_once_with()
|
||||
|
||||
@mock.patch("letsencrypt.client.enhancements")
|
||||
def test_enhance_config_no_ask(self, mock_enhancements):
|
||||
config = ConfigHelper(redirect=True, hsts=False, uir=False)
|
||||
self.assertRaises(errors.Error,
|
||||
self.client.enhance_config, ["foo.bar"], config)
|
||||
|
||||
mock_enhancements.ask.return_value = True
|
||||
installer = mock.MagicMock()
|
||||
self.client.installer = installer
|
||||
installer.supported_enhancements.return_value = ["redirect", "ensure-http-header"]
|
||||
|
||||
config = ConfigHelper(redirect=True, hsts=False, uir=False)
|
||||
self.client.enhance_config(["foo.bar"], config)
|
||||
installer.enhance.assert_called_with("foo.bar", "redirect", None)
|
||||
|
||||
config = ConfigHelper(redirect=False, hsts=True, uir=False)
|
||||
self.client.enhance_config(["foo.bar"], config)
|
||||
installer.enhance.assert_called_with("foo.bar", "ensure-http-header",
|
||||
"Strict-Transport-Security")
|
||||
|
||||
config = ConfigHelper(redirect=False, hsts=False, uir=True)
|
||||
self.client.enhance_config(["foo.bar"], config)
|
||||
installer.enhance.assert_called_with("foo.bar", "ensure-http-header",
|
||||
"Upgrade-Insecure-Requests")
|
||||
|
||||
self.assertEqual(installer.save.call_count, 3)
|
||||
self.assertEqual(installer.restart.call_count, 3)
|
||||
|
||||
@mock.patch("letsencrypt.client.enhancements")
|
||||
def test_enhance_config_unsupported(self, mock_enhancements):
|
||||
installer = mock.MagicMock()
|
||||
self.client.installer = installer
|
||||
installer.supported_enhancements.return_value = []
|
||||
|
||||
config = ConfigHelper(redirect=None, hsts=True, uir=True)
|
||||
self.client.enhance_config(["foo.bar"], config)
|
||||
installer.enhance.assert_not_called()
|
||||
mock_enhancements.ask.assert_not_called()
|
||||
|
||||
def test_enhance_config_no_installer(self):
|
||||
config = ConfigHelper(redirect=True, hsts=False, uir=False)
|
||||
self.assertRaises(errors.Error,
|
||||
self.client.enhance_config, ["foo.bar"], config)
|
||||
|
||||
@mock.patch("letsencrypt.client.zope.component.getUtility")
|
||||
@mock.patch("letsencrypt.client.enhancements")
|
||||
def test_enhance_config_enhance_failure(self, mock_enhancements,
|
||||
mock_get_utility):
|
||||
mock_enhancements.ask.return_value = True
|
||||
installer = mock.MagicMock()
|
||||
self.client.installer = installer
|
||||
installer.supported_enhancements.return_value = ["redirect"]
|
||||
installer.enhance.side_effect = errors.PluginError
|
||||
|
||||
config = ConfigHelper(redirect=True, hsts=False, uir=False)
|
||||
|
||||
self.assertRaises(errors.PluginError,
|
||||
self.client.enhance_config, ["foo.bar"], config)
|
||||
installer.recovery_routine.assert_called_once_with()
|
||||
self.assertEqual(mock_get_utility().add_message.call_count, 1)
|
||||
|
||||
@mock.patch("letsencrypt.client.zope.component.getUtility")
|
||||
@mock.patch("letsencrypt.client.enhancements")
|
||||
def test_enhance_config_save_failure(self, mock_enhancements,
|
||||
mock_get_utility):
|
||||
mock_enhancements.ask.return_value = True
|
||||
installer = mock.MagicMock()
|
||||
self.client.installer = installer
|
||||
installer.supported_enhancements.return_value = ["redirect"]
|
||||
installer.save.side_effect = errors.PluginError
|
||||
|
||||
config = ConfigHelper(redirect=True, hsts=False, uir=False)
|
||||
|
||||
self.assertRaises(errors.PluginError,
|
||||
self.client.enhance_config, ["foo.bar"], config)
|
||||
installer.recovery_routine.assert_called_once_with()
|
||||
self.assertEqual(mock_get_utility().add_message.call_count, 1)
|
||||
|
||||
@mock.patch("letsencrypt.client.zope.component.getUtility")
|
||||
@mock.patch("letsencrypt.client.enhancements")
|
||||
def test_enhance_config_restart_failure(self, mock_enhancements,
|
||||
mock_get_utility):
|
||||
mock_enhancements.ask.return_value = True
|
||||
installer = mock.MagicMock()
|
||||
self.client.installer = installer
|
||||
installer.supported_enhancements.return_value = ["redirect"]
|
||||
installer.restart.side_effect = [errors.PluginError, None]
|
||||
|
||||
config = ConfigHelper(redirect=True, hsts=False, uir=False)
|
||||
|
||||
self.assertRaises(errors.PluginError,
|
||||
self.client.enhance_config, ["foo.bar"], config)
|
||||
|
||||
self.assertEqual(mock_get_utility().add_message.call_count, 1)
|
||||
installer.rollback_checkpoints.assert_called_once_with()
|
||||
self.assertEqual(installer.restart.call_count, 2)
|
||||
|
||||
@mock.patch("letsencrypt.client.zope.component.getUtility")
|
||||
@mock.patch("letsencrypt.client.enhancements")
|
||||
def test_enhance_config_restart_failure2(self, mock_enhancements,
|
||||
mock_get_utility):
|
||||
mock_enhancements.ask.return_value = True
|
||||
installer = mock.MagicMock()
|
||||
self.client.installer = installer
|
||||
installer.supported_enhancements.return_value = ["redirect"]
|
||||
installer.restart.side_effect = errors.PluginError
|
||||
installer.rollback_checkpoints.side_effect = errors.ReverterError
|
||||
|
||||
config = ConfigHelper(redirect=True, hsts=False, uir=False)
|
||||
|
||||
self.assertRaises(errors.PluginError,
|
||||
self.client.enhance_config, ["foo.bar"], config)
|
||||
self.assertEqual(mock_get_utility().add_message.call_count, 1)
|
||||
installer.rollback_checkpoints.assert_called_once_with()
|
||||
self.assertEqual(installer.restart.call_count, 1)
|
||||
|
||||
|
||||
class RollbackTest(unittest.TestCase):
|
||||
"""Tests for letsencrypt.client.rollback."""
|
||||
|
||||
def setUp(self):
|
||||
self.m_install = mock.MagicMock()
|
||||
|
||||
@classmethod
|
||||
def _call(cls, checkpoints, side_effect):
|
||||
from letsencrypt.client import rollback
|
||||
with mock.patch("letsencrypt.client.plugin_selection.pick_installer") as mpi:
|
||||
mpi.side_effect = side_effect
|
||||
rollback(None, checkpoints, {}, mock.MagicMock())
|
||||
|
||||
def test_no_problems(self):
|
||||
self._call(1, self.m_install)
|
||||
self.assertEqual(self.m_install().rollback_checkpoints.call_count, 1)
|
||||
self.assertEqual(self.m_install().restart.call_count, 1)
|
||||
|
||||
def test_no_installer(self):
|
||||
self._call(1, None) # Just make sure no exceptions are raised
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main() # pragma: no cover
|
||||
@@ -1,41 +0,0 @@
|
||||
"""Tests for letsencrypt.colored_logging."""
|
||||
import logging
|
||||
import unittest
|
||||
|
||||
import six
|
||||
|
||||
from letsencrypt import le_util
|
||||
|
||||
|
||||
class StreamHandlerTest(unittest.TestCase):
|
||||
"""Tests for letsencrypt.colored_logging."""
|
||||
|
||||
def setUp(self):
|
||||
from letsencrypt import colored_logging
|
||||
|
||||
self.stream = six.StringIO()
|
||||
self.stream.isatty = lambda: True
|
||||
self.handler = colored_logging.StreamHandler(self.stream)
|
||||
|
||||
self.logger = logging.getLogger()
|
||||
self.logger.setLevel(logging.DEBUG)
|
||||
self.logger.addHandler(self.handler)
|
||||
|
||||
def test_format(self):
|
||||
msg = 'I did a thing'
|
||||
self.logger.debug(msg)
|
||||
self.assertEqual(self.stream.getvalue(), '{0}\n'.format(msg))
|
||||
|
||||
def test_format_and_red_level(self):
|
||||
msg = 'I did another thing'
|
||||
self.handler.red_level = logging.DEBUG
|
||||
self.logger.debug(msg)
|
||||
|
||||
self.assertEqual(self.stream.getvalue(),
|
||||
'{0}{1}{2}\n'.format(le_util.ANSI_SGR_RED,
|
||||
msg,
|
||||
le_util.ANSI_SGR_RESET))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main() # pragma: no cover
|
||||
@@ -1,135 +0,0 @@
|
||||
"""Tests for letsencrypt.configuration."""
|
||||
import os
|
||||
import unittest
|
||||
|
||||
import mock
|
||||
|
||||
from letsencrypt import errors
|
||||
|
||||
|
||||
class NamespaceConfigTest(unittest.TestCase):
|
||||
"""Tests for letsencrypt.configuration.NamespaceConfig."""
|
||||
|
||||
def setUp(self):
|
||||
self.namespace = mock.MagicMock(
|
||||
config_dir='/tmp/config', work_dir='/tmp/foo', foo='bar',
|
||||
server='https://acme-server.org:443/new',
|
||||
tls_sni_01_port=1234, http01_port=4321)
|
||||
from letsencrypt.configuration import NamespaceConfig
|
||||
self.config = NamespaceConfig(self.namespace)
|
||||
|
||||
def test_init_same_ports(self):
|
||||
self.namespace.tls_sni_01_port = 4321
|
||||
from letsencrypt.configuration import NamespaceConfig
|
||||
self.assertRaises(errors.Error, NamespaceConfig, self.namespace)
|
||||
|
||||
def test_proxy_getattr(self):
|
||||
self.assertEqual(self.config.foo, 'bar')
|
||||
self.assertEqual(self.config.work_dir, '/tmp/foo')
|
||||
|
||||
def test_server_path(self):
|
||||
self.assertEqual(['acme-server.org:443', 'new'],
|
||||
self.config.server_path.split(os.path.sep))
|
||||
|
||||
self.namespace.server = ('http://user:pass@acme.server:443'
|
||||
'/p/a/t/h;parameters?query#fragment')
|
||||
self.assertEqual(['user:pass@acme.server:443', 'p', 'a', 't', 'h'],
|
||||
self.config.server_path.split(os.path.sep))
|
||||
|
||||
@mock.patch('letsencrypt.configuration.constants')
|
||||
def test_dynamic_dirs(self, constants):
|
||||
constants.ACCOUNTS_DIR = 'acc'
|
||||
constants.BACKUP_DIR = 'backups'
|
||||
constants.CSR_DIR = 'csr'
|
||||
|
||||
constants.IN_PROGRESS_DIR = '../p'
|
||||
constants.KEY_DIR = 'keys'
|
||||
constants.TEMP_CHECKPOINT_DIR = 't'
|
||||
|
||||
self.assertEqual(
|
||||
self.config.accounts_dir, '/tmp/config/acc/acme-server.org:443/new')
|
||||
self.assertEqual(self.config.backup_dir, '/tmp/foo/backups')
|
||||
self.assertEqual(self.config.csr_dir, '/tmp/config/csr')
|
||||
self.assertEqual(self.config.in_progress_dir, '/tmp/foo/../p')
|
||||
self.assertEqual(self.config.key_dir, '/tmp/config/keys')
|
||||
self.assertEqual(self.config.temp_checkpoint_dir, '/tmp/foo/t')
|
||||
|
||||
def test_absolute_paths(self):
|
||||
from letsencrypt.configuration import NamespaceConfig
|
||||
|
||||
config_base = "foo"
|
||||
work_base = "bar"
|
||||
logs_base = "baz"
|
||||
|
||||
mock_namespace = mock.MagicMock(spec=['config_dir', 'work_dir',
|
||||
'logs_dir', 'http01_port',
|
||||
'tls_sni_01_port',
|
||||
'domains', 'server'])
|
||||
mock_namespace.config_dir = config_base
|
||||
mock_namespace.work_dir = work_base
|
||||
mock_namespace.logs_dir = logs_base
|
||||
config = NamespaceConfig(mock_namespace)
|
||||
|
||||
self.assertTrue(os.path.isabs(config.config_dir))
|
||||
self.assertEqual(config.config_dir,
|
||||
os.path.join(os.getcwd(), config_base))
|
||||
self.assertTrue(os.path.isabs(config.work_dir))
|
||||
self.assertEqual(config.work_dir,
|
||||
os.path.join(os.getcwd(), work_base))
|
||||
self.assertTrue(os.path.isabs(config.logs_dir))
|
||||
self.assertEqual(config.logs_dir,
|
||||
os.path.join(os.getcwd(), logs_base))
|
||||
self.assertTrue(os.path.isabs(config.accounts_dir))
|
||||
self.assertTrue(os.path.isabs(config.backup_dir))
|
||||
self.assertTrue(os.path.isabs(config.csr_dir))
|
||||
self.assertTrue(os.path.isabs(config.in_progress_dir))
|
||||
self.assertTrue(os.path.isabs(config.key_dir))
|
||||
self.assertTrue(os.path.isabs(config.temp_checkpoint_dir))
|
||||
|
||||
|
||||
class RenewerConfigurationTest(unittest.TestCase):
|
||||
"""Test for letsencrypt.configuration.RenewerConfiguration."""
|
||||
|
||||
def setUp(self):
|
||||
self.namespace = mock.MagicMock(config_dir='/tmp/config')
|
||||
from letsencrypt.configuration import RenewerConfiguration
|
||||
self.config = RenewerConfiguration(self.namespace)
|
||||
|
||||
@mock.patch('letsencrypt.configuration.constants')
|
||||
def test_dynamic_dirs(self, constants):
|
||||
constants.ARCHIVE_DIR = 'a'
|
||||
constants.LIVE_DIR = 'l'
|
||||
constants.RENEWAL_CONFIGS_DIR = 'renewal_configs'
|
||||
constants.RENEWER_CONFIG_FILENAME = 'r.conf'
|
||||
|
||||
self.assertEqual(self.config.archive_dir, '/tmp/config/a')
|
||||
self.assertEqual(self.config.live_dir, '/tmp/config/l')
|
||||
self.assertEqual(
|
||||
self.config.renewal_configs_dir, '/tmp/config/renewal_configs')
|
||||
self.assertEqual(self.config.renewer_config_file, '/tmp/config/r.conf')
|
||||
|
||||
def test_absolute_paths(self):
|
||||
from letsencrypt.configuration import NamespaceConfig
|
||||
from letsencrypt.configuration import RenewerConfiguration
|
||||
|
||||
config_base = "foo"
|
||||
work_base = "bar"
|
||||
logs_base = "baz"
|
||||
|
||||
mock_namespace = mock.MagicMock(spec=['config_dir', 'work_dir',
|
||||
'logs_dir', 'http01_port',
|
||||
'tls_sni_01_port',
|
||||
'domains', 'server'])
|
||||
mock_namespace.config_dir = config_base
|
||||
mock_namespace.work_dir = work_base
|
||||
mock_namespace.logs_dir = logs_base
|
||||
config = RenewerConfiguration(NamespaceConfig(mock_namespace))
|
||||
|
||||
self.assertTrue(os.path.isabs(config.archive_dir))
|
||||
self.assertTrue(os.path.isabs(config.live_dir))
|
||||
self.assertTrue(os.path.isabs(config.renewal_configs_dir))
|
||||
self.assertTrue(os.path.isabs(config.renewer_config_file))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main() # pragma: no cover
|
||||
@@ -1,254 +0,0 @@
|
||||
"""Tests for letsencrypt.crypto_util."""
|
||||
import logging
|
||||
import shutil
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
import OpenSSL
|
||||
import mock
|
||||
import zope.component
|
||||
|
||||
from letsencrypt import errors
|
||||
from letsencrypt import interfaces
|
||||
from letsencrypt.tests import test_util
|
||||
|
||||
|
||||
RSA256_KEY = test_util.load_vector('rsa256_key.pem')
|
||||
RSA512_KEY = test_util.load_vector('rsa512_key.pem')
|
||||
CERT_PATH = test_util.vector_path('cert.pem')
|
||||
CERT = test_util.load_vector('cert.pem')
|
||||
SAN_CERT = test_util.load_vector('cert-san.pem')
|
||||
|
||||
|
||||
class InitSaveKeyTest(unittest.TestCase):
|
||||
"""Tests for letsencrypt.crypto_util.init_save_key."""
|
||||
def setUp(self):
|
||||
logging.disable(logging.CRITICAL)
|
||||
zope.component.provideUtility(
|
||||
mock.Mock(strict_permissions=True), interfaces.IConfig)
|
||||
self.key_dir = tempfile.mkdtemp('key_dir')
|
||||
|
||||
def tearDown(self):
|
||||
logging.disable(logging.NOTSET)
|
||||
shutil.rmtree(self.key_dir)
|
||||
|
||||
@classmethod
|
||||
def _call(cls, key_size, key_dir):
|
||||
from letsencrypt.crypto_util import init_save_key
|
||||
return init_save_key(key_size, key_dir, 'key-letsencrypt.pem')
|
||||
|
||||
@mock.patch('letsencrypt.crypto_util.make_key')
|
||||
def test_success(self, mock_make):
|
||||
mock_make.return_value = 'key_pem'
|
||||
key = self._call(1024, self.key_dir)
|
||||
self.assertEqual(key.pem, 'key_pem')
|
||||
self.assertTrue('key-letsencrypt.pem' in key.file)
|
||||
|
||||
@mock.patch('letsencrypt.crypto_util.make_key')
|
||||
def test_key_failure(self, mock_make):
|
||||
mock_make.side_effect = ValueError
|
||||
self.assertRaises(ValueError, self._call, 431, self.key_dir)
|
||||
|
||||
|
||||
class InitSaveCSRTest(unittest.TestCase):
|
||||
"""Tests for letsencrypt.crypto_util.init_save_csr."""
|
||||
|
||||
def setUp(self):
|
||||
zope.component.provideUtility(
|
||||
mock.Mock(strict_permissions=True), interfaces.IConfig)
|
||||
self.csr_dir = tempfile.mkdtemp('csr_dir')
|
||||
|
||||
def tearDown(self):
|
||||
shutil.rmtree(self.csr_dir)
|
||||
|
||||
@mock.patch('letsencrypt.crypto_util.make_csr')
|
||||
@mock.patch('letsencrypt.crypto_util.le_util.make_or_verify_dir')
|
||||
def test_it(self, unused_mock_verify, mock_csr):
|
||||
from letsencrypt.crypto_util import init_save_csr
|
||||
|
||||
mock_csr.return_value = ('csr_pem', 'csr_der')
|
||||
|
||||
csr = init_save_csr(
|
||||
mock.Mock(pem='dummy_key'), 'example.com', self.csr_dir,
|
||||
'csr-letsencrypt.pem')
|
||||
|
||||
self.assertEqual(csr.data, 'csr_der')
|
||||
self.assertTrue('csr-letsencrypt.pem' in csr.file)
|
||||
|
||||
|
||||
class MakeCSRTest(unittest.TestCase):
|
||||
"""Tests for letsencrypt.crypto_util.make_csr."""
|
||||
|
||||
@classmethod
|
||||
def _call(cls, *args, **kwargs):
|
||||
from letsencrypt.crypto_util import make_csr
|
||||
return make_csr(*args, **kwargs)
|
||||
|
||||
def test_san(self):
|
||||
from letsencrypt.crypto_util import get_sans_from_csr
|
||||
# TODO: Fails for RSA256_KEY
|
||||
csr_pem, csr_der = self._call(
|
||||
RSA512_KEY, ['example.com', 'www.example.com'])
|
||||
self.assertEqual(
|
||||
['example.com', 'www.example.com'], get_sans_from_csr(csr_pem))
|
||||
self.assertEqual(
|
||||
['example.com', 'www.example.com'], get_sans_from_csr(
|
||||
csr_der, OpenSSL.crypto.FILETYPE_ASN1))
|
||||
|
||||
|
||||
class ValidCSRTest(unittest.TestCase):
|
||||
"""Tests for letsencrypt.crypto_util.valid_csr."""
|
||||
|
||||
@classmethod
|
||||
def _call(cls, csr):
|
||||
from letsencrypt.crypto_util import valid_csr
|
||||
return valid_csr(csr)
|
||||
|
||||
def test_valid_pem_true(self):
|
||||
self.assertTrue(self._call(test_util.load_vector('csr.pem')))
|
||||
|
||||
def test_valid_pem_san_true(self):
|
||||
self.assertTrue(self._call(test_util.load_vector('csr-san.pem')))
|
||||
|
||||
def test_valid_der_false(self):
|
||||
self.assertFalse(self._call(test_util.load_vector('csr.der')))
|
||||
|
||||
def test_valid_der_san_false(self):
|
||||
self.assertFalse(self._call(test_util.load_vector('csr-san.der')))
|
||||
|
||||
def test_empty_false(self):
|
||||
self.assertFalse(self._call(''))
|
||||
|
||||
def test_random_false(self):
|
||||
self.assertFalse(self._call('foo bar'))
|
||||
|
||||
|
||||
class CSRMatchesPubkeyTest(unittest.TestCase):
|
||||
"""Tests for letsencrypt.crypto_util.csr_matches_pubkey."""
|
||||
|
||||
@classmethod
|
||||
def _call(cls, *args, **kwargs):
|
||||
from letsencrypt.crypto_util import csr_matches_pubkey
|
||||
return csr_matches_pubkey(*args, **kwargs)
|
||||
|
||||
def test_valid_true(self):
|
||||
self.assertTrue(self._call(
|
||||
test_util.load_vector('csr.pem'), RSA512_KEY))
|
||||
|
||||
def test_invalid_false(self):
|
||||
self.assertFalse(self._call(
|
||||
test_util.load_vector('csr.pem'), RSA256_KEY))
|
||||
|
||||
|
||||
class MakeKeyTest(unittest.TestCase): # pylint: disable=too-few-public-methods
|
||||
"""Tests for letsencrypt.crypto_util.make_key."""
|
||||
|
||||
def test_it(self): # pylint: disable=no-self-use
|
||||
from letsencrypt.crypto_util import make_key
|
||||
# Do not test larger keys as it takes too long.
|
||||
OpenSSL.crypto.load_privatekey(
|
||||
OpenSSL.crypto.FILETYPE_PEM, make_key(1024))
|
||||
|
||||
|
||||
class ValidPrivkeyTest(unittest.TestCase):
|
||||
"""Tests for letsencrypt.crypto_util.valid_privkey."""
|
||||
|
||||
@classmethod
|
||||
def _call(cls, privkey):
|
||||
from letsencrypt.crypto_util import valid_privkey
|
||||
return valid_privkey(privkey)
|
||||
|
||||
def test_valid_true(self):
|
||||
self.assertTrue(self._call(RSA256_KEY))
|
||||
|
||||
def test_empty_false(self):
|
||||
self.assertFalse(self._call(''))
|
||||
|
||||
def test_random_false(self):
|
||||
self.assertFalse(self._call('foo bar'))
|
||||
|
||||
|
||||
class GetSANsFromCertTest(unittest.TestCase):
|
||||
"""Tests for letsencrypt.crypto_util.get_sans_from_cert."""
|
||||
|
||||
@classmethod
|
||||
def _call(cls, *args, **kwargs):
|
||||
from letsencrypt.crypto_util import get_sans_from_cert
|
||||
return get_sans_from_cert(*args, **kwargs)
|
||||
|
||||
def test_single(self):
|
||||
self.assertEqual([], self._call(test_util.load_vector('cert.pem')))
|
||||
|
||||
def test_san(self):
|
||||
self.assertEqual(
|
||||
['example.com', 'www.example.com'],
|
||||
self._call(test_util.load_vector('cert-san.pem')))
|
||||
|
||||
|
||||
class GetSANsFromCSRTest(unittest.TestCase):
|
||||
"""Tests for letsencrypt.crypto_util.get_sans_from_csr."""
|
||||
|
||||
@classmethod
|
||||
def _call(cls, *args, **kwargs):
|
||||
from letsencrypt.crypto_util import get_sans_from_csr
|
||||
return get_sans_from_csr(*args, **kwargs)
|
||||
|
||||
def test_extract_one_san(self):
|
||||
self.assertEqual(['example.com'], self._call(
|
||||
test_util.load_vector('csr.pem')))
|
||||
|
||||
def test_extract_two_sans(self):
|
||||
self.assertEqual(['example.com', 'www.example.com'], self._call(
|
||||
test_util.load_vector('csr-san.pem')))
|
||||
|
||||
def test_extract_six_sans(self):
|
||||
self.assertEqual(self._call(test_util.load_vector('csr-6sans.pem')),
|
||||
["example.com", "example.org", "example.net",
|
||||
"example.info", "subdomain.example.com",
|
||||
"other.subdomain.example.com"])
|
||||
|
||||
def test_parse_non_csr(self):
|
||||
self.assertRaises(OpenSSL.crypto.Error, self._call, "hello there")
|
||||
|
||||
def test_parse_no_sans(self):
|
||||
self.assertEqual(
|
||||
[], self._call(test_util.load_vector('csr-nosans.pem')))
|
||||
|
||||
|
||||
class CertLoaderTest(unittest.TestCase):
|
||||
"""Tests for letsencrypt.crypto_util.pyopenssl_load_certificate"""
|
||||
|
||||
def test_load_valid_cert(self):
|
||||
from letsencrypt.crypto_util import pyopenssl_load_certificate
|
||||
|
||||
cert, file_type = pyopenssl_load_certificate(CERT)
|
||||
self.assertEqual(cert.digest('sha1'),
|
||||
OpenSSL.crypto.load_certificate(file_type, CERT).digest('sha1'))
|
||||
|
||||
def test_load_invalid_cert(self):
|
||||
from letsencrypt.crypto_util import pyopenssl_load_certificate
|
||||
bad_cert_data = CERT.replace("BEGIN CERTIFICATE", "ASDFASDFASDF!!!")
|
||||
self.assertRaises(
|
||||
errors.Error, pyopenssl_load_certificate, bad_cert_data)
|
||||
|
||||
|
||||
class NotBeforeTest(unittest.TestCase):
|
||||
"""Tests for letsencrypt.crypto_util.notBefore"""
|
||||
|
||||
def test_notBefore(self):
|
||||
from letsencrypt.crypto_util import notBefore
|
||||
self.assertEqual(notBefore(CERT_PATH).isoformat(),
|
||||
'2014-12-11T22:34:45+00:00')
|
||||
|
||||
|
||||
class NotAfterTest(unittest.TestCase):
|
||||
"""Tests for letsencrypt.crypto_util.notAfter"""
|
||||
|
||||
def test_notAfter(self):
|
||||
from letsencrypt.crypto_util import notAfter
|
||||
self.assertEqual(notAfter(CERT_PATH).isoformat(),
|
||||
'2014-12-18T22:34:45+00:00')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main() # pragma: no cover
|
||||
@@ -1 +0,0 @@
|
||||
"""Let's Encrypt Display Tests"""
|
||||
@@ -1,102 +0,0 @@
|
||||
"""Test letsencrypt.display.completer."""
|
||||
import os
|
||||
import readline
|
||||
import shutil
|
||||
import string
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
import mock
|
||||
from six.moves import reload_module # pylint: disable=import-error
|
||||
|
||||
|
||||
class CompleterTest(unittest.TestCase):
|
||||
"""Test letsencrypt.display.completer.Completer."""
|
||||
|
||||
def setUp(self):
|
||||
self.temp_dir = tempfile.mkdtemp()
|
||||
|
||||
# directories must end with os.sep for completer to
|
||||
# search inside the directory for possible completions
|
||||
if self.temp_dir[-1] != os.sep:
|
||||
self.temp_dir += os.sep
|
||||
|
||||
self.paths = []
|
||||
# create some files and directories in temp_dir
|
||||
for c in string.ascii_lowercase:
|
||||
path = os.path.join(self.temp_dir, c)
|
||||
self.paths.append(path)
|
||||
if ord(c) % 2:
|
||||
os.mkdir(path)
|
||||
else:
|
||||
with open(path, 'w'):
|
||||
pass
|
||||
|
||||
def tearDown(self):
|
||||
shutil.rmtree(self.temp_dir)
|
||||
|
||||
def test_complete(self):
|
||||
from letsencrypt.display import completer
|
||||
my_completer = completer.Completer()
|
||||
num_paths = len(self.paths)
|
||||
|
||||
for i in range(num_paths):
|
||||
completion = my_completer.complete(self.temp_dir, i)
|
||||
self.assertTrue(completion in self.paths)
|
||||
self.paths.remove(completion)
|
||||
|
||||
self.assertFalse(self.paths)
|
||||
completion = my_completer.complete(self.temp_dir, num_paths)
|
||||
self.assertEqual(completion, None)
|
||||
|
||||
def test_import_error(self):
|
||||
original_readline = sys.modules['readline']
|
||||
sys.modules['readline'] = None
|
||||
|
||||
self.test_context_manager_with_unmocked_readline()
|
||||
|
||||
sys.modules['readline'] = original_readline
|
||||
|
||||
def test_context_manager_with_unmocked_readline(self):
|
||||
from letsencrypt.display import completer
|
||||
reload_module(completer)
|
||||
|
||||
original_completer = readline.get_completer()
|
||||
original_delims = readline.get_completer_delims()
|
||||
|
||||
with completer.Completer():
|
||||
pass
|
||||
|
||||
self.assertEqual(readline.get_completer(), original_completer)
|
||||
self.assertEqual(readline.get_completer_delims(), original_delims)
|
||||
|
||||
@mock.patch('letsencrypt.display.completer.readline', autospec=True)
|
||||
def test_context_manager_libedit(self, mock_readline):
|
||||
mock_readline.__doc__ = 'libedit'
|
||||
self._test_context_manager_with_mock_readline(mock_readline)
|
||||
|
||||
@mock.patch('letsencrypt.display.completer.readline', autospec=True)
|
||||
def test_context_manager_readline(self, mock_readline):
|
||||
mock_readline.__doc__ = 'GNU readline'
|
||||
self._test_context_manager_with_mock_readline(mock_readline)
|
||||
|
||||
def _test_context_manager_with_mock_readline(self, mock_readline):
|
||||
from letsencrypt.display import completer
|
||||
|
||||
mock_readline.parse_and_bind.side_effect = enable_tab_completion
|
||||
|
||||
with completer.Completer():
|
||||
pass
|
||||
|
||||
self.assertTrue(mock_readline.parse_and_bind.called)
|
||||
|
||||
|
||||
def enable_tab_completion(unused_command):
|
||||
"""Enables readline tab completion using the system specific syntax."""
|
||||
libedit = 'libedit' in readline.__doc__
|
||||
command = 'bind ^I rl_complete' if libedit else 'tab: complete'
|
||||
readline.parse_and_bind(command)
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main() # pragma: no cover
|
||||
@@ -1,57 +0,0 @@
|
||||
"""Module for enhancement UI."""
|
||||
import logging
|
||||
import unittest
|
||||
|
||||
import mock
|
||||
|
||||
from letsencrypt import errors
|
||||
from letsencrypt.display import util as display_util
|
||||
|
||||
|
||||
class AskTest(unittest.TestCase):
|
||||
"""Test the ask method."""
|
||||
def setUp(self):
|
||||
logging.disable(logging.CRITICAL)
|
||||
|
||||
def tearDown(self):
|
||||
logging.disable(logging.NOTSET)
|
||||
|
||||
@classmethod
|
||||
def _call(cls, enhancement):
|
||||
from letsencrypt.display.enhancements import ask
|
||||
return ask(enhancement)
|
||||
|
||||
@mock.patch("letsencrypt.display.enhancements.util")
|
||||
def test_redirect(self, mock_util):
|
||||
mock_util().menu.return_value = (display_util.OK, 1)
|
||||
self.assertTrue(self._call("redirect"))
|
||||
|
||||
def test_key_error(self):
|
||||
self.assertRaises(errors.Error, self._call, "unknown_enhancement")
|
||||
|
||||
|
||||
class RedirectTest(unittest.TestCase):
|
||||
"""Test the redirect_by_default method."""
|
||||
@classmethod
|
||||
def _call(cls):
|
||||
from letsencrypt.display.enhancements import redirect_by_default
|
||||
return redirect_by_default()
|
||||
|
||||
@mock.patch("letsencrypt.display.enhancements.util")
|
||||
def test_secure(self, mock_util):
|
||||
mock_util().menu.return_value = (display_util.OK, 1)
|
||||
self.assertTrue(self._call())
|
||||
|
||||
@mock.patch("letsencrypt.display.enhancements.util")
|
||||
def test_cancel(self, mock_util):
|
||||
mock_util().menu.return_value = (display_util.CANCEL, 1)
|
||||
self.assertFalse(self._call())
|
||||
|
||||
@mock.patch("letsencrypt.display.enhancements.util")
|
||||
def test_easy(self, mock_util):
|
||||
mock_util().menu.return_value = (display_util.OK, 0)
|
||||
self.assertFalse(self._call())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main() # pragma: no cover
|
||||
@@ -1,345 +0,0 @@
|
||||
# coding=utf-8
|
||||
"""Test letsencrypt.display.ops."""
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
import mock
|
||||
import zope.component
|
||||
|
||||
from acme import jose
|
||||
from acme import messages
|
||||
|
||||
from letsencrypt import account
|
||||
from letsencrypt import interfaces
|
||||
|
||||
from letsencrypt.display import util as display_util
|
||||
|
||||
from letsencrypt.tests import test_util
|
||||
|
||||
|
||||
KEY = jose.JWKRSA.load(test_util.load_vector("rsa512_key.pem"))
|
||||
|
||||
|
||||
class GetEmailTest(unittest.TestCase):
|
||||
"""Tests for letsencrypt.display.ops.get_email."""
|
||||
|
||||
def setUp(self):
|
||||
mock_display = mock.MagicMock()
|
||||
self.input = mock_display.input
|
||||
zope.component.provideUtility(mock_display, interfaces.IDisplay)
|
||||
|
||||
@classmethod
|
||||
def _call(cls, **kwargs):
|
||||
from letsencrypt.display.ops import get_email
|
||||
return get_email(**kwargs)
|
||||
|
||||
def test_cancel_none(self):
|
||||
self.input.return_value = (display_util.CANCEL, "foo@bar.baz")
|
||||
self.assertTrue(self._call() is None)
|
||||
|
||||
def test_ok_safe(self):
|
||||
self.input.return_value = (display_util.OK, "foo@bar.baz")
|
||||
with mock.patch("letsencrypt.display.ops.le_util.safe_email") as mock_safe_email:
|
||||
mock_safe_email.return_value = True
|
||||
self.assertTrue(self._call() is "foo@bar.baz")
|
||||
|
||||
def test_ok_not_safe(self):
|
||||
self.input.return_value = (display_util.OK, "foo@bar.baz")
|
||||
with mock.patch("letsencrypt.display.ops.le_util.safe_email") as mock_safe_email:
|
||||
mock_safe_email.side_effect = [False, True]
|
||||
self.assertTrue(self._call() is "foo@bar.baz")
|
||||
|
||||
def test_more_and_invalid_flags(self):
|
||||
more_txt = "--register-unsafely-without-email"
|
||||
invalid_txt = "There seem to be problems"
|
||||
base_txt = "Enter email"
|
||||
self.input.return_value = (display_util.OK, "foo@bar.baz")
|
||||
with mock.patch("letsencrypt.display.ops.le_util.safe_email") as mock_safe_email:
|
||||
mock_safe_email.return_value = True
|
||||
self._call()
|
||||
msg = self.input.call_args[0][0]
|
||||
self.assertTrue(more_txt not in msg)
|
||||
self.assertTrue(invalid_txt not in msg)
|
||||
self.assertTrue(base_txt in msg)
|
||||
self._call(more=True)
|
||||
msg = self.input.call_args[0][0]
|
||||
self.assertTrue(more_txt in msg)
|
||||
self.assertTrue(invalid_txt not in msg)
|
||||
self._call(more=True, invalid=True)
|
||||
msg = self.input.call_args[0][0]
|
||||
self.assertTrue(more_txt in msg)
|
||||
self.assertTrue(invalid_txt in msg)
|
||||
self.assertTrue(base_txt in msg)
|
||||
|
||||
|
||||
class ChooseAccountTest(unittest.TestCase):
|
||||
"""Tests for letsencrypt.display.ops.choose_account."""
|
||||
def setUp(self):
|
||||
zope.component.provideUtility(display_util.FileDisplay(sys.stdout))
|
||||
|
||||
self.accounts_dir = tempfile.mkdtemp("accounts")
|
||||
self.account_keys_dir = os.path.join(self.accounts_dir, "keys")
|
||||
os.makedirs(self.account_keys_dir, 0o700)
|
||||
|
||||
self.config = mock.MagicMock(
|
||||
accounts_dir=self.accounts_dir,
|
||||
account_keys_dir=self.account_keys_dir,
|
||||
server="letsencrypt-demo.org")
|
||||
self.key = KEY
|
||||
|
||||
self.acc1 = account.Account(messages.RegistrationResource(
|
||||
uri=None, new_authzr_uri=None, body=messages.Registration.from_data(
|
||||
email="email1@g.com")), self.key)
|
||||
self.acc2 = account.Account(messages.RegistrationResource(
|
||||
uri=None, new_authzr_uri=None, body=messages.Registration.from_data(
|
||||
email="email2@g.com", phone="phone")), self.key)
|
||||
|
||||
@classmethod
|
||||
def _call(cls, accounts):
|
||||
from letsencrypt.display import ops
|
||||
return ops.choose_account(accounts)
|
||||
|
||||
@mock.patch("letsencrypt.display.ops.z_util")
|
||||
def test_one(self, mock_util):
|
||||
mock_util().menu.return_value = (display_util.OK, 0)
|
||||
self.assertEqual(self._call([self.acc1]), self.acc1)
|
||||
|
||||
@mock.patch("letsencrypt.display.ops.z_util")
|
||||
def test_two(self, mock_util):
|
||||
mock_util().menu.return_value = (display_util.OK, 1)
|
||||
self.assertEqual(self._call([self.acc1, self.acc2]), self.acc2)
|
||||
|
||||
@mock.patch("letsencrypt.display.ops.z_util")
|
||||
def test_cancel(self, mock_util):
|
||||
mock_util().menu.return_value = (display_util.CANCEL, 1)
|
||||
self.assertTrue(self._call([self.acc1, self.acc2]) is None)
|
||||
|
||||
|
||||
class GenSSLLabURLs(unittest.TestCase):
|
||||
"""Loose test of _gen_ssl_lab_urls. URL can change easily in the future."""
|
||||
def setUp(self):
|
||||
zope.component.provideUtility(display_util.FileDisplay(sys.stdout))
|
||||
|
||||
@classmethod
|
||||
def _call(cls, domains):
|
||||
from letsencrypt.display.ops import _gen_ssl_lab_urls
|
||||
return _gen_ssl_lab_urls(domains)
|
||||
|
||||
def test_zero(self):
|
||||
self.assertEqual(self._call([]), [])
|
||||
|
||||
def test_two(self):
|
||||
urls = self._call(["eff.org", "umich.edu"])
|
||||
self.assertTrue("eff.org" in urls[0])
|
||||
self.assertTrue("umich.edu" in urls[1])
|
||||
|
||||
|
||||
class GenHttpsNamesTest(unittest.TestCase):
|
||||
"""Test _gen_https_names."""
|
||||
def setUp(self):
|
||||
zope.component.provideUtility(display_util.FileDisplay(sys.stdout))
|
||||
|
||||
@classmethod
|
||||
def _call(cls, domains):
|
||||
from letsencrypt.display.ops import _gen_https_names
|
||||
return _gen_https_names(domains)
|
||||
|
||||
def test_zero(self):
|
||||
self.assertEqual(self._call([]), "")
|
||||
|
||||
def test_one(self):
|
||||
doms = [
|
||||
"example.com",
|
||||
"asllkjsadfljasdf.c",
|
||||
]
|
||||
for dom in doms:
|
||||
self.assertEqual(self._call([dom]), "https://%s" % dom)
|
||||
|
||||
def test_two(self):
|
||||
domains_list = [
|
||||
["foo.bar.org", "bar.org"],
|
||||
["paypal.google.facebook.live.com", "*.zombo.example.com"],
|
||||
]
|
||||
for doms in domains_list:
|
||||
self.assertEqual(
|
||||
self._call(doms),
|
||||
"https://{dom[0]} and https://{dom[1]}".format(dom=doms))
|
||||
|
||||
def test_three(self):
|
||||
doms = ["a.org", "b.org", "c.org"]
|
||||
# We use an oxford comma
|
||||
self.assertEqual(
|
||||
self._call(doms),
|
||||
"https://{dom[0]}, https://{dom[1]}, and https://{dom[2]}".format(
|
||||
dom=doms))
|
||||
|
||||
def test_four(self):
|
||||
doms = ["a.org", "b.org", "c.org", "d.org"]
|
||||
exp = ("https://{dom[0]}, https://{dom[1]}, https://{dom[2]}, "
|
||||
"and https://{dom[3]}".format(dom=doms))
|
||||
|
||||
self.assertEqual(self._call(doms), exp)
|
||||
|
||||
|
||||
class ChooseNamesTest(unittest.TestCase):
|
||||
"""Test choose names."""
|
||||
def setUp(self):
|
||||
zope.component.provideUtility(display_util.FileDisplay(sys.stdout))
|
||||
self.mock_install = mock.MagicMock()
|
||||
|
||||
@classmethod
|
||||
def _call(cls, installer):
|
||||
from letsencrypt.display.ops import choose_names
|
||||
return choose_names(installer)
|
||||
|
||||
@mock.patch("letsencrypt.display.ops._choose_names_manually")
|
||||
def test_no_installer(self, mock_manual):
|
||||
self._call(None)
|
||||
self.assertEqual(mock_manual.call_count, 1)
|
||||
|
||||
@mock.patch("letsencrypt.display.ops.z_util")
|
||||
def test_no_installer_cancel(self, mock_util):
|
||||
mock_util().input.return_value = (display_util.CANCEL, [])
|
||||
self.assertEqual(self._call(None), [])
|
||||
|
||||
@mock.patch("letsencrypt.display.ops.z_util")
|
||||
def test_no_names_choose(self, mock_util):
|
||||
self.mock_install().get_all_names.return_value = set()
|
||||
mock_util().yesno.return_value = True
|
||||
domain = "example.com"
|
||||
mock_util().input.return_value = (display_util.OK, domain)
|
||||
|
||||
actual_doms = self._call(self.mock_install)
|
||||
self.assertEqual(mock_util().input.call_count, 1)
|
||||
self.assertEqual(actual_doms, [domain])
|
||||
|
||||
@mock.patch("letsencrypt.display.ops.z_util")
|
||||
def test_no_names_quit(self, mock_util):
|
||||
self.mock_install().get_all_names.return_value = set()
|
||||
mock_util().yesno.return_value = False
|
||||
|
||||
self.assertEqual(self._call(self.mock_install), [])
|
||||
|
||||
@mock.patch("letsencrypt.display.ops.z_util")
|
||||
def test_filter_names_valid_return(self, mock_util):
|
||||
self.mock_install.get_all_names.return_value = set(["example.com"])
|
||||
mock_util().checklist.return_value = (display_util.OK, ["example.com"])
|
||||
|
||||
names = self._call(self.mock_install)
|
||||
self.assertEqual(names, ["example.com"])
|
||||
self.assertEqual(mock_util().checklist.call_count, 1)
|
||||
|
||||
@mock.patch("letsencrypt.display.ops.z_util")
|
||||
def test_filter_names_nothing_selected(self, mock_util):
|
||||
self.mock_install.get_all_names.return_value = set(["example.com"])
|
||||
mock_util().checklist.return_value = (display_util.OK, [])
|
||||
|
||||
self.assertEqual(self._call(self.mock_install), [])
|
||||
|
||||
@mock.patch("letsencrypt.display.ops.z_util")
|
||||
def test_filter_names_cancel(self, mock_util):
|
||||
self.mock_install.get_all_names.return_value = set(["example.com"])
|
||||
mock_util().checklist.return_value = (
|
||||
display_util.CANCEL, ["example.com"])
|
||||
|
||||
self.assertEqual(self._call(self.mock_install), [])
|
||||
|
||||
def test_get_valid_domains(self):
|
||||
from letsencrypt.display.ops import get_valid_domains
|
||||
all_valid = ["example.com", "second.example.com",
|
||||
"also.example.com"]
|
||||
all_invalid = ["xn--ls8h.tld", "*.wildcard.com", "notFQDN",
|
||||
"uniçodé.com"]
|
||||
two_valid = ["example.com", "xn--ls8h.tld", "also.example.com"]
|
||||
self.assertEqual(get_valid_domains(all_valid), all_valid)
|
||||
self.assertEqual(get_valid_domains(all_invalid), [])
|
||||
self.assertEqual(len(get_valid_domains(two_valid)), 2)
|
||||
|
||||
@mock.patch("letsencrypt.display.ops.z_util")
|
||||
def test_choose_manually(self, mock_util):
|
||||
from letsencrypt.display.ops import _choose_names_manually
|
||||
# No retry
|
||||
mock_util().yesno.return_value = False
|
||||
# IDN and no retry
|
||||
mock_util().input.return_value = (display_util.OK,
|
||||
"uniçodé.com")
|
||||
self.assertEqual(_choose_names_manually(), [])
|
||||
# IDN exception with previous mocks
|
||||
with mock.patch(
|
||||
"letsencrypt.display.ops.display_util.separate_list_input"
|
||||
) as mock_sli:
|
||||
unicode_error = UnicodeEncodeError('mock', u'', 0, 1, 'mock')
|
||||
mock_sli.side_effect = unicode_error
|
||||
self.assertEqual(_choose_names_manually(), [])
|
||||
# Punycode and no retry
|
||||
mock_util().input.return_value = (display_util.OK,
|
||||
"xn--ls8h.tld")
|
||||
self.assertEqual(_choose_names_manually(), [])
|
||||
# non-FQDN and no retry
|
||||
mock_util().input.return_value = (display_util.OK,
|
||||
"notFQDN")
|
||||
self.assertEqual(_choose_names_manually(), [])
|
||||
# Two valid domains
|
||||
mock_util().input.return_value = (display_util.OK,
|
||||
("example.com,"
|
||||
"valid.example.com"))
|
||||
self.assertEqual(_choose_names_manually(),
|
||||
["example.com", "valid.example.com"])
|
||||
# Three iterations
|
||||
mock_util().input.return_value = (display_util.OK,
|
||||
"notFQDN")
|
||||
yn = mock.MagicMock()
|
||||
yn.side_effect = [True, True, False]
|
||||
mock_util().yesno = yn
|
||||
_choose_names_manually()
|
||||
self.assertEqual(mock_util().yesno.call_count, 3)
|
||||
|
||||
|
||||
class SuccessInstallationTest(unittest.TestCase):
|
||||
# pylint: disable=too-few-public-methods
|
||||
"""Test the success installation message."""
|
||||
@classmethod
|
||||
def _call(cls, names):
|
||||
from letsencrypt.display.ops import success_installation
|
||||
success_installation(names)
|
||||
|
||||
@mock.patch("letsencrypt.display.ops.z_util")
|
||||
def test_success_installation(self, mock_util):
|
||||
mock_util().notification.return_value = None
|
||||
names = ["example.com", "abc.com"]
|
||||
|
||||
self._call(names)
|
||||
|
||||
self.assertEqual(mock_util().notification.call_count, 1)
|
||||
arg = mock_util().notification.call_args_list[0][0][0]
|
||||
|
||||
for name in names:
|
||||
self.assertTrue(name in arg)
|
||||
|
||||
|
||||
class SuccessRenewalTest(unittest.TestCase):
|
||||
# pylint: disable=too-few-public-methods
|
||||
"""Test the success renewal message."""
|
||||
@classmethod
|
||||
def _call(cls, names):
|
||||
from letsencrypt.display.ops import success_renewal
|
||||
success_renewal(names, "renew")
|
||||
|
||||
@mock.patch("letsencrypt.display.ops.z_util")
|
||||
def test_success_renewal(self, mock_util):
|
||||
mock_util().notification.return_value = None
|
||||
names = ["example.com", "abc.com"]
|
||||
|
||||
self._call(names)
|
||||
|
||||
self.assertEqual(mock_util().notification.call_count, 1)
|
||||
arg = mock_util().notification.call_args_list[0][0][0]
|
||||
|
||||
for name in names:
|
||||
self.assertTrue(name in arg)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main() # pragma: no cover
|
||||
@@ -1,393 +0,0 @@
|
||||
"""Test :mod:`letsencrypt.display.util`."""
|
||||
import os
|
||||
import unittest
|
||||
|
||||
import mock
|
||||
|
||||
import letsencrypt.errors as errors
|
||||
|
||||
from letsencrypt.display import util as display_util
|
||||
|
||||
|
||||
CHOICES = [("First", "Description1"), ("Second", "Description2")]
|
||||
TAGS = ["tag1", "tag2", "tag3"]
|
||||
TAGS_CHOICES = [("1", "tag1"), ("2", "tag2"), ("3", "tag3")]
|
||||
|
||||
|
||||
class NcursesDisplayTest(unittest.TestCase):
|
||||
"""Test ncurses display.
|
||||
|
||||
Since this is mostly a wrapper, it might be more helpful to test the
|
||||
actual dialog boxes. The test file located in ./tests/display.py
|
||||
(relative to the root of the repository) will actually display the
|
||||
various boxes but requires the user to do the verification. If
|
||||
something seems amiss please use that test script to debug it, the
|
||||
automatic tests rely on too much mocking.
|
||||
|
||||
"""
|
||||
def setUp(self):
|
||||
super(NcursesDisplayTest, self).setUp()
|
||||
self.displayer = display_util.NcursesDisplay()
|
||||
|
||||
self.default_menu_options = {
|
||||
"choices": CHOICES,
|
||||
"ok_label": "OK",
|
||||
"cancel_label": "Cancel",
|
||||
"help_button": False,
|
||||
"help_label": "",
|
||||
"width": display_util.WIDTH,
|
||||
"height": display_util.HEIGHT,
|
||||
"menu_height": display_util.HEIGHT - 6,
|
||||
}
|
||||
|
||||
@mock.patch("letsencrypt.display.util.dialog.Dialog.msgbox")
|
||||
def test_notification(self, mock_msgbox):
|
||||
"""Kind of worthless... one liner."""
|
||||
self.displayer.notification("message")
|
||||
self.assertEqual(mock_msgbox.call_count, 1)
|
||||
|
||||
@mock.patch("letsencrypt.display.util.dialog.Dialog.menu")
|
||||
def test_menu_tag_and_desc(self, mock_menu):
|
||||
mock_menu.return_value = (display_util.OK, "First")
|
||||
|
||||
ret = self.displayer.menu("Message", CHOICES)
|
||||
mock_menu.assert_called_with("Message", **self.default_menu_options)
|
||||
|
||||
self.assertEqual(ret, (display_util.OK, 0))
|
||||
|
||||
@mock.patch("letsencrypt.display.util.dialog.Dialog.menu")
|
||||
def test_menu_tag_and_desc_cancel(self, mock_menu):
|
||||
mock_menu.return_value = (display_util.CANCEL, "")
|
||||
|
||||
ret = self.displayer.menu("Message", CHOICES)
|
||||
|
||||
mock_menu.assert_called_with("Message", **self.default_menu_options)
|
||||
|
||||
self.assertEqual(ret, (display_util.CANCEL, -1))
|
||||
|
||||
@mock.patch("letsencrypt.display.util.dialog.Dialog.menu")
|
||||
def test_menu_desc_only(self, mock_menu):
|
||||
mock_menu.return_value = (display_util.OK, "1")
|
||||
|
||||
ret = self.displayer.menu("Message", TAGS, help_label="More Info")
|
||||
|
||||
self.default_menu_options.update(
|
||||
choices=TAGS_CHOICES, help_button=True, help_label="More Info")
|
||||
mock_menu.assert_called_with("Message", **self.default_menu_options)
|
||||
|
||||
self.assertEqual(ret, (display_util.OK, 0))
|
||||
|
||||
@mock.patch("letsencrypt.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", TAGS, help_label="More Info")
|
||||
|
||||
self.assertEqual(ret, (display_util.HELP, 1))
|
||||
|
||||
@mock.patch("letsencrypt.display.util.dialog.Dialog.menu")
|
||||
def test_menu_desc_only_cancel(self, mock_menu):
|
||||
mock_menu.return_value = (display_util.CANCEL, "")
|
||||
|
||||
ret = self.displayer.menu("Message", TAGS, help_label="More Info")
|
||||
|
||||
self.assertEqual(ret, (display_util.CANCEL, -1))
|
||||
|
||||
@mock.patch("letsencrypt.display.util."
|
||||
"dialog.Dialog.inputbox")
|
||||
def test_input(self, mock_input):
|
||||
self.displayer.input("message")
|
||||
self.assertEqual(mock_input.call_count, 1)
|
||||
|
||||
@mock.patch("letsencrypt.display.util.dialog.Dialog.yesno")
|
||||
def test_yesno(self, mock_yesno):
|
||||
mock_yesno.return_value = display_util.OK
|
||||
|
||||
self.assertTrue(self.displayer.yesno("message"))
|
||||
|
||||
mock_yesno.assert_called_with(
|
||||
"message", display_util.HEIGHT, display_util.WIDTH,
|
||||
yes_label="Yes", no_label="No")
|
||||
|
||||
@mock.patch("letsencrypt.display.util."
|
||||
"dialog.Dialog.checklist")
|
||||
def test_checklist(self, mock_checklist):
|
||||
self.displayer.checklist("message", TAGS)
|
||||
|
||||
choices = [
|
||||
(TAGS[0], "", True),
|
||||
(TAGS[1], "", True),
|
||||
(TAGS[2], "", True),
|
||||
]
|
||||
mock_checklist.assert_called_with(
|
||||
"message", width=display_util.WIDTH, height=display_util.HEIGHT,
|
||||
choices=choices)
|
||||
|
||||
@mock.patch("letsencrypt.display.util.dialog.Dialog.dselect")
|
||||
def test_directory_select(self, mock_dselect):
|
||||
self.displayer.directory_select("message")
|
||||
self.assertEqual(mock_dselect.call_count, 1)
|
||||
|
||||
|
||||
class FileOutputDisplayTest(unittest.TestCase):
|
||||
"""Test stdout display.
|
||||
|
||||
Most of this class has to deal with visual output. In order to test how the
|
||||
functions look to a user, uncomment the test_visual function.
|
||||
|
||||
"""
|
||||
def setUp(self):
|
||||
super(FileOutputDisplayTest, self).setUp()
|
||||
self.mock_stdout = mock.MagicMock()
|
||||
self.displayer = display_util.FileDisplay(self.mock_stdout)
|
||||
|
||||
def test_notification_no_pause(self):
|
||||
self.displayer.notification("message", 10, False)
|
||||
string = self.mock_stdout.write.call_args[0][0]
|
||||
|
||||
self.assertTrue("message" in string)
|
||||
|
||||
def test_notification_pause(self):
|
||||
with mock.patch("__builtin__.raw_input", return_value="enter"):
|
||||
self.displayer.notification("message")
|
||||
|
||||
self.assertTrue("message" in self.mock_stdout.write.call_args[0][0])
|
||||
|
||||
@mock.patch("letsencrypt.display.util."
|
||||
"FileDisplay._get_valid_int_ans")
|
||||
def test_menu(self, mock_ans):
|
||||
mock_ans.return_value = (display_util.OK, 1)
|
||||
ret = self.displayer.menu("message", CHOICES)
|
||||
self.assertEqual(ret, (display_util.OK, 0))
|
||||
|
||||
def test_input_cancel(self):
|
||||
with mock.patch("__builtin__.raw_input", return_value="c"):
|
||||
code, _ = self.displayer.input("message")
|
||||
|
||||
self.assertTrue(code, display_util.CANCEL)
|
||||
|
||||
def test_input_normal(self):
|
||||
with mock.patch("__builtin__.raw_input", return_value="domain.com"):
|
||||
code, input_ = self.displayer.input("message")
|
||||
|
||||
self.assertEqual(code, display_util.OK)
|
||||
self.assertEqual(input_, "domain.com")
|
||||
|
||||
def test_yesno(self):
|
||||
with mock.patch("__builtin__.raw_input", return_value="Yes"):
|
||||
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", 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"))
|
||||
|
||||
@mock.patch("letsencrypt.display.util.FileDisplay.input")
|
||||
def test_checklist_valid(self, mock_input):
|
||||
mock_input.return_value = (display_util.OK, "2 1")
|
||||
code, tag_list = self.displayer.checklist("msg", TAGS)
|
||||
self.assertEqual(
|
||||
(code, set(tag_list)), (display_util.OK, set(["tag1", "tag2"])))
|
||||
|
||||
@mock.patch("letsencrypt.display.util.FileDisplay.input")
|
||||
def test_checklist_miss_valid(self, mock_input):
|
||||
mock_input.side_effect = [
|
||||
(display_util.OK, "10"),
|
||||
(display_util.OK, "tag1 please"),
|
||||
(display_util.OK, "1")
|
||||
]
|
||||
|
||||
ret = self.displayer.checklist("msg", TAGS)
|
||||
self.assertEqual(ret, (display_util.OK, ["tag1"]))
|
||||
|
||||
@mock.patch("letsencrypt.display.util.FileDisplay.input")
|
||||
def test_checklist_miss_quit(self, mock_input):
|
||||
mock_input.side_effect = [
|
||||
(display_util.OK, "10"),
|
||||
(display_util.CANCEL, "1")
|
||||
]
|
||||
ret = self.displayer.checklist("msg", TAGS)
|
||||
self.assertEqual(ret, (display_util.CANCEL, []))
|
||||
|
||||
def test_scrub_checklist_input_valid(self):
|
||||
# pylint: disable=protected-access
|
||||
indices = [
|
||||
["1"],
|
||||
["1", "2", "1"],
|
||||
["2", "3"],
|
||||
]
|
||||
exp = [
|
||||
set(["tag1"]),
|
||||
set(["tag1", "tag2"]),
|
||||
set(["tag2", "tag3"]),
|
||||
]
|
||||
for i, list_ in enumerate(indices):
|
||||
set_tags = set(
|
||||
self.displayer._scrub_checklist_input(list_, TAGS))
|
||||
self.assertEqual(set_tags, exp[i])
|
||||
|
||||
@mock.patch("letsencrypt.display.util.FileDisplay.input")
|
||||
def test_directory_select(self, mock_input):
|
||||
message = "msg"
|
||||
result = (display_util.OK, "/var/www/html",)
|
||||
mock_input.return_value = result
|
||||
|
||||
self.assertEqual(self.displayer.directory_select(message), result)
|
||||
mock_input.assert_called_once_with(message)
|
||||
|
||||
def test_scrub_checklist_input_invalid(self):
|
||||
# pylint: disable=protected-access
|
||||
indices = [
|
||||
["0"],
|
||||
["4"],
|
||||
["tag1"],
|
||||
["1", "tag1"],
|
||||
["2", "o"]
|
||||
]
|
||||
for list_ in indices:
|
||||
self.assertEqual(
|
||||
self.displayer._scrub_checklist_input(list_, TAGS), [])
|
||||
|
||||
def test_print_menu(self):
|
||||
# pylint: disable=protected-access
|
||||
# This is purely cosmetic... just make sure there aren't any exceptions
|
||||
self.displayer._print_menu("msg", CHOICES)
|
||||
self.displayer._print_menu("msg", TAGS)
|
||||
|
||||
def test_wrap_lines(self):
|
||||
# pylint: disable=protected-access
|
||||
msg = ("This is just a weak test{0}"
|
||||
"This function is only meant to be for easy viewing{0}"
|
||||
"Test a really really really really really really really really "
|
||||
"really really really really long line...".format(os.linesep))
|
||||
text = display_util._wrap_lines(msg)
|
||||
|
||||
self.assertEqual(text.count(os.linesep), 3)
|
||||
|
||||
def test_get_valid_int_ans_valid(self):
|
||||
# pylint: disable=protected-access
|
||||
with mock.patch("__builtin__.raw_input", return_value="1"):
|
||||
self.assertEqual(
|
||||
self.displayer._get_valid_int_ans(1), (display_util.OK, 1))
|
||||
ans = "2"
|
||||
with mock.patch("__builtin__.raw_input", return_value=ans):
|
||||
self.assertEqual(
|
||||
self.displayer._get_valid_int_ans(3),
|
||||
(display_util.OK, int(ans)))
|
||||
|
||||
def test_get_valid_int_ans_invalid(self):
|
||||
# pylint: disable=protected-access
|
||||
answers = [
|
||||
["0", "c"],
|
||||
["4", "one", "C"],
|
||||
["c"],
|
||||
]
|
||||
for ans in answers:
|
||||
with mock.patch("__builtin__.raw_input", side_effect=ans):
|
||||
self.assertEqual(
|
||||
self.displayer._get_valid_int_ans(3),
|
||||
(display_util.CANCEL, -1))
|
||||
|
||||
|
||||
class NoninteractiveDisplayTest(unittest.TestCase):
|
||||
"""Test non-interactive display.
|
||||
|
||||
These tests are pretty easy!
|
||||
|
||||
"""
|
||||
def setUp(self):
|
||||
super(NoninteractiveDisplayTest, self).setUp()
|
||||
self.mock_stdout = mock.MagicMock()
|
||||
self.displayer = display_util.NoninteractiveDisplay(self.mock_stdout)
|
||||
|
||||
def test_notification_no_pause(self):
|
||||
self.displayer.notification("message", 10)
|
||||
string = self.mock_stdout.write.call_args[0][0]
|
||||
|
||||
self.assertTrue("message" in string)
|
||||
|
||||
def test_input(self):
|
||||
d = "an incomputable value"
|
||||
ret = self.displayer.input("message", default=d)
|
||||
self.assertEqual(ret, (display_util.OK, d))
|
||||
self.assertRaises(errors.MissingCommandlineFlag, self.displayer.input, "message")
|
||||
|
||||
def test_menu(self):
|
||||
ret = self.displayer.menu("message", CHOICES, default=1)
|
||||
self.assertEqual(ret, (display_util.OK, 1))
|
||||
self.assertRaises(errors.MissingCommandlineFlag, self.displayer.menu, "message", CHOICES)
|
||||
|
||||
def test_yesno(self):
|
||||
d = False
|
||||
ret = self.displayer.yesno("message", default=d)
|
||||
self.assertEqual(ret, d)
|
||||
self.assertRaises(errors.MissingCommandlineFlag, self.displayer.yesno, "message")
|
||||
|
||||
def test_checklist(self):
|
||||
d = [1, 3]
|
||||
ret = self.displayer.checklist("message", TAGS, default=d)
|
||||
self.assertEqual(ret, (display_util.OK, d))
|
||||
self.assertRaises(errors.MissingCommandlineFlag, self.displayer.checklist, "message", TAGS)
|
||||
|
||||
def test_directory_select(self):
|
||||
default = "/var/www/html"
|
||||
expected = (display_util.OK, default)
|
||||
actual = self.displayer.directory_select("msg", default)
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
self.assertRaises(
|
||||
errors.MissingCommandlineFlag, self.displayer.directory_select, "msg")
|
||||
|
||||
|
||||
class SeparateListInputTest(unittest.TestCase):
|
||||
"""Test Module functions."""
|
||||
def setUp(self):
|
||||
self.exp = ["a", "b", "c", "test"]
|
||||
|
||||
@classmethod
|
||||
def _call(cls, input_):
|
||||
from letsencrypt.display.util import separate_list_input
|
||||
return separate_list_input(input_)
|
||||
|
||||
def test_commas(self):
|
||||
self.assertEqual(self._call("a,b,c,test"), self.exp)
|
||||
|
||||
def test_spaces(self):
|
||||
self.assertEqual(self._call("a b c test"), self.exp)
|
||||
|
||||
def test_both(self):
|
||||
self.assertEqual(self._call("a, b, c, test"), self.exp)
|
||||
|
||||
def test_mess(self):
|
||||
actual = [
|
||||
self._call(" a , b c \t test"),
|
||||
self._call(",a, ,, , b c test "),
|
||||
self._call(",,,,, , a b,,, , c,test"),
|
||||
]
|
||||
|
||||
for act in actual:
|
||||
self.assertEqual(act, self.exp)
|
||||
|
||||
|
||||
class PlaceParensTest(unittest.TestCase):
|
||||
@classmethod
|
||||
def _call(cls, label): # pylint: disable=protected-access
|
||||
from letsencrypt.display.util import _parens_around_char
|
||||
return _parens_around_char(label)
|
||||
|
||||
def test_single_letter(self):
|
||||
self.assertEqual("(a)", self._call("a"))
|
||||
|
||||
def test_multiple(self):
|
||||
self.assertEqual("(L)abel", self._call("Label"))
|
||||
self.assertEqual("(y)es please", self._call("yes please"))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main() # pragma: no cover
|
||||
@@ -1,71 +0,0 @@
|
||||
"""Tests for letsencrypt.error_handler."""
|
||||
import signal
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
import mock
|
||||
|
||||
|
||||
class ErrorHandlerTest(unittest.TestCase):
|
||||
"""Tests for letsencrypt.error_handler."""
|
||||
|
||||
def setUp(self):
|
||||
from letsencrypt import error_handler
|
||||
|
||||
self.init_func = mock.MagicMock()
|
||||
self.init_args = set((42,))
|
||||
self.init_kwargs = {'foo': 'bar'}
|
||||
self.handler = error_handler.ErrorHandler(self.init_func,
|
||||
*self.init_args,
|
||||
**self.init_kwargs)
|
||||
# pylint: disable=protected-access
|
||||
self.signals = error_handler._SIGNALS
|
||||
|
||||
def test_context_manager(self):
|
||||
try:
|
||||
with self.handler:
|
||||
raise ValueError
|
||||
except ValueError:
|
||||
pass
|
||||
self.init_func.assert_called_once_with(*self.init_args,
|
||||
**self.init_kwargs)
|
||||
|
||||
@mock.patch('letsencrypt.error_handler.os')
|
||||
@mock.patch('letsencrypt.error_handler.signal')
|
||||
def test_signal_handler(self, mock_signal, mock_os):
|
||||
# pylint: disable=protected-access
|
||||
mock_signal.getsignal.return_value = signal.SIG_DFL
|
||||
self.handler.set_signal_handlers()
|
||||
signal_handler = self.handler._signal_handler
|
||||
for signum in self.signals:
|
||||
mock_signal.signal.assert_any_call(signum, signal_handler)
|
||||
|
||||
signum = self.signals[0]
|
||||
signal_handler(signum, None)
|
||||
self.init_func.assert_called_once_with(*self.init_args,
|
||||
**self.init_kwargs)
|
||||
mock_os.kill.assert_called_once_with(mock_os.getpid(), signum)
|
||||
|
||||
self.handler.reset_signal_handlers()
|
||||
for signum in self.signals:
|
||||
mock_signal.signal.assert_any_call(signum, signal.SIG_DFL)
|
||||
|
||||
def test_bad_recovery(self):
|
||||
bad_func = mock.MagicMock(side_effect=[ValueError])
|
||||
self.handler.register(bad_func)
|
||||
self.handler.call_registered()
|
||||
self.init_func.assert_called_once_with(*self.init_args,
|
||||
**self.init_kwargs)
|
||||
bad_func.assert_called_once_with()
|
||||
|
||||
def test_sysexit_ignored(self):
|
||||
try:
|
||||
with self.handler:
|
||||
sys.exit(0)
|
||||
except SystemExit:
|
||||
pass
|
||||
self.assertFalse(self.init_func.called)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main() # pragma: no cover
|
||||
@@ -1,44 +0,0 @@
|
||||
"""Tests for letsencrypt.errors."""
|
||||
import unittest
|
||||
|
||||
import mock
|
||||
|
||||
from acme import messages
|
||||
|
||||
from letsencrypt import achallenges
|
||||
from letsencrypt.tests import acme_util
|
||||
|
||||
|
||||
class FaiiledChallengesTest(unittest.TestCase):
|
||||
"""Tests for letsencrypt.errors.FailedChallenges."""
|
||||
|
||||
def setUp(self):
|
||||
from letsencrypt.errors import FailedChallenges
|
||||
self.error = FailedChallenges(set([achallenges.DNS(
|
||||
domain="example.com", challb=messages.ChallengeBody(
|
||||
chall=acme_util.DNS, uri=None,
|
||||
error=messages.Error(typ="tls", detail="detail")))]))
|
||||
|
||||
def test_str(self):
|
||||
self.assertTrue(str(self.error).startswith(
|
||||
"Failed authorization procedure. example.com (dns): tls"))
|
||||
|
||||
|
||||
class StandaloneBindErrorTest(unittest.TestCase):
|
||||
"""Tests for letsencrypt.errors.StandaloneBindError."""
|
||||
|
||||
def setUp(self):
|
||||
from letsencrypt.errors import StandaloneBindError
|
||||
self.error = StandaloneBindError(mock.sentinel.error, 1234)
|
||||
|
||||
def test_instance_args(self):
|
||||
self.assertEqual(mock.sentinel.error, self.error.socket_error)
|
||||
self.assertEqual(1234, self.error.port)
|
||||
|
||||
def test_str(self):
|
||||
self.assertTrue(str(self.error).startswith(
|
||||
"Problem binding to port 1234: "))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main() # pragma: no cover
|
||||
@@ -1,101 +0,0 @@
|
||||
"""Tests for hooks.py"""
|
||||
# pylint: disable=protected-access
|
||||
|
||||
import os
|
||||
import unittest
|
||||
|
||||
import mock
|
||||
|
||||
from letsencrypt import errors
|
||||
from letsencrypt import hooks
|
||||
|
||||
class HookTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
pass
|
||||
|
||||
def tearDown(self):
|
||||
pass
|
||||
|
||||
@mock.patch('letsencrypt.hooks._prog')
|
||||
def test_validate_hooks(self, mock_prog):
|
||||
config = mock.MagicMock(pre_hook="", post_hook="ls -lR", renew_hook="uptime")
|
||||
hooks.validate_hooks(config)
|
||||
self.assertEqual(mock_prog.call_count, 2)
|
||||
self.assertEqual(mock_prog.call_args_list[1][0][0], 'uptime')
|
||||
self.assertEqual(mock_prog.call_args_list[0][0][0], 'ls')
|
||||
mock_prog.return_value = None
|
||||
config = mock.MagicMock(pre_hook="explodinator", post_hook="", renew_hook="")
|
||||
self.assertRaises(errors.HookCommandNotFound, hooks.validate_hooks, config)
|
||||
|
||||
@mock.patch('letsencrypt.hooks._is_exe')
|
||||
def test_which(self, mock_is_exe):
|
||||
mock_is_exe.return_value = True
|
||||
self.assertEqual(hooks._which("/path/to/something"), "/path/to/something")
|
||||
|
||||
with mock.patch.dict('os.environ', {"PATH": "/floop:/fleep"}):
|
||||
mock_is_exe.return_value = True
|
||||
self.assertEqual(hooks._which("pingify"), "/floop/pingify")
|
||||
mock_is_exe.return_value = False
|
||||
self.assertEqual(hooks._which("pingify"), None)
|
||||
self.assertEqual(hooks._which("/path/to/something"), None)
|
||||
|
||||
@mock.patch('letsencrypt.hooks._which')
|
||||
def test_prog(self, mockwhich):
|
||||
mockwhich.return_value = "/very/very/funky"
|
||||
self.assertEqual(hooks._prog("funky"), "funky")
|
||||
mockwhich.return_value = None
|
||||
self.assertEqual(hooks._prog("funky"), None)
|
||||
|
||||
def _test_a_hook(self, config, hook_function, calls_expected):
|
||||
with mock.patch('letsencrypt.hooks.logger') as mock_logger:
|
||||
mock_logger.warning = mock.MagicMock()
|
||||
with mock.patch('letsencrypt.hooks._run_hook') as mock_run_hook:
|
||||
hook_function(config)
|
||||
hook_function(config)
|
||||
self.assertEqual(mock_run_hook.call_count, calls_expected)
|
||||
return mock_logger.warning
|
||||
|
||||
def test_pre_hook(self):
|
||||
config = mock.MagicMock(pre_hook="true")
|
||||
self._test_a_hook(config, hooks.pre_hook, 1)
|
||||
config = mock.MagicMock(pre_hook="")
|
||||
self._test_a_hook(config, hooks.pre_hook, 0)
|
||||
|
||||
def test_post_hook(self):
|
||||
config = mock.MagicMock(post_hook="true", verb="splonk")
|
||||
self._test_a_hook(config, hooks.post_hook, 2)
|
||||
config = mock.MagicMock(post_hook="true", verb="renew")
|
||||
self._test_a_hook(config, hooks.post_hook, 0)
|
||||
|
||||
def test_renew_hook(self):
|
||||
with mock.patch.dict('os.environ', {}):
|
||||
domains = ["a", "b"]
|
||||
lineage = "thing"
|
||||
rhook = lambda x: hooks.renew_hook(x, domains, lineage)
|
||||
|
||||
config = mock.MagicMock(renew_hook="true", dry_run=False)
|
||||
self._test_a_hook(config, rhook, 2)
|
||||
self.assertEqual(os.environ["RENEWED_DOMAINS"], "a b")
|
||||
self.assertEqual(os.environ["RENEWED_LINEAGE"], "thing")
|
||||
|
||||
config = mock.MagicMock(renew_hook="true", dry_run=True)
|
||||
mock_warn = self._test_a_hook(config, rhook, 0)
|
||||
self.assertEqual(mock_warn.call_count, 2)
|
||||
|
||||
@mock.patch('letsencrypt.hooks.Popen')
|
||||
def test_run_hook(self, mock_popen):
|
||||
with mock.patch('letsencrypt.hooks.logger.error') as mock_error:
|
||||
mock_cmd = mock.MagicMock()
|
||||
mock_cmd.returncode = 1
|
||||
mock_cmd.communicate.return_value = ("", "")
|
||||
mock_popen.return_value = mock_cmd
|
||||
hooks._run_hook("ls")
|
||||
self.assertEqual(mock_error.call_count, 1)
|
||||
with mock.patch('letsencrypt.hooks.logger.error') as mock_error:
|
||||
mock_cmd.communicate.return_value = ("", "thing")
|
||||
hooks._run_hook("ls")
|
||||
self.assertEqual(mock_error.call_count, 2)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main() # pragma: no cover
|
||||
@@ -1,343 +0,0 @@
|
||||
"""Tests for letsencrypt.le_util."""
|
||||
import argparse
|
||||
import errno
|
||||
import os
|
||||
import shutil
|
||||
import stat
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
import mock
|
||||
import six
|
||||
|
||||
from letsencrypt import errors
|
||||
|
||||
|
||||
class RunScriptTest(unittest.TestCase):
|
||||
"""Tests for letsencrypt.le_util.run_script."""
|
||||
@classmethod
|
||||
def _call(cls, params):
|
||||
from letsencrypt.le_util import run_script
|
||||
return run_script(params)
|
||||
|
||||
@mock.patch("letsencrypt.le_util.subprocess.Popen")
|
||||
def test_default(self, mock_popen):
|
||||
"""These will be changed soon enough with reload."""
|
||||
mock_popen().returncode = 0
|
||||
mock_popen().communicate.return_value = ("stdout", "stderr")
|
||||
|
||||
out, err = self._call(["test"])
|
||||
self.assertEqual(out, "stdout")
|
||||
self.assertEqual(err, "stderr")
|
||||
|
||||
@mock.patch("letsencrypt.le_util.subprocess.Popen")
|
||||
def test_bad_process(self, mock_popen):
|
||||
mock_popen.side_effect = OSError
|
||||
|
||||
self.assertRaises(errors.SubprocessError, self._call, ["test"])
|
||||
|
||||
@mock.patch("letsencrypt.le_util.subprocess.Popen")
|
||||
def test_failure(self, mock_popen):
|
||||
mock_popen().communicate.return_value = ("", "")
|
||||
mock_popen().returncode = 1
|
||||
|
||||
self.assertRaises(errors.SubprocessError, self._call, ["test"])
|
||||
|
||||
|
||||
class ExeExistsTest(unittest.TestCase):
|
||||
"""Tests for letsencrypt.le_util.exe_exists."""
|
||||
|
||||
@classmethod
|
||||
def _call(cls, exe):
|
||||
from letsencrypt.le_util import exe_exists
|
||||
return exe_exists(exe)
|
||||
|
||||
@mock.patch("letsencrypt.le_util.os.path.isfile")
|
||||
@mock.patch("letsencrypt.le_util.os.access")
|
||||
def test_full_path(self, mock_access, mock_isfile):
|
||||
mock_access.return_value = True
|
||||
mock_isfile.return_value = True
|
||||
self.assertTrue(self._call("/path/to/exe"))
|
||||
|
||||
@mock.patch("letsencrypt.le_util.os.path.isfile")
|
||||
@mock.patch("letsencrypt.le_util.os.access")
|
||||
def test_on_path(self, mock_access, mock_isfile):
|
||||
mock_access.return_value = True
|
||||
mock_isfile.return_value = True
|
||||
self.assertTrue(self._call("exe"))
|
||||
|
||||
@mock.patch("letsencrypt.le_util.os.path.isfile")
|
||||
@mock.patch("letsencrypt.le_util.os.access")
|
||||
def test_not_found(self, mock_access, mock_isfile):
|
||||
mock_access.return_value = False
|
||||
mock_isfile.return_value = True
|
||||
self.assertFalse(self._call("exe"))
|
||||
|
||||
|
||||
class MakeOrVerifyDirTest(unittest.TestCase):
|
||||
"""Tests for letsencrypt.le_util.make_or_verify_dir.
|
||||
|
||||
Note that it is not possible to test for a wrong directory owner,
|
||||
as this testing script would have to be run as root.
|
||||
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
self.root_path = tempfile.mkdtemp()
|
||||
self.path = os.path.join(self.root_path, "foo")
|
||||
os.mkdir(self.path, 0o400)
|
||||
|
||||
self.uid = os.getuid()
|
||||
|
||||
def tearDown(self):
|
||||
shutil.rmtree(self.root_path, ignore_errors=True)
|
||||
|
||||
def _call(self, directory, mode):
|
||||
from letsencrypt.le_util import make_or_verify_dir
|
||||
return make_or_verify_dir(directory, mode, self.uid, strict=True)
|
||||
|
||||
def test_creates_dir_when_missing(self):
|
||||
path = os.path.join(self.root_path, "bar")
|
||||
self._call(path, 0o650)
|
||||
self.assertTrue(os.path.isdir(path))
|
||||
self.assertEqual(stat.S_IMODE(os.stat(path).st_mode), 0o650)
|
||||
|
||||
def test_existing_correct_mode_does_not_fail(self):
|
||||
self._call(self.path, 0o400)
|
||||
self.assertEqual(stat.S_IMODE(os.stat(self.path).st_mode), 0o400)
|
||||
|
||||
def test_existing_wrong_mode_fails(self):
|
||||
self.assertRaises(errors.Error, self._call, self.path, 0o600)
|
||||
|
||||
def test_reraises_os_error(self):
|
||||
with mock.patch.object(os, "makedirs") as makedirs:
|
||||
makedirs.side_effect = OSError()
|
||||
self.assertRaises(OSError, self._call, "bar", 12312312)
|
||||
|
||||
|
||||
class CheckPermissionsTest(unittest.TestCase):
|
||||
"""Tests for letsencrypt.le_util.check_permissions.
|
||||
|
||||
Note that it is not possible to test for a wrong file owner,
|
||||
as this testing script would have to be run as root.
|
||||
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
_, self.path = tempfile.mkstemp()
|
||||
self.uid = os.getuid()
|
||||
|
||||
def tearDown(self):
|
||||
os.remove(self.path)
|
||||
|
||||
def _call(self, mode):
|
||||
from letsencrypt.le_util import check_permissions
|
||||
return check_permissions(self.path, mode, self.uid)
|
||||
|
||||
def test_ok_mode(self):
|
||||
os.chmod(self.path, 0o600)
|
||||
self.assertTrue(self._call(0o600))
|
||||
|
||||
def test_wrong_mode(self):
|
||||
os.chmod(self.path, 0o400)
|
||||
self.assertFalse(self._call(0o600))
|
||||
|
||||
|
||||
class UniqueFileTest(unittest.TestCase):
|
||||
"""Tests for letsencrypt.le_util.unique_file."""
|
||||
|
||||
def setUp(self):
|
||||
self.root_path = tempfile.mkdtemp()
|
||||
self.default_name = os.path.join(self.root_path, "foo.txt")
|
||||
|
||||
def tearDown(self):
|
||||
shutil.rmtree(self.root_path, ignore_errors=True)
|
||||
|
||||
def _call(self, mode=0o600):
|
||||
from letsencrypt.le_util import unique_file
|
||||
return unique_file(self.default_name, mode)
|
||||
|
||||
def test_returns_fd_for_writing(self):
|
||||
fd, name = self._call()
|
||||
fd.write("bar")
|
||||
fd.close()
|
||||
self.assertEqual(open(name).read(), "bar")
|
||||
|
||||
def test_right_mode(self):
|
||||
self.assertEqual(0o700, os.stat(self._call(0o700)[1]).st_mode & 0o777)
|
||||
self.assertEqual(0o100, os.stat(self._call(0o100)[1]).st_mode & 0o777)
|
||||
|
||||
def test_default_exists(self):
|
||||
name1 = self._call()[1] # create 0000_foo.txt
|
||||
name2 = self._call()[1]
|
||||
name3 = self._call()[1]
|
||||
|
||||
self.assertNotEqual(name1, name2)
|
||||
self.assertNotEqual(name1, name3)
|
||||
self.assertNotEqual(name2, name3)
|
||||
|
||||
self.assertEqual(os.path.dirname(name1), self.root_path)
|
||||
self.assertEqual(os.path.dirname(name2), self.root_path)
|
||||
self.assertEqual(os.path.dirname(name3), self.root_path)
|
||||
|
||||
basename1 = os.path.basename(name2)
|
||||
self.assertTrue(basename1.endswith("foo.txt"))
|
||||
basename2 = os.path.basename(name2)
|
||||
self.assertTrue(basename2.endswith("foo.txt"))
|
||||
basename3 = os.path.basename(name3)
|
||||
self.assertTrue(basename3.endswith("foo.txt"))
|
||||
|
||||
|
||||
class UniqueLineageNameTest(unittest.TestCase):
|
||||
"""Tests for letsencrypt.le_util.unique_lineage_name."""
|
||||
|
||||
def setUp(self):
|
||||
self.root_path = tempfile.mkdtemp()
|
||||
|
||||
def tearDown(self):
|
||||
shutil.rmtree(self.root_path, ignore_errors=True)
|
||||
|
||||
def _call(self, filename, mode=0o777):
|
||||
from letsencrypt.le_util import unique_lineage_name
|
||||
return unique_lineage_name(self.root_path, filename, mode)
|
||||
|
||||
def test_basic(self):
|
||||
f, path = self._call("wow")
|
||||
self.assertTrue(isinstance(f, file))
|
||||
self.assertEqual(os.path.join(self.root_path, "wow.conf"), path)
|
||||
|
||||
def test_multiple(self):
|
||||
for _ in xrange(10):
|
||||
f, name = self._call("wow")
|
||||
self.assertTrue(isinstance(f, file))
|
||||
self.assertTrue(isinstance(name, str))
|
||||
self.assertTrue("wow-0009.conf" in name)
|
||||
|
||||
@mock.patch("letsencrypt.le_util.os.fdopen")
|
||||
def test_failure(self, mock_fdopen):
|
||||
err = OSError("whoops")
|
||||
err.errno = errno.EIO
|
||||
mock_fdopen.side_effect = err
|
||||
self.assertRaises(OSError, self._call, "wow")
|
||||
|
||||
@mock.patch("letsencrypt.le_util.os.fdopen")
|
||||
def test_subsequent_failure(self, mock_fdopen):
|
||||
self._call("wow")
|
||||
err = OSError("whoops")
|
||||
err.errno = errno.EIO
|
||||
mock_fdopen.side_effect = err
|
||||
self.assertRaises(OSError, self._call, "wow")
|
||||
|
||||
|
||||
class SafelyRemoveTest(unittest.TestCase):
|
||||
"""Tests for letsencrypt.le_util.safely_remove."""
|
||||
|
||||
def setUp(self):
|
||||
self.tmp = tempfile.mkdtemp()
|
||||
self.path = os.path.join(self.tmp, "foo")
|
||||
|
||||
def tearDown(self):
|
||||
shutil.rmtree(self.tmp)
|
||||
|
||||
def _call(self):
|
||||
from letsencrypt.le_util import safely_remove
|
||||
return safely_remove(self.path)
|
||||
|
||||
def test_exists(self):
|
||||
with open(self.path, "w"):
|
||||
pass # just create the file
|
||||
self._call()
|
||||
self.assertFalse(os.path.exists(self.path))
|
||||
|
||||
def test_missing(self):
|
||||
self._call()
|
||||
# no error, yay!
|
||||
self.assertFalse(os.path.exists(self.path))
|
||||
|
||||
@mock.patch("letsencrypt.le_util.os.remove")
|
||||
def test_other_error_passthrough(self, mock_remove):
|
||||
mock_remove.side_effect = OSError
|
||||
self.assertRaises(OSError, self._call)
|
||||
|
||||
|
||||
class SafeEmailTest(unittest.TestCase):
|
||||
"""Test safe_email."""
|
||||
@classmethod
|
||||
def _call(cls, addr):
|
||||
from letsencrypt.le_util import safe_email
|
||||
return safe_email(addr)
|
||||
|
||||
def test_valid_emails(self):
|
||||
addrs = [
|
||||
"letsencrypt@letsencrypt.org",
|
||||
"tbd.ade@gmail.com",
|
||||
"abc_def.jdk@hotmail.museum",
|
||||
]
|
||||
for addr in addrs:
|
||||
self.assertTrue(self._call(addr), "%s failed." % addr)
|
||||
|
||||
def test_invalid_emails(self):
|
||||
addrs = [
|
||||
"letsencrypt@letsencrypt..org",
|
||||
".tbd.ade@gmail.com",
|
||||
"~/abc_def.jdk@hotmail.museum",
|
||||
]
|
||||
for addr in addrs:
|
||||
self.assertFalse(self._call(addr), "%s failed." % addr)
|
||||
|
||||
|
||||
class AddDeprecatedArgumentTest(unittest.TestCase):
|
||||
"""Test add_deprecated_argument."""
|
||||
def setUp(self):
|
||||
self.parser = argparse.ArgumentParser()
|
||||
|
||||
def _call(self, argument_name, nargs):
|
||||
from letsencrypt.le_util import add_deprecated_argument
|
||||
|
||||
add_deprecated_argument(self.parser.add_argument, argument_name, nargs)
|
||||
|
||||
def test_warning_no_arg(self):
|
||||
self._call("--old-option", 0)
|
||||
stderr = self._get_argparse_warnings(["--old-option"])
|
||||
self.assertTrue("--old-option is deprecated" in stderr)
|
||||
|
||||
def test_warning_with_arg(self):
|
||||
self._call("--old-option", 1)
|
||||
stderr = self._get_argparse_warnings(["--old-option", "42"])
|
||||
self.assertTrue("--old-option is deprecated" in stderr)
|
||||
|
||||
def _get_argparse_warnings(self, args):
|
||||
stderr = six.StringIO()
|
||||
with mock.patch("letsencrypt.le_util.sys.stderr", new=stderr):
|
||||
self.parser.parse_args(args)
|
||||
return stderr.getvalue()
|
||||
|
||||
def test_help(self):
|
||||
self._call("--old-option", 2)
|
||||
stdout = six.StringIO()
|
||||
with mock.patch("letsencrypt.le_util.sys.stdout", new=stdout):
|
||||
try:
|
||||
self.parser.parse_args(["-h"])
|
||||
except SystemExit:
|
||||
pass
|
||||
self.assertTrue("--old-option" not in stdout.getvalue())
|
||||
|
||||
|
||||
class EnforceDomainSanityTest(unittest.TestCase):
|
||||
"""Test enforce_domain_sanity."""
|
||||
|
||||
def _call(self, domain):
|
||||
from letsencrypt.le_util import enforce_domain_sanity
|
||||
return enforce_domain_sanity(domain)
|
||||
|
||||
def test_nonascii_str(self):
|
||||
self.assertRaises(errors.ConfigurationError, self._call,
|
||||
u"eichh\u00f6rnchen.example.com".encode("utf-8"))
|
||||
|
||||
def test_nonascii_unicode(self):
|
||||
self.assertRaises(errors.ConfigurationError, self._call,
|
||||
u"eichh\u00f6rnchen.example.com")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main() # pragma: no cover
|
||||
@@ -1,48 +0,0 @@
|
||||
"""Tests for letsencrypt.log."""
|
||||
import logging
|
||||
import unittest
|
||||
|
||||
import mock
|
||||
|
||||
|
||||
class DialogHandlerTest(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.d = mock.MagicMock()
|
||||
|
||||
from letsencrypt.log import DialogHandler
|
||||
self.handler = DialogHandler(height=2, width=6, d=self.d)
|
||||
self.handler.PADDING_HEIGHT = 2
|
||||
self.handler.PADDING_WIDTH = 4
|
||||
|
||||
def test_adds_padding(self):
|
||||
self.handler.emit(logging.makeLogRecord({}))
|
||||
self.d.infobox.assert_called_once_with(mock.ANY, 4, 10)
|
||||
|
||||
def test_args_in_msg_get_replaced(self):
|
||||
assert len('123456') <= self.handler.width
|
||||
self.handler.emit(logging.makeLogRecord(
|
||||
{'msg': '123%s', 'args': (456,)}))
|
||||
self.d.infobox.assert_called_once_with('123456', mock.ANY, mock.ANY)
|
||||
|
||||
def test_wraps_nospace_is_greedy(self):
|
||||
assert len('1234567') > self.handler.width
|
||||
self.handler.emit(logging.makeLogRecord({'msg': '1234567'}))
|
||||
self.d.infobox.assert_called_once_with('123456\n7', mock.ANY, mock.ANY)
|
||||
|
||||
def test_wraps_at_whitespace(self):
|
||||
assert len('123 567') > self.handler.width
|
||||
self.handler.emit(logging.makeLogRecord({'msg': '123 567'}))
|
||||
self.d.infobox.assert_called_once_with('123\n567', mock.ANY, mock.ANY)
|
||||
|
||||
def test_only_last_lines_are_printed(self):
|
||||
assert len('a\nb\nc'.split()) > self.handler.height
|
||||
self.handler.emit(logging.makeLogRecord({'msg': 'a\n\nb\nc'}))
|
||||
self.d.infobox.assert_called_once_with('b\nc', mock.ANY, mock.ANY)
|
||||
|
||||
def test_non_str(self):
|
||||
self.handler.emit(logging.makeLogRecord({'msg': {'foo': 'bar'}}))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main() # pragma: no cover
|
||||
@@ -1,52 +0,0 @@
|
||||
"""Tests for letsencrypt.notify."""
|
||||
import socket
|
||||
import unittest
|
||||
|
||||
import mock
|
||||
|
||||
|
||||
class NotifyTests(unittest.TestCase):
|
||||
"""Tests for the notifier."""
|
||||
|
||||
@mock.patch("letsencrypt.notify.smtplib.LMTP")
|
||||
def test_smtp_success(self, mock_lmtp):
|
||||
from letsencrypt.notify import notify
|
||||
lmtp_obj = mock.MagicMock()
|
||||
mock_lmtp.return_value = lmtp_obj
|
||||
self.assertTrue(notify("Goose", "auntrhody@example.com",
|
||||
"The old grey goose is dead."))
|
||||
self.assertEqual(lmtp_obj.connect.call_count, 1)
|
||||
self.assertEqual(lmtp_obj.sendmail.call_count, 1)
|
||||
|
||||
@mock.patch("letsencrypt.notify.smtplib.LMTP")
|
||||
@mock.patch("letsencrypt.notify.subprocess.Popen")
|
||||
def test_smtp_failure(self, mock_popen, mock_lmtp):
|
||||
from letsencrypt.notify import notify
|
||||
lmtp_obj = mock.MagicMock()
|
||||
mock_lmtp.return_value = lmtp_obj
|
||||
lmtp_obj.sendmail.side_effect = socket.error(17)
|
||||
proc = mock.MagicMock()
|
||||
mock_popen.return_value = proc
|
||||
self.assertTrue(notify("Goose", "auntrhody@example.com",
|
||||
"The old grey goose is dead."))
|
||||
self.assertEqual(lmtp_obj.sendmail.call_count, 1)
|
||||
self.assertEqual(proc.communicate.call_count, 1)
|
||||
|
||||
@mock.patch("letsencrypt.notify.smtplib.LMTP")
|
||||
@mock.patch("letsencrypt.notify.subprocess.Popen")
|
||||
def test_everything_fails(self, mock_popen, mock_lmtp):
|
||||
from letsencrypt.notify import notify
|
||||
lmtp_obj = mock.MagicMock()
|
||||
mock_lmtp.return_value = lmtp_obj
|
||||
lmtp_obj.sendmail.side_effect = socket.error(17)
|
||||
proc = mock.MagicMock()
|
||||
mock_popen.return_value = proc
|
||||
proc.communicate.side_effect = OSError("What we have here is a "
|
||||
"failure to communicate.")
|
||||
self.assertFalse(notify("Goose", "auntrhody@example.com",
|
||||
"The old grey goose is dead."))
|
||||
self.assertEqual(lmtp_obj.sendmail.call_count, 1)
|
||||
self.assertEqual(proc.communicate.call_count, 1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main() # pragma: no cover
|
||||
@@ -1,95 +0,0 @@
|
||||
"""Tests for letsencrypt.reporter."""
|
||||
import mock
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
import six
|
||||
|
||||
|
||||
class ReporterTest(unittest.TestCase):
|
||||
"""Tests for letsencrypt.reporter.Reporter."""
|
||||
|
||||
def setUp(self):
|
||||
from letsencrypt import reporter
|
||||
self.reporter = reporter.Reporter(mock.MagicMock(quiet=False))
|
||||
|
||||
self.old_stdout = sys.stdout
|
||||
sys.stdout = six.StringIO()
|
||||
|
||||
def tearDown(self):
|
||||
sys.stdout = self.old_stdout
|
||||
|
||||
def test_multiline_message(self):
|
||||
self.reporter.add_message("Line 1\nLine 2", self.reporter.LOW_PRIORITY)
|
||||
self.reporter.atexit_print_messages()
|
||||
output = sys.stdout.getvalue()
|
||||
self.assertTrue("Line 1\n" in output)
|
||||
self.assertTrue("Line 2" in output)
|
||||
|
||||
def test_tty_print_empty(self):
|
||||
sys.stdout.isatty = lambda: True
|
||||
self.test_no_tty_print_empty()
|
||||
|
||||
def test_no_tty_print_empty(self):
|
||||
self.reporter.print_messages()
|
||||
self.assertEqual(sys.stdout.getvalue(), "")
|
||||
try:
|
||||
raise ValueError
|
||||
except ValueError:
|
||||
self.reporter.print_messages()
|
||||
self.assertEqual(sys.stdout.getvalue(), "")
|
||||
|
||||
def test_atexit_print_messages(self):
|
||||
self._add_messages()
|
||||
self.reporter.atexit_print_messages()
|
||||
output = sys.stdout.getvalue()
|
||||
self.assertTrue("IMPORTANT NOTES:" in output)
|
||||
self.assertTrue("High" in output)
|
||||
self.assertTrue("Med" in output)
|
||||
self.assertTrue("Low" in output)
|
||||
|
||||
def test_tty_successful_exit(self):
|
||||
sys.stdout.isatty = lambda: True
|
||||
self._successful_exit_common()
|
||||
|
||||
def test_no_tty_successful_exit(self):
|
||||
self._successful_exit_common()
|
||||
|
||||
def test_tty_unsuccessful_exit(self):
|
||||
sys.stdout.isatty = lambda: True
|
||||
self._unsuccessful_exit_common()
|
||||
|
||||
def test_no_tty_unsuccessful_exit(self):
|
||||
self._unsuccessful_exit_common()
|
||||
|
||||
def _successful_exit_common(self):
|
||||
self._add_messages()
|
||||
self.reporter.print_messages()
|
||||
output = sys.stdout.getvalue()
|
||||
self.assertTrue("IMPORTANT NOTES:" in output)
|
||||
self.assertTrue("High" in output)
|
||||
self.assertTrue("Med" in output)
|
||||
self.assertTrue("Low" in output)
|
||||
|
||||
def _unsuccessful_exit_common(self):
|
||||
self._add_messages()
|
||||
try:
|
||||
raise ValueError
|
||||
except ValueError:
|
||||
self.reporter.print_messages()
|
||||
output = sys.stdout.getvalue()
|
||||
self.assertTrue("IMPORTANT NOTES:" in output)
|
||||
self.assertTrue("High" in output)
|
||||
self.assertTrue("Med" not in output)
|
||||
self.assertTrue("Low" not in output)
|
||||
|
||||
def _add_messages(self):
|
||||
self.reporter.add_message("High", self.reporter.HIGH_PRIORITY)
|
||||
self.reporter.add_message(
|
||||
"Med", self.reporter.MEDIUM_PRIORITY, on_crash=False)
|
||||
self.reporter.add_message(
|
||||
"Low", self.reporter.LOW_PRIORITY, on_crash=False)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main() # pragma: no cover
|
||||
@@ -1,493 +0,0 @@
|
||||
"""Test letsencrypt.reverter."""
|
||||
import csv
|
||||
import itertools
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
import mock
|
||||
|
||||
from letsencrypt import errors
|
||||
|
||||
|
||||
class ReverterCheckpointLocalTest(unittest.TestCase):
|
||||
# pylint: disable=too-many-instance-attributes, too-many-public-methods
|
||||
"""Test the Reverter Class."""
|
||||
def setUp(self):
|
||||
from letsencrypt.reverter import Reverter
|
||||
|
||||
# Disable spurious errors... we are trying to test for them
|
||||
logging.disable(logging.CRITICAL)
|
||||
|
||||
self.config = setup_work_direc()
|
||||
self.reverter = Reverter(self.config)
|
||||
|
||||
tup = setup_test_files()
|
||||
self.config1, self.config2, self.dir1, self.dir2, self.sets = tup
|
||||
|
||||
def tearDown(self):
|
||||
shutil.rmtree(self.config.work_dir)
|
||||
shutil.rmtree(self.dir1)
|
||||
shutil.rmtree(self.dir2)
|
||||
|
||||
logging.disable(logging.NOTSET)
|
||||
|
||||
def test_basic_add_to_temp_checkpoint(self):
|
||||
# These shouldn't conflict even though they are both named config.txt
|
||||
self.reverter.add_to_temp_checkpoint(self.sets[0], "save1")
|
||||
self.reverter.add_to_temp_checkpoint(self.sets[1], "save2")
|
||||
|
||||
self.assertTrue(os.path.isdir(self.config.temp_checkpoint_dir))
|
||||
self.assertEqual(get_save_notes(
|
||||
self.config.temp_checkpoint_dir), "save1save2")
|
||||
self.assertFalse(os.path.isfile(
|
||||
os.path.join(self.config.temp_checkpoint_dir, "NEW_FILES")))
|
||||
|
||||
self.assertEqual(
|
||||
get_filepaths(self.config.temp_checkpoint_dir),
|
||||
"{0}\n{1}\n".format(self.config1, self.config2))
|
||||
|
||||
def test_add_to_checkpoint_copy_failure(self):
|
||||
with mock.patch("letsencrypt.reverter.shutil.copy2") as mock_copy2:
|
||||
mock_copy2.side_effect = IOError("bad copy")
|
||||
self.assertRaises(
|
||||
errors.ReverterError, self.reverter.add_to_checkpoint,
|
||||
self.sets[0], "save1")
|
||||
|
||||
def test_checkpoint_conflict(self):
|
||||
"""Make sure that checkpoint errors are thrown appropriately."""
|
||||
config3 = os.path.join(self.dir1, "config3.txt")
|
||||
self.reverter.register_file_creation(True, config3)
|
||||
update_file(config3, "This is a new file!")
|
||||
|
||||
self.reverter.add_to_checkpoint(self.sets[2], "save1")
|
||||
# This shouldn't throw an error
|
||||
self.reverter.add_to_temp_checkpoint(self.sets[0], "save2")
|
||||
# Raise error
|
||||
self.assertRaises(errors.ReverterError, self.reverter.add_to_checkpoint,
|
||||
self.sets[2], "save3")
|
||||
# Should not cause an error
|
||||
self.reverter.add_to_checkpoint(self.sets[1], "save4")
|
||||
|
||||
# Check to make sure new files are also checked...
|
||||
self.assertRaises(errors.ReverterError, self.reverter.add_to_checkpoint,
|
||||
set([config3]), "invalid save")
|
||||
|
||||
def test_multiple_saves_and_temp_revert(self):
|
||||
self.reverter.add_to_temp_checkpoint(self.sets[0], "save1")
|
||||
update_file(self.config1, "updated-directive")
|
||||
self.reverter.add_to_temp_checkpoint(self.sets[0], "save2-updated dir")
|
||||
update_file(self.config1, "new directive change that we won't keep")
|
||||
|
||||
self.reverter.revert_temporary_config()
|
||||
self.assertEqual(read_in(self.config1), "directive-dir1")
|
||||
|
||||
def test_multiple_registration_fail_and_revert(self):
|
||||
|
||||
config3 = os.path.join(self.dir1, "config3.txt")
|
||||
update_file(config3, "Config3")
|
||||
config4 = os.path.join(self.dir2, "config4.txt")
|
||||
update_file(config4, "Config4")
|
||||
|
||||
# Test multiple registrations and two registrations at once
|
||||
self.reverter.register_file_creation(True, self.config1)
|
||||
self.reverter.register_file_creation(True, self.config2)
|
||||
self.reverter.register_file_creation(True, config3, config4)
|
||||
|
||||
# Simulate Let's Encrypt crash... recovery routine is run
|
||||
self.reverter.recovery_routine()
|
||||
|
||||
self.assertFalse(os.path.isfile(self.config1))
|
||||
self.assertFalse(os.path.isfile(self.config2))
|
||||
self.assertFalse(os.path.isfile(config3))
|
||||
self.assertFalse(os.path.isfile(config4))
|
||||
|
||||
def test_multiple_registration_same_file(self):
|
||||
self.reverter.register_file_creation(True, self.config1)
|
||||
self.reverter.register_file_creation(True, self.config1)
|
||||
self.reverter.register_file_creation(True, self.config1)
|
||||
self.reverter.register_file_creation(True, self.config1)
|
||||
|
||||
files = get_new_files(self.config.temp_checkpoint_dir)
|
||||
|
||||
self.assertEqual(len(files), 1)
|
||||
|
||||
def test_register_file_creation_write_error(self):
|
||||
m_open = mock.mock_open()
|
||||
with mock.patch("letsencrypt.reverter.open", m_open, create=True):
|
||||
m_open.side_effect = OSError("bad open")
|
||||
self.assertRaises(
|
||||
errors.ReverterError, self.reverter.register_file_creation,
|
||||
True, self.config1)
|
||||
|
||||
def test_bad_registration(self):
|
||||
# Made this mistake and want to make sure it doesn't happen again...
|
||||
self.assertRaises(
|
||||
errors.ReverterError, self.reverter.register_file_creation,
|
||||
"filepath")
|
||||
|
||||
def test_register_undo_command(self):
|
||||
coms = [
|
||||
["a2dismod", "ssl"],
|
||||
["a2dismod", "rewrite"],
|
||||
["cleanslate"]
|
||||
]
|
||||
for com in coms:
|
||||
self.reverter.register_undo_command(True, com)
|
||||
|
||||
act_coms = get_undo_commands(self.config.temp_checkpoint_dir)
|
||||
|
||||
for a_com, com in itertools.izip(act_coms, coms):
|
||||
self.assertEqual(a_com, com)
|
||||
|
||||
def test_bad_register_undo_command(self):
|
||||
m_open = mock.mock_open()
|
||||
with mock.patch("letsencrypt.reverter.open", m_open, create=True):
|
||||
m_open.side_effect = OSError("bad open")
|
||||
self.assertRaises(
|
||||
errors.ReverterError, self.reverter.register_undo_command,
|
||||
True, ["command"])
|
||||
|
||||
@mock.patch("letsencrypt.le_util.run_script")
|
||||
def test_run_undo_commands(self, mock_run):
|
||||
mock_run.side_effect = ["", errors.SubprocessError]
|
||||
coms = [
|
||||
["invalid_command"],
|
||||
["a2dismod", "ssl"],
|
||||
]
|
||||
for com in coms:
|
||||
self.reverter.register_undo_command(True, com)
|
||||
|
||||
self.reverter.revert_temporary_config()
|
||||
|
||||
self.assertEqual(mock_run.call_count, 2)
|
||||
|
||||
def test_recovery_routine_in_progress_failure(self):
|
||||
self.reverter.add_to_checkpoint(self.sets[0], "perm save")
|
||||
|
||||
# pylint: disable=protected-access
|
||||
self.reverter._recover_checkpoint = mock.MagicMock(
|
||||
side_effect=errors.ReverterError)
|
||||
self.assertRaises(errors.ReverterError, self.reverter.recovery_routine)
|
||||
|
||||
def test_recover_checkpoint_revert_temp_failures(self):
|
||||
|
||||
mock_recover = mock.MagicMock(
|
||||
side_effect=errors.ReverterError("e"))
|
||||
|
||||
# pylint: disable=protected-access
|
||||
self.reverter._recover_checkpoint = mock_recover
|
||||
|
||||
self.reverter.add_to_temp_checkpoint(self.sets[0], "config1 save")
|
||||
|
||||
self.assertRaises(
|
||||
errors.ReverterError, self.reverter.revert_temporary_config)
|
||||
|
||||
def test_recover_checkpoint_rollback_failure(self):
|
||||
mock_recover = mock.MagicMock(
|
||||
side_effect=errors.ReverterError("e"))
|
||||
# pylint: disable=protected-access
|
||||
self.reverter._recover_checkpoint = mock_recover
|
||||
|
||||
self.reverter.add_to_checkpoint(self.sets[0], "config1 save")
|
||||
self.reverter.finalize_checkpoint("Title")
|
||||
|
||||
self.assertRaises(
|
||||
errors.ReverterError, self.reverter.rollback_checkpoints, 1)
|
||||
|
||||
def test_recover_checkpoint_copy_failure(self):
|
||||
self.reverter.add_to_temp_checkpoint(self.sets[0], "save1")
|
||||
|
||||
with mock.patch("letsencrypt.reverter.shutil.copy2") as mock_copy2:
|
||||
mock_copy2.side_effect = OSError("bad copy")
|
||||
self.assertRaises(
|
||||
errors.ReverterError, self.reverter.revert_temporary_config)
|
||||
|
||||
def test_recover_checkpoint_rm_failure(self):
|
||||
self.reverter.add_to_temp_checkpoint(self.sets[0], "temp save")
|
||||
|
||||
with mock.patch("letsencrypt.reverter.shutil.rmtree") as mock_rmtree:
|
||||
mock_rmtree.side_effect = OSError("Cannot remove tree")
|
||||
self.assertRaises(
|
||||
errors.ReverterError, self.reverter.revert_temporary_config)
|
||||
|
||||
@mock.patch("letsencrypt.reverter.logger.warning")
|
||||
def test_recover_checkpoint_missing_new_files(self, mock_warn):
|
||||
self.reverter.register_file_creation(
|
||||
True, os.path.join(self.dir1, "missing_file.txt"))
|
||||
self.reverter.revert_temporary_config()
|
||||
self.assertEqual(mock_warn.call_count, 1)
|
||||
|
||||
@mock.patch("letsencrypt.reverter.os.remove")
|
||||
def test_recover_checkpoint_remove_failure(self, mock_remove):
|
||||
self.reverter.register_file_creation(True, self.config1)
|
||||
mock_remove.side_effect = OSError("Can't remove")
|
||||
self.assertRaises(
|
||||
errors.ReverterError, self.reverter.revert_temporary_config)
|
||||
|
||||
def test_recovery_routine_temp_and_perm(self):
|
||||
# Register a new perm checkpoint file
|
||||
config3 = os.path.join(self.dir1, "config3.txt")
|
||||
self.reverter.register_file_creation(False, config3)
|
||||
update_file(config3, "This is a new perm file!")
|
||||
|
||||
# Add changes to perm checkpoint
|
||||
self.reverter.add_to_checkpoint(self.sets[0], "perm save1")
|
||||
update_file(self.config1, "updated perm config1")
|
||||
self.reverter.add_to_checkpoint(self.sets[1], "perm save2")
|
||||
update_file(self.config2, "updated perm config2")
|
||||
|
||||
# Add changes to a temporary checkpoint
|
||||
self.reverter.add_to_temp_checkpoint(self.sets[0], "temp save1")
|
||||
update_file(self.config1, "second update now temp config1")
|
||||
|
||||
# Register a new temp checkpoint file
|
||||
config4 = os.path.join(self.dir2, "config4.txt")
|
||||
self.reverter.register_file_creation(True, config4)
|
||||
update_file(config4, "New temporary file!")
|
||||
|
||||
# Now erase everything
|
||||
self.reverter.recovery_routine()
|
||||
|
||||
# Now Run tests
|
||||
# These were new files.. they should be removed
|
||||
self.assertFalse(os.path.isfile(config3))
|
||||
self.assertFalse(os.path.isfile(config4))
|
||||
|
||||
# Check to make sure everything got rolled back appropriately
|
||||
self.assertEqual(read_in(self.config1), "directive-dir1")
|
||||
self.assertEqual(read_in(self.config2), "directive-dir2")
|
||||
|
||||
|
||||
class TestFullCheckpointsReverter(unittest.TestCase):
|
||||
# pylint: disable=too-many-instance-attributes
|
||||
"""Tests functions having to deal with full checkpoints."""
|
||||
def setUp(self):
|
||||
from letsencrypt.reverter import Reverter
|
||||
# Disable spurious errors...
|
||||
logging.disable(logging.CRITICAL)
|
||||
|
||||
self.config = setup_work_direc()
|
||||
self.reverter = Reverter(self.config)
|
||||
|
||||
tup = setup_test_files()
|
||||
self.config1, self.config2, self.dir1, self.dir2, self.sets = tup
|
||||
|
||||
def tearDown(self):
|
||||
shutil.rmtree(self.config.work_dir)
|
||||
shutil.rmtree(self.dir1)
|
||||
shutil.rmtree(self.dir2)
|
||||
|
||||
logging.disable(logging.NOTSET)
|
||||
|
||||
def test_rollback_improper_inputs(self):
|
||||
self.assertRaises(
|
||||
errors.ReverterError, self.reverter.rollback_checkpoints, "-1")
|
||||
self.assertRaises(
|
||||
errors.ReverterError, self.reverter.rollback_checkpoints, -1000)
|
||||
self.assertRaises(
|
||||
errors.ReverterError, self.reverter.rollback_checkpoints, "one")
|
||||
|
||||
def test_rollback_finalize_checkpoint_valid_inputs(self):
|
||||
|
||||
config3 = self._setup_three_checkpoints()
|
||||
|
||||
# Check resulting backup directory
|
||||
self.assertEqual(len(os.listdir(self.config.backup_dir)), 3)
|
||||
# Check rollbacks
|
||||
# First rollback
|
||||
self.reverter.rollback_checkpoints(1)
|
||||
self.assertEqual(read_in(self.config1), "update config1")
|
||||
self.assertEqual(read_in(self.config2), "update config2")
|
||||
# config3 was not included in checkpoint
|
||||
self.assertEqual(read_in(config3), "Final form config3")
|
||||
|
||||
# Second rollback
|
||||
self.reverter.rollback_checkpoints(1)
|
||||
self.assertEqual(read_in(self.config1), "update config1")
|
||||
self.assertEqual(read_in(self.config2), "directive-dir2")
|
||||
self.assertFalse(os.path.isfile(config3))
|
||||
|
||||
# One dir left... check title
|
||||
all_dirs = os.listdir(self.config.backup_dir)
|
||||
self.assertEqual(len(all_dirs), 1)
|
||||
self.assertTrue(
|
||||
"First Checkpoint" in get_save_notes(
|
||||
os.path.join(self.config.backup_dir, all_dirs[0])))
|
||||
# Final rollback
|
||||
self.reverter.rollback_checkpoints(1)
|
||||
self.assertEqual(read_in(self.config1), "directive-dir1")
|
||||
|
||||
def test_finalize_checkpoint_no_in_progress(self):
|
||||
# No need to warn for this... just make sure there are no errors.
|
||||
self.reverter.finalize_checkpoint("No checkpoint...")
|
||||
|
||||
@mock.patch("letsencrypt.reverter.shutil.move")
|
||||
def test_finalize_checkpoint_cannot_title(self, mock_move):
|
||||
self.reverter.add_to_checkpoint(self.sets[0], "perm save")
|
||||
mock_move.side_effect = OSError("cannot move")
|
||||
|
||||
self.assertRaises(
|
||||
errors.ReverterError, self.reverter.finalize_checkpoint, "Title")
|
||||
|
||||
@mock.patch("letsencrypt.reverter.os.rename")
|
||||
def test_finalize_checkpoint_no_rename_directory(self, mock_rename):
|
||||
|
||||
self.reverter.add_to_checkpoint(self.sets[0], "perm save")
|
||||
mock_rename.side_effect = OSError
|
||||
|
||||
self.assertRaises(
|
||||
errors.ReverterError, self.reverter.finalize_checkpoint, "Title")
|
||||
|
||||
@mock.patch("letsencrypt.reverter.logger")
|
||||
def test_rollback_too_many(self, mock_logger):
|
||||
# Test no exist warning...
|
||||
self.reverter.rollback_checkpoints(1)
|
||||
self.assertEqual(mock_logger.warning.call_count, 1)
|
||||
|
||||
# Test Generic warning
|
||||
mock_logger.warning.call_count = 0
|
||||
self._setup_three_checkpoints()
|
||||
self.reverter.rollback_checkpoints(4)
|
||||
self.assertEqual(mock_logger.warning.call_count, 1)
|
||||
|
||||
def test_multi_rollback(self):
|
||||
config3 = self._setup_three_checkpoints()
|
||||
self.reverter.rollback_checkpoints(3)
|
||||
|
||||
self.assertEqual(read_in(self.config1), "directive-dir1")
|
||||
self.assertEqual(read_in(self.config2), "directive-dir2")
|
||||
self.assertFalse(os.path.isfile(config3))
|
||||
|
||||
@mock.patch("letsencrypt.reverter.zope.component.getUtility")
|
||||
def test_view_config_changes(self, mock_output):
|
||||
"""This is not strict as this is subject to change."""
|
||||
self._setup_three_checkpoints()
|
||||
|
||||
# Make sure it doesn't throw any errors
|
||||
self.reverter.view_config_changes()
|
||||
|
||||
# Make sure notification is output
|
||||
self.assertEqual(mock_output().notification.call_count, 1)
|
||||
|
||||
@mock.patch("letsencrypt.reverter.logger")
|
||||
def test_view_config_changes_no_backups(self, mock_logger):
|
||||
self.reverter.view_config_changes()
|
||||
self.assertTrue(mock_logger.info.call_count > 0)
|
||||
|
||||
def test_view_config_changes_bad_backups_dir(self):
|
||||
# There shouldn't be any "in progess directories when this is called
|
||||
# It must just be clean checkpoints
|
||||
os.makedirs(os.path.join(self.config.backup_dir, "in_progress"))
|
||||
|
||||
self.assertRaises(
|
||||
errors.ReverterError, self.reverter.view_config_changes)
|
||||
|
||||
def test_view_config_changes_for_logging(self):
|
||||
self._setup_three_checkpoints()
|
||||
|
||||
config_changes = self.reverter.view_config_changes(for_logging=True)
|
||||
|
||||
self.assertTrue("First Checkpoint" in config_changes)
|
||||
self.assertTrue("Second Checkpoint" in config_changes)
|
||||
self.assertTrue("Third Checkpoint" in config_changes)
|
||||
|
||||
def _setup_three_checkpoints(self):
|
||||
"""Generate some finalized checkpoints."""
|
||||
# Checkpoint1 - config1
|
||||
self.reverter.add_to_checkpoint(self.sets[0], "first save")
|
||||
self.reverter.finalize_checkpoint("First Checkpoint")
|
||||
|
||||
update_file(self.config1, "update config1")
|
||||
|
||||
# Checkpoint2 - new file config3, update config2
|
||||
config3 = os.path.join(self.dir1, "config3.txt")
|
||||
self.reverter.register_file_creation(False, config3)
|
||||
update_file(config3, "directive-config3")
|
||||
self.reverter.add_to_checkpoint(self.sets[1], "second save")
|
||||
self.reverter.finalize_checkpoint("Second Checkpoint")
|
||||
|
||||
update_file(self.config2, "update config2")
|
||||
update_file(config3, "update config3")
|
||||
|
||||
# Checkpoint3 - update config1, config2
|
||||
self.reverter.add_to_checkpoint(self.sets[2], "third save")
|
||||
self.reverter.finalize_checkpoint("Third Checkpoint - Save both")
|
||||
|
||||
update_file(self.config1, "Final form config1")
|
||||
update_file(self.config2, "Final form config2")
|
||||
update_file(config3, "Final form config3")
|
||||
|
||||
return config3
|
||||
|
||||
|
||||
def setup_work_direc():
|
||||
"""Setup directories.
|
||||
|
||||
:returns: Mocked :class:`letsencrypt.interfaces.IConfig`
|
||||
|
||||
"""
|
||||
work_dir = tempfile.mkdtemp("work")
|
||||
backup_dir = os.path.join(work_dir, "backup")
|
||||
|
||||
return mock.MagicMock(
|
||||
work_dir=work_dir, backup_dir=backup_dir,
|
||||
temp_checkpoint_dir=os.path.join(work_dir, "temp"),
|
||||
in_progress_dir=os.path.join(backup_dir, "in_progress_dir"))
|
||||
|
||||
|
||||
def setup_test_files():
|
||||
"""Setup sample configuration files."""
|
||||
dir1 = tempfile.mkdtemp("dir1")
|
||||
dir2 = tempfile.mkdtemp("dir2")
|
||||
config1 = os.path.join(dir1, "config.txt")
|
||||
config2 = os.path.join(dir2, "config.txt")
|
||||
with open(config1, "w") as file_fd:
|
||||
file_fd.write("directive-dir1")
|
||||
with open(config2, "w") as file_fd:
|
||||
file_fd.write("directive-dir2")
|
||||
|
||||
sets = [set([config1]),
|
||||
set([config2]),
|
||||
set([config1, config2])]
|
||||
|
||||
return config1, config2, dir1, dir2, sets
|
||||
|
||||
|
||||
def get_save_notes(dire):
|
||||
"""Read save notes"""
|
||||
return read_in(os.path.join(dire, "CHANGES_SINCE"))
|
||||
|
||||
|
||||
def get_filepaths(dire):
|
||||
"""Get Filepaths"""
|
||||
return read_in(os.path.join(dire, "FILEPATHS"))
|
||||
|
||||
|
||||
def get_new_files(dire):
|
||||
"""Get new files."""
|
||||
return read_in(os.path.join(dire, "NEW_FILES")).splitlines()
|
||||
|
||||
|
||||
def get_undo_commands(dire):
|
||||
"""Get new files."""
|
||||
with open(os.path.join(dire, "COMMANDS")) as csvfile:
|
||||
return list(csv.reader(csvfile))
|
||||
|
||||
|
||||
def read_in(path):
|
||||
"""Read in a file, return the str"""
|
||||
with open(path, "r") as file_fd:
|
||||
return file_fd.read()
|
||||
|
||||
|
||||
def update_file(filename, string):
|
||||
"""Update a file with a new value."""
|
||||
with open(filename, "w") as file_fd:
|
||||
file_fd.write(string)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main() # pragma: no cover
|
||||
@@ -1,770 +0,0 @@
|
||||
"""Tests for letsencrypt.storage."""
|
||||
# pylint disable=protected-access
|
||||
import datetime
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
import configobj
|
||||
import mock
|
||||
import pytz
|
||||
|
||||
from letsencrypt import configuration
|
||||
from letsencrypt import errors
|
||||
from letsencrypt.storage import ALL_FOUR
|
||||
|
||||
from letsencrypt.tests import test_util
|
||||
|
||||
|
||||
CERT = test_util.load_cert('cert.pem')
|
||||
|
||||
|
||||
def unlink_all(rc_object):
|
||||
"""Unlink all four items associated with this RenewableCert."""
|
||||
for kind in ALL_FOUR:
|
||||
os.unlink(getattr(rc_object, kind))
|
||||
|
||||
|
||||
def fill_with_sample_data(rc_object):
|
||||
"""Put dummy data into all four files of this RenewableCert."""
|
||||
for kind in ALL_FOUR:
|
||||
with open(getattr(rc_object, kind), "w") as f:
|
||||
f.write(kind)
|
||||
|
||||
|
||||
class BaseRenewableCertTest(unittest.TestCase):
|
||||
"""Base class for setting up Renewable Cert tests.
|
||||
|
||||
.. note:: It may be required to write out self.config for
|
||||
your test. Check :class:`.cli_test.DuplicateCertTest` for an example.
|
||||
|
||||
"""
|
||||
def setUp(self):
|
||||
from letsencrypt import storage
|
||||
self.tempdir = tempfile.mkdtemp()
|
||||
|
||||
self.cli_config = configuration.RenewerConfiguration(
|
||||
namespace=mock.MagicMock(
|
||||
config_dir=self.tempdir,
|
||||
work_dir=self.tempdir,
|
||||
logs_dir=self.tempdir,
|
||||
)
|
||||
)
|
||||
|
||||
# TODO: maybe provide RenewerConfiguration.make_dirs?
|
||||
# TODO: main() should create those dirs, c.f. #902
|
||||
os.makedirs(os.path.join(self.tempdir, "live", "example.org"))
|
||||
os.makedirs(os.path.join(self.tempdir, "archive", "example.org"))
|
||||
os.makedirs(os.path.join(self.tempdir, "renewal"))
|
||||
|
||||
config = configobj.ConfigObj()
|
||||
for kind in ALL_FOUR:
|
||||
config[kind] = os.path.join(self.tempdir, "live", "example.org",
|
||||
kind + ".pem")
|
||||
config.filename = os.path.join(self.tempdir, "renewal",
|
||||
"example.org.conf")
|
||||
config.write()
|
||||
self.config = config
|
||||
|
||||
# We also create a file that isn't a renewal config in the same
|
||||
# location to test that logic that reads in all-and-only renewal
|
||||
# configs will ignore it and NOT attempt to parse it.
|
||||
junk = open(os.path.join(self.tempdir, "renewal", "IGNORE.THIS"), "w")
|
||||
junk.write("This file should be ignored!")
|
||||
junk.close()
|
||||
|
||||
self.defaults = configobj.ConfigObj()
|
||||
|
||||
with mock.patch("letsencrypt.storage.RenewableCert._check_symlinks") as check:
|
||||
check.return_value = True
|
||||
self.test_rc = storage.RenewableCert(config.filename, self.cli_config)
|
||||
|
||||
def tearDown(self):
|
||||
shutil.rmtree(self.tempdir)
|
||||
|
||||
def _write_out_ex_kinds(self):
|
||||
for kind in ALL_FOUR:
|
||||
where = getattr(self.test_rc, kind)
|
||||
os.symlink(os.path.join("..", "..", "archive", "example.org",
|
||||
"{0}12.pem".format(kind)), where)
|
||||
with open(where, "w") as f:
|
||||
f.write(kind)
|
||||
os.unlink(where)
|
||||
os.symlink(os.path.join("..", "..", "archive", "example.org",
|
||||
"{0}11.pem".format(kind)), where)
|
||||
with open(where, "w") as f:
|
||||
f.write(kind)
|
||||
|
||||
|
||||
class RenewableCertTests(BaseRenewableCertTest):
|
||||
# pylint: disable=too-many-public-methods
|
||||
"""Tests for letsencrypt.storage."""
|
||||
|
||||
def test_initialization(self):
|
||||
self.assertEqual(self.test_rc.lineagename, "example.org")
|
||||
for kind in ALL_FOUR:
|
||||
self.assertEqual(
|
||||
getattr(self.test_rc, kind), os.path.join(
|
||||
self.tempdir, "live", "example.org", kind + ".pem"))
|
||||
|
||||
def test_renewal_bad_config(self):
|
||||
"""Test that the RenewableCert constructor will complain if
|
||||
the renewal configuration file doesn't end in ".conf"
|
||||
|
||||
"""
|
||||
from letsencrypt import storage
|
||||
broken = os.path.join(self.tempdir, "broken.conf")
|
||||
with open(broken, "w") as f:
|
||||
f.write("[No closing bracket for you!")
|
||||
self.assertRaises(errors.CertStorageError, storage.RenewableCert,
|
||||
broken, self.cli_config)
|
||||
os.unlink(broken)
|
||||
self.assertRaises(errors.CertStorageError, storage.RenewableCert,
|
||||
"fun", self.cli_config)
|
||||
|
||||
def test_renewal_incomplete_config(self):
|
||||
"""Test that the RenewableCert constructor will complain if
|
||||
the renewal configuration file is missing a required file element."""
|
||||
from letsencrypt import storage
|
||||
config = configobj.ConfigObj()
|
||||
config["cert"] = "imaginary_cert.pem"
|
||||
# Here the required privkey is missing.
|
||||
config["chain"] = "imaginary_chain.pem"
|
||||
config["fullchain"] = "imaginary_fullchain.pem"
|
||||
config.filename = os.path.join(self.tempdir, "imaginary_config.conf")
|
||||
config.write()
|
||||
self.assertRaises(errors.CertStorageError, storage.RenewableCert,
|
||||
config.filename, self.cli_config)
|
||||
|
||||
def test_consistent(self):
|
||||
# pylint: disable=too-many-statements,protected-access
|
||||
oldcert = self.test_rc.cert
|
||||
self.test_rc.cert = "relative/path"
|
||||
# Absolute path for item requirement
|
||||
self.assertFalse(self.test_rc._consistent())
|
||||
self.test_rc.cert = oldcert
|
||||
# Items must exist requirement
|
||||
self.assertFalse(self.test_rc._consistent())
|
||||
# Items must be symlinks requirements
|
||||
fill_with_sample_data(self.test_rc)
|
||||
self.assertFalse(self.test_rc._consistent())
|
||||
unlink_all(self.test_rc)
|
||||
# Items must point to desired place if they are relative
|
||||
for kind in ALL_FOUR:
|
||||
os.symlink(os.path.join("..", kind + "17.pem"),
|
||||
getattr(self.test_rc, kind))
|
||||
self.assertFalse(self.test_rc._consistent())
|
||||
unlink_all(self.test_rc)
|
||||
# Items must point to desired place if they are absolute
|
||||
for kind in ALL_FOUR:
|
||||
os.symlink(os.path.join(self.tempdir, kind + "17.pem"),
|
||||
getattr(self.test_rc, kind))
|
||||
self.assertFalse(self.test_rc._consistent())
|
||||
unlink_all(self.test_rc)
|
||||
# Items must point to things that exist
|
||||
for kind in ALL_FOUR:
|
||||
os.symlink(os.path.join("..", "..", "archive", "example.org",
|
||||
kind + "17.pem"),
|
||||
getattr(self.test_rc, kind))
|
||||
self.assertFalse(self.test_rc._consistent())
|
||||
# This version should work
|
||||
fill_with_sample_data(self.test_rc)
|
||||
self.assertTrue(self.test_rc._consistent())
|
||||
# Items must point to things that follow the naming convention
|
||||
os.unlink(self.test_rc.fullchain)
|
||||
os.symlink(os.path.join("..", "..", "archive", "example.org",
|
||||
"fullchain_17.pem"), self.test_rc.fullchain)
|
||||
with open(self.test_rc.fullchain, "w") as f:
|
||||
f.write("wrongly-named fullchain")
|
||||
self.assertFalse(self.test_rc._consistent())
|
||||
|
||||
def test_current_target(self):
|
||||
# Relative path logic
|
||||
os.symlink(os.path.join("..", "..", "archive", "example.org",
|
||||
"cert17.pem"), self.test_rc.cert)
|
||||
with open(self.test_rc.cert, "w") as f:
|
||||
f.write("cert")
|
||||
self.assertTrue(os.path.samefile(self.test_rc.current_target("cert"),
|
||||
os.path.join(self.tempdir, "archive",
|
||||
"example.org",
|
||||
"cert17.pem")))
|
||||
# Absolute path logic
|
||||
os.unlink(self.test_rc.cert)
|
||||
os.symlink(os.path.join(self.tempdir, "archive", "example.org",
|
||||
"cert17.pem"), self.test_rc.cert)
|
||||
with open(self.test_rc.cert, "w") as f:
|
||||
f.write("cert")
|
||||
self.assertTrue(os.path.samefile(self.test_rc.current_target("cert"),
|
||||
os.path.join(self.tempdir, "archive",
|
||||
"example.org",
|
||||
"cert17.pem")))
|
||||
|
||||
def test_current_version(self):
|
||||
for ver in (1, 5, 10, 20):
|
||||
os.symlink(os.path.join("..", "..", "archive", "example.org",
|
||||
"cert{0}.pem".format(ver)),
|
||||
self.test_rc.cert)
|
||||
with open(self.test_rc.cert, "w") as f:
|
||||
f.write("cert")
|
||||
os.unlink(self.test_rc.cert)
|
||||
os.symlink(os.path.join("..", "..", "archive", "example.org",
|
||||
"cert10.pem"), self.test_rc.cert)
|
||||
self.assertEqual(self.test_rc.current_version("cert"), 10)
|
||||
|
||||
def test_no_current_version(self):
|
||||
self.assertEqual(self.test_rc.current_version("cert"), None)
|
||||
|
||||
def test_latest_and_next_versions(self):
|
||||
for ver in xrange(1, 6):
|
||||
for kind in ALL_FOUR:
|
||||
where = getattr(self.test_rc, kind)
|
||||
if os.path.islink(where):
|
||||
os.unlink(where)
|
||||
os.symlink(os.path.join("..", "..", "archive", "example.org",
|
||||
"{0}{1}.pem".format(kind, ver)), where)
|
||||
with open(where, "w") as f:
|
||||
f.write(kind)
|
||||
self.assertEqual(self.test_rc.latest_common_version(), 5)
|
||||
self.assertEqual(self.test_rc.next_free_version(), 6)
|
||||
# Having one kind of file of a later version doesn't change the
|
||||
# result
|
||||
os.unlink(self.test_rc.privkey)
|
||||
os.symlink(os.path.join("..", "..", "archive", "example.org",
|
||||
"privkey7.pem"), self.test_rc.privkey)
|
||||
with open(self.test_rc.privkey, "w") as f:
|
||||
f.write("privkey")
|
||||
self.assertEqual(self.test_rc.latest_common_version(), 5)
|
||||
# ... although it does change the next free version
|
||||
self.assertEqual(self.test_rc.next_free_version(), 8)
|
||||
# Nor does having three out of four change the result
|
||||
os.unlink(self.test_rc.cert)
|
||||
os.symlink(os.path.join("..", "..", "archive", "example.org",
|
||||
"cert7.pem"), self.test_rc.cert)
|
||||
with open(self.test_rc.cert, "w") as f:
|
||||
f.write("cert")
|
||||
os.unlink(self.test_rc.fullchain)
|
||||
os.symlink(os.path.join("..", "..", "archive", "example.org",
|
||||
"fullchain7.pem"), self.test_rc.fullchain)
|
||||
with open(self.test_rc.fullchain, "w") as f:
|
||||
f.write("fullchain")
|
||||
self.assertEqual(self.test_rc.latest_common_version(), 5)
|
||||
# If we have everything from a much later version, it does change
|
||||
# the result
|
||||
ver = 17
|
||||
for kind in ALL_FOUR:
|
||||
where = getattr(self.test_rc, kind)
|
||||
if os.path.islink(where):
|
||||
os.unlink(where)
|
||||
os.symlink(os.path.join("..", "..", "archive", "example.org",
|
||||
"{0}{1}.pem".format(kind, ver)), where)
|
||||
with open(where, "w") as f:
|
||||
f.write(kind)
|
||||
self.assertEqual(self.test_rc.latest_common_version(), 17)
|
||||
self.assertEqual(self.test_rc.next_free_version(), 18)
|
||||
|
||||
def test_update_link_to(self):
|
||||
for ver in xrange(1, 6):
|
||||
for kind in ALL_FOUR:
|
||||
where = getattr(self.test_rc, kind)
|
||||
if os.path.islink(where):
|
||||
os.unlink(where)
|
||||
os.symlink(os.path.join("..", "..", "archive", "example.org",
|
||||
"{0}{1}.pem".format(kind, ver)), where)
|
||||
with open(where, "w") as f:
|
||||
f.write(kind)
|
||||
self.assertEqual(ver, self.test_rc.current_version(kind))
|
||||
# pylint: disable=protected-access
|
||||
self.test_rc._update_link_to("cert", 3)
|
||||
self.test_rc._update_link_to("privkey", 2)
|
||||
self.assertEqual(3, self.test_rc.current_version("cert"))
|
||||
self.assertEqual(2, self.test_rc.current_version("privkey"))
|
||||
self.assertEqual(5, self.test_rc.current_version("chain"))
|
||||
self.assertEqual(5, self.test_rc.current_version("fullchain"))
|
||||
# Currently we are allowed to update to a version that doesn't exist
|
||||
self.test_rc._update_link_to("chain", 3000)
|
||||
# However, current_version doesn't allow querying the resulting
|
||||
# version (because it's a broken link).
|
||||
self.assertEqual(os.path.basename(os.readlink(self.test_rc.chain)),
|
||||
"chain3000.pem")
|
||||
|
||||
def test_version(self):
|
||||
os.symlink(os.path.join("..", "..", "archive", "example.org",
|
||||
"cert12.pem"), self.test_rc.cert)
|
||||
with open(self.test_rc.cert, "w") as f:
|
||||
f.write("cert")
|
||||
# TODO: We should probably test that the directory is still the
|
||||
# same, but it's tricky because we can get an absolute
|
||||
# path out when we put a relative path in.
|
||||
self.assertEqual("cert8.pem",
|
||||
os.path.basename(self.test_rc.version("cert", 8)))
|
||||
|
||||
def test_update_all_links_to_success(self):
|
||||
for ver in xrange(1, 6):
|
||||
for kind in ALL_FOUR:
|
||||
where = getattr(self.test_rc, kind)
|
||||
if os.path.islink(where):
|
||||
os.unlink(where)
|
||||
os.symlink(os.path.join("..", "..", "archive", "example.org",
|
||||
"{0}{1}.pem".format(kind, ver)), where)
|
||||
with open(where, "w") as f:
|
||||
f.write(kind)
|
||||
self.assertEqual(ver, self.test_rc.current_version(kind))
|
||||
self.assertEqual(self.test_rc.latest_common_version(), 5)
|
||||
for ver in xrange(1, 6):
|
||||
self.test_rc.update_all_links_to(ver)
|
||||
for kind in ALL_FOUR:
|
||||
self.assertEqual(ver, self.test_rc.current_version(kind))
|
||||
self.assertEqual(self.test_rc.latest_common_version(), 5)
|
||||
|
||||
def test_update_all_links_to_partial_failure(self):
|
||||
def unlink_or_raise(path, real_unlink=os.unlink):
|
||||
# pylint: disable=missing-docstring
|
||||
basename = os.path.basename(path)
|
||||
if "fullchain" in basename and basename.startswith("prev"):
|
||||
raise ValueError
|
||||
else:
|
||||
real_unlink(path)
|
||||
|
||||
self._write_out_ex_kinds()
|
||||
with mock.patch("letsencrypt.storage.os.unlink") as mock_unlink:
|
||||
mock_unlink.side_effect = unlink_or_raise
|
||||
self.assertRaises(ValueError, self.test_rc.update_all_links_to, 12)
|
||||
|
||||
for kind in ALL_FOUR:
|
||||
self.assertEqual(self.test_rc.current_version(kind), 12)
|
||||
|
||||
def test_update_all_links_to_full_failure(self):
|
||||
def unlink_or_raise(path, real_unlink=os.unlink):
|
||||
# pylint: disable=missing-docstring
|
||||
if "fullchain" in os.path.basename(path):
|
||||
raise ValueError
|
||||
else:
|
||||
real_unlink(path)
|
||||
|
||||
self._write_out_ex_kinds()
|
||||
with mock.patch("letsencrypt.storage.os.unlink") as mock_unlink:
|
||||
mock_unlink.side_effect = unlink_or_raise
|
||||
self.assertRaises(ValueError, self.test_rc.update_all_links_to, 12)
|
||||
|
||||
for kind in ALL_FOUR:
|
||||
self.assertEqual(self.test_rc.current_version(kind), 11)
|
||||
|
||||
def test_has_pending_deployment(self):
|
||||
for ver in xrange(1, 6):
|
||||
for kind in ALL_FOUR:
|
||||
where = getattr(self.test_rc, kind)
|
||||
if os.path.islink(where):
|
||||
os.unlink(where)
|
||||
os.symlink(os.path.join("..", "..", "archive", "example.org",
|
||||
"{0}{1}.pem".format(kind, ver)), where)
|
||||
with open(where, "w") as f:
|
||||
f.write(kind)
|
||||
self.assertEqual(ver, self.test_rc.current_version(kind))
|
||||
for ver in xrange(1, 6):
|
||||
self.test_rc.update_all_links_to(ver)
|
||||
for kind in ALL_FOUR:
|
||||
self.assertEqual(ver, self.test_rc.current_version(kind))
|
||||
if ver < 5:
|
||||
self.assertTrue(self.test_rc.has_pending_deployment())
|
||||
else:
|
||||
self.assertFalse(self.test_rc.has_pending_deployment())
|
||||
|
||||
def test_names(self):
|
||||
# Trying the current version
|
||||
test_cert = test_util.load_vector("cert-san.pem")
|
||||
os.symlink(os.path.join("..", "..", "archive", "example.org",
|
||||
"cert12.pem"), self.test_rc.cert)
|
||||
with open(self.test_rc.cert, "w") as f:
|
||||
f.write(test_cert)
|
||||
self.assertEqual(self.test_rc.names(),
|
||||
["example.com", "www.example.com"])
|
||||
|
||||
# Trying a non-current version
|
||||
test_cert = test_util.load_vector("cert.pem")
|
||||
os.unlink(self.test_rc.cert)
|
||||
os.symlink(os.path.join("..", "..", "archive", "example.org",
|
||||
"cert15.pem"), self.test_rc.cert)
|
||||
with open(self.test_rc.cert, "w") as f:
|
||||
f.write(test_cert)
|
||||
self.assertEqual(self.test_rc.names(12),
|
||||
["example.com", "www.example.com"])
|
||||
|
||||
# Trying missing cert
|
||||
os.unlink(self.test_rc.cert)
|
||||
self.assertRaises(errors.CertStorageError, self.test_rc.names)
|
||||
|
||||
@mock.patch("letsencrypt.storage.datetime")
|
||||
def test_time_interval_judgments(self, mock_datetime):
|
||||
"""Test should_autodeploy() and should_autorenew() on the basis
|
||||
of expiry time windows."""
|
||||
test_cert = test_util.load_vector("cert.pem")
|
||||
self._write_out_ex_kinds()
|
||||
|
||||
self.test_rc.update_all_links_to(12)
|
||||
with open(self.test_rc.cert, "w") as f:
|
||||
f.write(test_cert)
|
||||
self.test_rc.update_all_links_to(11)
|
||||
with open(self.test_rc.cert, "w") as f:
|
||||
f.write(test_cert)
|
||||
|
||||
mock_datetime.timedelta = datetime.timedelta
|
||||
|
||||
for (current_time, interval, result) in [
|
||||
# 2014-12-13 12:00:00+00:00 (about 5 days prior to expiry)
|
||||
# Times that should result in autorenewal/autodeployment
|
||||
(1418472000, "2 months", True), (1418472000, "1 week", True),
|
||||
# Times that should not
|
||||
(1418472000, "4 days", False), (1418472000, "2 days", False),
|
||||
# 2009-05-01 12:00:00+00:00 (about 5 years prior to expiry)
|
||||
# Times that should result in autorenewal/autodeployment
|
||||
(1241179200, "7 years", True),
|
||||
(1241179200, "11 years 2 months", True),
|
||||
# Times that should not
|
||||
(1241179200, "8 hours", False), (1241179200, "2 days", False),
|
||||
(1241179200, "40 days", False), (1241179200, "9 months", False),
|
||||
# 2015-01-01 (after expiry has already happened, so all
|
||||
# intervals should cause autorenewal/autodeployment)
|
||||
(1420070400, "0 seconds", True),
|
||||
(1420070400, "10 seconds", True),
|
||||
(1420070400, "10 minutes", True),
|
||||
(1420070400, "10 weeks", True), (1420070400, "10 months", True),
|
||||
(1420070400, "10 years", True), (1420070400, "99 months", True),
|
||||
]:
|
||||
sometime = datetime.datetime.utcfromtimestamp(current_time)
|
||||
mock_datetime.datetime.utcnow.return_value = sometime
|
||||
self.test_rc.configuration["deploy_before_expiry"] = interval
|
||||
self.test_rc.configuration["renew_before_expiry"] = interval
|
||||
self.assertEqual(self.test_rc.should_autodeploy(), result)
|
||||
self.assertEqual(self.test_rc.should_autorenew(), result)
|
||||
|
||||
def test_autodeployment_is_enabled(self):
|
||||
self.assertTrue(self.test_rc.autodeployment_is_enabled())
|
||||
self.test_rc.configuration["autodeploy"] = "1"
|
||||
self.assertTrue(self.test_rc.autodeployment_is_enabled())
|
||||
|
||||
self.test_rc.configuration["autodeploy"] = "0"
|
||||
self.assertFalse(self.test_rc.autodeployment_is_enabled())
|
||||
|
||||
def test_should_autodeploy(self):
|
||||
"""Test should_autodeploy() on the basis of reasons other than
|
||||
expiry time window."""
|
||||
# pylint: disable=too-many-statements
|
||||
# Autodeployment turned off
|
||||
self.test_rc.configuration["autodeploy"] = "0"
|
||||
self.assertFalse(self.test_rc.should_autodeploy())
|
||||
self.test_rc.configuration["autodeploy"] = "1"
|
||||
# No pending deployment
|
||||
for ver in xrange(1, 6):
|
||||
for kind in ALL_FOUR:
|
||||
where = getattr(self.test_rc, kind)
|
||||
if os.path.islink(where):
|
||||
os.unlink(where)
|
||||
os.symlink(os.path.join("..", "..", "archive", "example.org",
|
||||
"{0}{1}.pem".format(kind, ver)), where)
|
||||
with open(where, "w") as f:
|
||||
f.write(kind)
|
||||
self.assertFalse(self.test_rc.should_autodeploy())
|
||||
|
||||
def test_autorenewal_is_enabled(self):
|
||||
self.assertTrue(self.test_rc.autorenewal_is_enabled())
|
||||
self.test_rc.configuration["autorenew"] = "1"
|
||||
self.assertTrue(self.test_rc.autorenewal_is_enabled())
|
||||
|
||||
self.test_rc.configuration["autorenew"] = "0"
|
||||
self.assertFalse(self.test_rc.autorenewal_is_enabled())
|
||||
|
||||
@mock.patch("letsencrypt.storage.RenewableCert.ocsp_revoked")
|
||||
def test_should_autorenew(self, mock_ocsp):
|
||||
"""Test should_autorenew on the basis of reasons other than
|
||||
expiry time window."""
|
||||
# pylint: disable=too-many-statements
|
||||
# Autorenewal turned off
|
||||
self.test_rc.configuration["autorenew"] = "0"
|
||||
self.assertFalse(self.test_rc.should_autorenew())
|
||||
self.test_rc.configuration["autorenew"] = "1"
|
||||
for kind in ALL_FOUR:
|
||||
where = getattr(self.test_rc, kind)
|
||||
os.symlink(os.path.join("..", "..", "archive", "example.org",
|
||||
"{0}12.pem".format(kind)), where)
|
||||
with open(where, "w") as f:
|
||||
f.write(kind)
|
||||
# Mandatory renewal on the basis of OCSP revocation
|
||||
mock_ocsp.return_value = True
|
||||
self.assertTrue(self.test_rc.should_autorenew())
|
||||
mock_ocsp.return_value = False
|
||||
|
||||
@mock.patch("letsencrypt.storage.relevant_values")
|
||||
def test_save_successor(self, mock_rv):
|
||||
# Mock relevant_values() to claim that all values are relevant here
|
||||
# (to avoid instantiating parser)
|
||||
mock_rv.side_effect = lambda x: x
|
||||
|
||||
for ver in xrange(1, 6):
|
||||
for kind in ALL_FOUR:
|
||||
where = getattr(self.test_rc, kind)
|
||||
if os.path.islink(where):
|
||||
os.unlink(where)
|
||||
os.symlink(os.path.join("..", "..", "archive", "example.org",
|
||||
"{0}{1}.pem".format(kind, ver)), where)
|
||||
with open(where, "w") as f:
|
||||
f.write(kind)
|
||||
self.test_rc.update_all_links_to(3)
|
||||
self.assertEqual(
|
||||
6, self.test_rc.save_successor(3, "new cert", None,
|
||||
"new chain", self.cli_config))
|
||||
with open(self.test_rc.version("cert", 6)) as f:
|
||||
self.assertEqual(f.read(), "new cert")
|
||||
with open(self.test_rc.version("chain", 6)) as f:
|
||||
self.assertEqual(f.read(), "new chain")
|
||||
with open(self.test_rc.version("fullchain", 6)) as f:
|
||||
self.assertEqual(f.read(), "new cert" + "new chain")
|
||||
# version 6 of the key should be a link back to version 3
|
||||
self.assertFalse(os.path.islink(self.test_rc.version("privkey", 3)))
|
||||
self.assertTrue(os.path.islink(self.test_rc.version("privkey", 6)))
|
||||
# Let's try two more updates
|
||||
self.assertEqual(
|
||||
7, self.test_rc.save_successor(6, "again", None,
|
||||
"newer chain", self.cli_config))
|
||||
self.assertEqual(
|
||||
8, self.test_rc.save_successor(7, "hello", None,
|
||||
"other chain", self.cli_config))
|
||||
# All of the subsequent versions should link directly to the original
|
||||
# privkey.
|
||||
for i in (6, 7, 8):
|
||||
self.assertTrue(os.path.islink(self.test_rc.version("privkey", i)))
|
||||
self.assertEqual("privkey3.pem", os.path.basename(os.readlink(
|
||||
self.test_rc.version("privkey", i))))
|
||||
|
||||
for kind in ALL_FOUR:
|
||||
self.assertEqual(self.test_rc.available_versions(kind), range(1, 9))
|
||||
self.assertEqual(self.test_rc.current_version(kind), 3)
|
||||
# Test updating from latest version rather than old version
|
||||
self.test_rc.update_all_links_to(8)
|
||||
self.assertEqual(
|
||||
9, self.test_rc.save_successor(8, "last", None,
|
||||
"attempt", self.cli_config))
|
||||
for kind in ALL_FOUR:
|
||||
self.assertEqual(self.test_rc.available_versions(kind),
|
||||
range(1, 10))
|
||||
self.assertEqual(self.test_rc.current_version(kind), 8)
|
||||
with open(self.test_rc.version("fullchain", 9)) as f:
|
||||
self.assertEqual(f.read(), "last" + "attempt")
|
||||
temp_config_file = os.path.join(self.cli_config.renewal_configs_dir,
|
||||
self.test_rc.lineagename) + ".conf.new"
|
||||
with open(temp_config_file, "w") as f:
|
||||
f.write("We previously crashed while writing me :(")
|
||||
# Test updating when providing a new privkey. The key should
|
||||
# be saved in a new file rather than creating a new symlink.
|
||||
self.assertEqual(
|
||||
10, self.test_rc.save_successor(9, "with", "a",
|
||||
"key", self.cli_config))
|
||||
self.assertTrue(os.path.exists(self.test_rc.version("privkey", 10)))
|
||||
self.assertFalse(os.path.islink(self.test_rc.version("privkey", 10)))
|
||||
self.assertFalse(os.path.exists(temp_config_file))
|
||||
|
||||
@mock.patch("letsencrypt.cli.helpful_parser")
|
||||
def test_relevant_values(self, mock_parser):
|
||||
"""Test that relevant_values() can reject an irrelevant value."""
|
||||
# pylint: disable=protected-access
|
||||
from letsencrypt import storage
|
||||
mock_parser.verb = "certonly"
|
||||
mock_parser.args = ["--standalone"]
|
||||
mock_action = mock.Mock(dest="rsa_key_size", default=2048)
|
||||
mock_parser.parser._actions = [mock_action]
|
||||
self.assertEqual(storage.relevant_values({"hello": "there"}), {})
|
||||
|
||||
@mock.patch("letsencrypt.cli.helpful_parser")
|
||||
def test_relevant_values_default(self, mock_parser):
|
||||
"""Test that relevant_values() can reject a default value."""
|
||||
# pylint: disable=protected-access
|
||||
from letsencrypt import storage
|
||||
mock_parser.verb = "certonly"
|
||||
mock_parser.args = ["--standalone"]
|
||||
mock_action = mock.Mock(dest="rsa_key_size", default=2048)
|
||||
mock_parser.parser._actions = [mock_action]
|
||||
self.assertEqual(storage.relevant_values({"rsa_key_size": 2048}), {})
|
||||
|
||||
@mock.patch("letsencrypt.cli.helpful_parser")
|
||||
def test_relevant_values_nondefault(self, mock_parser):
|
||||
"""Test that relevant_values() can retain a non-default value."""
|
||||
# pylint: disable=protected-access
|
||||
from letsencrypt import storage
|
||||
mock_parser.verb = "certonly"
|
||||
mock_parser.args = ["--standalone"]
|
||||
mock_action = mock.Mock(dest="rsa_key_size", default=2048)
|
||||
mock_parser.parser._actions = [mock_action]
|
||||
self.assertEqual(storage.relevant_values({"rsa_key_size": 12}),
|
||||
{"rsa_key_size": 12})
|
||||
|
||||
@mock.patch("letsencrypt.storage.relevant_values")
|
||||
def test_new_lineage(self, mock_rv):
|
||||
"""Test for new_lineage() class method."""
|
||||
# Mock relevant_values to say everything is relevant here (so we
|
||||
# don't have to mock the parser to help it decide!)
|
||||
mock_rv.side_effect = lambda x: x
|
||||
|
||||
from letsencrypt import storage
|
||||
result = storage.RenewableCert.new_lineage(
|
||||
"the-lineage.com", "cert", "privkey", "chain", self.cli_config)
|
||||
# This consistency check tests most relevant properties about the
|
||||
# newly created cert lineage.
|
||||
# pylint: disable=protected-access
|
||||
self.assertTrue(result._consistent())
|
||||
self.assertTrue(os.path.exists(os.path.join(
|
||||
self.cli_config.renewal_configs_dir, "the-lineage.com.conf")))
|
||||
with open(result.fullchain) as f:
|
||||
self.assertEqual(f.read(), "cert" + "chain")
|
||||
# Let's do it again and make sure it makes a different lineage
|
||||
result = storage.RenewableCert.new_lineage(
|
||||
"the-lineage.com", "cert2", "privkey2", "chain2", self.cli_config)
|
||||
self.assertTrue(os.path.exists(os.path.join(
|
||||
self.cli_config.renewal_configs_dir, "the-lineage.com-0001.conf")))
|
||||
# Now trigger the detection of already existing files
|
||||
os.mkdir(os.path.join(
|
||||
self.cli_config.live_dir, "the-lineage.com-0002"))
|
||||
self.assertRaises(errors.CertStorageError,
|
||||
storage.RenewableCert.new_lineage, "the-lineage.com",
|
||||
"cert3", "privkey3", "chain3", self.cli_config)
|
||||
os.mkdir(os.path.join(self.cli_config.archive_dir, "other-example.com"))
|
||||
self.assertRaises(errors.CertStorageError,
|
||||
storage.RenewableCert.new_lineage,
|
||||
"other-example.com", "cert4",
|
||||
"privkey4", "chain4", self.cli_config)
|
||||
# Make sure it can accept renewal parameters
|
||||
result = storage.RenewableCert.new_lineage(
|
||||
"the-lineage.com", "cert2", "privkey2", "chain2", self.cli_config)
|
||||
# TODO: Conceivably we could test that the renewal parameters actually
|
||||
# got saved
|
||||
|
||||
@mock.patch("letsencrypt.storage.relevant_values")
|
||||
def test_new_lineage_nonexistent_dirs(self, mock_rv):
|
||||
"""Test that directories can be created if they don't exist."""
|
||||
# Mock relevant_values to say everything is relevant here (so we
|
||||
# don't have to mock the parser to help it decide!)
|
||||
mock_rv.side_effect = lambda x: x
|
||||
|
||||
from letsencrypt import storage
|
||||
shutil.rmtree(self.cli_config.renewal_configs_dir)
|
||||
shutil.rmtree(self.cli_config.archive_dir)
|
||||
shutil.rmtree(self.cli_config.live_dir)
|
||||
|
||||
storage.RenewableCert.new_lineage(
|
||||
"the-lineage.com", "cert2", "privkey2", "chain2", self.cli_config)
|
||||
self.assertTrue(os.path.exists(
|
||||
os.path.join(
|
||||
self.cli_config.renewal_configs_dir, "the-lineage.com.conf")))
|
||||
self.assertTrue(os.path.exists(os.path.join(
|
||||
self.cli_config.live_dir, "the-lineage.com", "privkey.pem")))
|
||||
self.assertTrue(os.path.exists(os.path.join(
|
||||
self.cli_config.archive_dir, "the-lineage.com", "privkey1.pem")))
|
||||
|
||||
@mock.patch("letsencrypt.storage.le_util.unique_lineage_name")
|
||||
def test_invalid_config_filename(self, mock_uln):
|
||||
from letsencrypt import storage
|
||||
mock_uln.return_value = "this_does_not_end_with_dot_conf", "yikes"
|
||||
self.assertRaises(errors.CertStorageError,
|
||||
storage.RenewableCert.new_lineage, "example.com",
|
||||
"cert", "privkey", "chain", self.cli_config)
|
||||
|
||||
def test_bad_kind(self):
|
||||
self.assertRaises(
|
||||
errors.CertStorageError, self.test_rc.current_target, "elephant")
|
||||
self.assertRaises(
|
||||
errors.CertStorageError, self.test_rc.current_version, "elephant")
|
||||
self.assertRaises(
|
||||
errors.CertStorageError, self.test_rc.version, "elephant", 17)
|
||||
self.assertRaises(
|
||||
errors.CertStorageError,
|
||||
self.test_rc.available_versions, "elephant")
|
||||
self.assertRaises(
|
||||
errors.CertStorageError,
|
||||
self.test_rc.newest_available_version, "elephant")
|
||||
# pylint: disable=protected-access
|
||||
self.assertRaises(
|
||||
errors.CertStorageError,
|
||||
self.test_rc._update_link_to, "elephant", 17)
|
||||
|
||||
def test_ocsp_revoked(self):
|
||||
# XXX: This is currently hardcoded to False due to a lack of an
|
||||
# OCSP server to test against.
|
||||
self.assertFalse(self.test_rc.ocsp_revoked())
|
||||
|
||||
def test_add_time_interval(self):
|
||||
from letsencrypt import storage
|
||||
|
||||
# this month has 30 days, and the next year is a leap year
|
||||
time_1 = pytz.UTC.fromutc(datetime.datetime(2003, 11, 20, 11, 59, 21))
|
||||
|
||||
# this month has 31 days, and the next year is not a leap year
|
||||
time_2 = pytz.UTC.fromutc(datetime.datetime(2012, 10, 18, 21, 31, 16))
|
||||
|
||||
# in different time zone (GMT+8)
|
||||
time_3 = pytz.timezone('Asia/Shanghai').fromutc(
|
||||
datetime.datetime(2015, 10, 26, 22, 25, 41))
|
||||
|
||||
intended = {
|
||||
(time_1, ""): time_1,
|
||||
(time_2, ""): time_2,
|
||||
(time_3, ""): time_3,
|
||||
(time_1, "17 days"): time_1 + datetime.timedelta(17),
|
||||
(time_2, "17 days"): time_2 + datetime.timedelta(17),
|
||||
(time_1, "30"): time_1 + datetime.timedelta(30),
|
||||
(time_2, "30"): time_2 + datetime.timedelta(30),
|
||||
(time_1, "7 weeks"): time_1 + datetime.timedelta(49),
|
||||
(time_2, "7 weeks"): time_2 + datetime.timedelta(49),
|
||||
# 1 month is always 30 days, no matter which month it is
|
||||
(time_1, "1 month"): time_1 + datetime.timedelta(30),
|
||||
(time_2, "1 month"): time_2 + datetime.timedelta(31),
|
||||
# 1 year could be 365 or 366 days, depends on the year
|
||||
(time_1, "1 year"): time_1 + datetime.timedelta(366),
|
||||
(time_2, "1 year"): time_2 + datetime.timedelta(365),
|
||||
(time_1, "1 year 1 day"): time_1 + datetime.timedelta(367),
|
||||
(time_2, "1 year 1 day"): time_2 + datetime.timedelta(366),
|
||||
(time_1, "1 year-1 day"): time_1 + datetime.timedelta(365),
|
||||
(time_2, "1 year-1 day"): time_2 + datetime.timedelta(364),
|
||||
(time_1, "4 years"): time_1 + datetime.timedelta(1461),
|
||||
(time_2, "4 years"): time_2 + datetime.timedelta(1461),
|
||||
}
|
||||
|
||||
for parameters, excepted in intended.items():
|
||||
base_time, interval = parameters
|
||||
self.assertEqual(storage.add_time_interval(base_time, interval),
|
||||
excepted)
|
||||
|
||||
def test_missing_cert(self):
|
||||
from letsencrypt import storage
|
||||
self.assertRaises(errors.CertStorageError,
|
||||
storage.RenewableCert,
|
||||
self.config.filename, self.cli_config)
|
||||
os.symlink("missing", self.config[ALL_FOUR[0]])
|
||||
self.assertRaises(errors.CertStorageError,
|
||||
storage.RenewableCert,
|
||||
self.config.filename, self.cli_config)
|
||||
|
||||
def test_write_renewal_config(self):
|
||||
# Mostly tested by the process of creating and updating lineages,
|
||||
# but we can test that this successfully creates files, removes
|
||||
# unneeded items, and preserves comments.
|
||||
temp = os.path.join(self.tempdir, "sample-file")
|
||||
temp2 = os.path.join(self.tempdir, "sample-file.new")
|
||||
with open(temp, "w") as f:
|
||||
f.write("[renewalparams]\nuseful = value # A useful value\n"
|
||||
"useless = value # Not needed\n")
|
||||
target = {}
|
||||
for x in ALL_FOUR:
|
||||
target[x] = "somewhere"
|
||||
relevant_data = {"useful": "new_value"}
|
||||
from letsencrypt import storage
|
||||
storage.write_renewal_config(temp, temp2, target, relevant_data)
|
||||
with open(temp2, "r") as f:
|
||||
content = f.read()
|
||||
# useful value was updated
|
||||
assert "useful = new_value" in content
|
||||
# associated comment was preserved
|
||||
assert "A useful value" in content
|
||||
# useless value was deleted
|
||||
assert "useless" not in content
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main() # pragma: no cover
|
||||
@@ -1,75 +0,0 @@
|
||||
"""Test utilities.
|
||||
|
||||
.. warning:: This module is not part of the public API.
|
||||
|
||||
"""
|
||||
import os
|
||||
import pkg_resources
|
||||
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
from cryptography.hazmat.primitives import serialization
|
||||
import OpenSSL
|
||||
|
||||
from acme import jose
|
||||
|
||||
|
||||
def vector_path(*names):
|
||||
"""Path to a test vector."""
|
||||
return pkg_resources.resource_filename(
|
||||
__name__, os.path.join('testdata', *names))
|
||||
|
||||
|
||||
def load_vector(*names):
|
||||
"""Load contents of a test vector."""
|
||||
# luckily, resource_string opens file in binary mode
|
||||
return pkg_resources.resource_string(
|
||||
__name__, os.path.join('testdata', *names))
|
||||
|
||||
|
||||
def _guess_loader(filename, loader_pem, loader_der):
|
||||
_, ext = os.path.splitext(filename)
|
||||
if ext.lower() == '.pem':
|
||||
return loader_pem
|
||||
elif ext.lower() == '.der':
|
||||
return loader_der
|
||||
else: # pragma: no cover
|
||||
raise ValueError("Loader could not be recognized based on extension")
|
||||
|
||||
|
||||
def load_cert(*names):
|
||||
"""Load certificate."""
|
||||
loader = _guess_loader(
|
||||
names[-1], OpenSSL.crypto.FILETYPE_PEM, OpenSSL.crypto.FILETYPE_ASN1)
|
||||
return OpenSSL.crypto.load_certificate(loader, load_vector(*names))
|
||||
|
||||
|
||||
def load_comparable_cert(*names):
|
||||
"""Load ComparableX509 cert."""
|
||||
return jose.ComparableX509(load_cert(*names))
|
||||
|
||||
|
||||
def load_csr(*names):
|
||||
"""Load certificate request."""
|
||||
loader = _guess_loader(
|
||||
names[-1], OpenSSL.crypto.FILETYPE_PEM, OpenSSL.crypto.FILETYPE_ASN1)
|
||||
return OpenSSL.crypto.load_certificate_request(loader, load_vector(*names))
|
||||
|
||||
|
||||
def load_comparable_csr(*names):
|
||||
"""Load ComparableX509 certificate request."""
|
||||
return jose.ComparableX509(load_csr(*names))
|
||||
|
||||
|
||||
def load_rsa_private_key(*names):
|
||||
"""Load RSA private key."""
|
||||
loader = _guess_loader(names[-1], serialization.load_pem_private_key,
|
||||
serialization.load_der_private_key)
|
||||
return jose.ComparableRSAKey(loader(
|
||||
load_vector(*names), password=None, backend=default_backend()))
|
||||
|
||||
|
||||
def load_pyopenssl_private_key(*names):
|
||||
"""Load pyOpenSSL private key."""
|
||||
loader = _guess_loader(
|
||||
names[-1], OpenSSL.crypto.FILETYPE_PEM, OpenSSL.crypto.FILETYPE_ASN1)
|
||||
return OpenSSL.crypto.load_privatekey(loader, load_vector(*names))
|
||||
@@ -1,28 +0,0 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIE1DCCA7ygAwIBAgITAPoz/CBluNQV/Eh9F+CS6dSxEDANBgkqhkiG9w0BAQsF
|
||||
ADAfMR0wGwYDVQQDDBRoYXBweSBoYWNrZXIgZmFrZSBDQTAeFw0xNjAyMDIyMzQ5
|
||||
MDBaFw0xNjA1MDIyMzQ5MDBaMBQxEjAQBgNVBAMTCWlzbm90Lm9yZzCCASIwDQYJ
|
||||
KoZIhvcNAQEBBQADggEPADCCAQoCggEBALyudqLKcIdWZ5VaK1fuhlEDbZtvs2E+
|
||||
slm4dmSS1nFve7MdlZ69K0gdtnhkiPQ0wGQTligeDZ8fY8iL87GZO0tp5f7S+QJN
|
||||
NYCiYw6j4qp5JBy/zG22kJz1Quu7/vXMYLzLvK6x6YixiWAWyqqvlUVBLS1r4W3h
|
||||
A5Z+F1EIsXeyz7TJe3lAzIWAAxpfH9OviIz2rEDotuCdU771USLLNSw4qJojNlTx
|
||||
UpZG6lGFs8KGb8tqROXknaMKE4PvN3SITixSUTFbktt1Wz60moWbNdLMKvgkzuUP
|
||||
r4viO2P4SO5slNAY0ZeEssPpVAelN3EvrAcEZtoKmG5fnQDVo8uVag0CAwEAAaOC
|
||||
AhIwggIOMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYB
|
||||
BQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUqhI4u6aaPrcYQnmypxV8Tap8
|
||||
L54wHwYDVR0jBBgwFoAU+3hPEvlgFYMsnxd/NBmzLjbqQYkweAYIKwYBBQUHAQEE
|
||||
bDBqMDMGCCsGAQUFBzABhidodHRwOi8vb2NzcC5zdGFnaW5nLXgxLmxldHNlbmNy
|
||||
eXB0Lm9yZy8wMwYIKwYBBQUHMAKGJ2h0dHA6Ly9jZXJ0LnN0YWdpbmcteDEubGV0
|
||||
c2VuY3J5cHQub3JnLzAUBgNVHREEDTALgglpc25vdC5vcmcwgf4GA1UdIASB9jCB
|
||||
8zAIBgZngQwBAgEwgeYGCysGAQQBgt8TAQEBMIHWMCYGCCsGAQUFBwIBFhpodHRw
|
||||
Oi8vY3BzLmxldHNlbmNyeXB0Lm9yZzCBqwYIKwYBBQUHAgIwgZ4MgZtUaGlzIENl
|
||||
cnRpZmljYXRlIG1heSBvbmx5IGJlIHJlbGllZCB1cG9uIGJ5IFJlbHlpbmcgUGFy
|
||||
dGllcyBhbmQgb25seSBpbiBhY2NvcmRhbmNlIHdpdGggdGhlIENlcnRpZmljYXRl
|
||||
IFBvbGljeSBmb3VuZCBhdCBodHRwczovL2xldHNlbmNyeXB0Lm9yZy9yZXBvc2l0
|
||||
b3J5LzANBgkqhkiG9w0BAQsFAAOCAQEAbAhX6FfQwELayneY4l5RvYSdw/Jj5CRy
|
||||
KzrM7ISld7x9YPpxX6Pmht/YyMhLWrtxvFUR2+RNhSIYB8IjQEjmKjvR7UNeiUve
|
||||
jzPEAuTg/9m3i0FJpPHc2aKGzlLFQCMm5/RrvnXI6ljIcyhocLvMiN46iexcExI2
|
||||
Ese3w8GoH6wARYKxU/QBexfoXQLgtAbYzNRE6EgKWtB+txV+7+d2MgbhCEit5VwU
|
||||
+ydT8inp9URsA7iKM03hDdGOBysddkrm1/yEhVy/Oo6bT9WMAUHVvz61hHekWcSf
|
||||
rAQ6BayubvWOUx06eTowXr1gln/rl+WXOxcsJeag127NuhmHOCXZxQ==
|
||||
-----END CERTIFICATE-----
|
||||
@@ -1,19 +0,0 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDETCCAfmgAwIBAgIJAJzxkS6o1QkIMA0GCSqGSIb3DQEBCwUAMB8xHTAbBgNV
|
||||
BAMMFGhhcHB5IGhhY2tlciBmYWtlIENBMB4XDTE1MDQwNzIzNTAzOFoXDTI1MDQw
|
||||
NDIzNTAzOFowHzEdMBsGA1UEAwwUaGFwcHkgaGFja2VyIGZha2UgQ0EwggEiMA0G
|
||||
CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDCCkd5mgXFErJ3F2M0E9dw+Ta/md5i
|
||||
8TDId01HberAApqmydG7UZYF3zLTSzNjlNSOmtybvrSGUnZ9r9tSQcL8VM6WUOM8
|
||||
tnIpiIjEA2QkBycMwvRmZ/B2ltPdYs/R9BqNwO1g18GDZrHSzUYtNKNeFI6Glamj
|
||||
7GK2Vr0SmiEamlNIR5ktAFsEErzf/d4jCF7sosMsJpMCm1p58QkP4LHLShVLXDa8
|
||||
BMfVoI+ipYcA08iNUFkgW8VWDclIDxcysa0psDDtMjX3+4aPkE/cefmP+1xOfUuD
|
||||
HOGV8XFynsP4EpTfVOZr0/g9gYQ7ZArqXX7GTQkFqduwPm/w5qxSPTarAgMBAAGj
|
||||
UDBOMB0GA1UdDgQWBBT7eE8S+WAVgyyfF380GbMuNupBiTAfBgNVHSMEGDAWgBT7
|
||||
eE8S+WAVgyyfF380GbMuNupBiTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUA
|
||||
A4IBAQAd9Da+Zv+TjMv7NTAmliqnWHY6d3UxEZN3hFEJ58IQVHbBZVZdW7zhRktB
|
||||
vR05Kweac0HJeK91TKmzvXl21IXLvh0gcNLU/uweD3no/snfdB4OoFompljThmgl
|
||||
zBqiqWoKBJQrLCA8w5UB+ReomRYd/EYXF/6TAfzm6hr//Xt5mPiUHPdvYt75lMAo
|
||||
vRxLSbF8TSQ6b7BYxISWjPgFASNNqJNHEItWsmQMtAjjwzb9cs01XH9pChVAWn9L
|
||||
oeMKa+SlHSYrWG93+EcrIH/dGU76uNOiaDzBSKvaehG53h25MHuO1anNICJvZovW
|
||||
rFo4Uv1EnkKJm3vJFe50eJGhEKlx
|
||||
-----END CERTIFICATE-----
|
||||
@@ -1,47 +0,0 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIE1DCCA7ygAwIBAgITAPoz/CBluNQV/Eh9F+CS6dSxEDANBgkqhkiG9w0BAQsF
|
||||
ADAfMR0wGwYDVQQDDBRoYXBweSBoYWNrZXIgZmFrZSBDQTAeFw0xNjAyMDIyMzQ5
|
||||
MDBaFw0xNjA1MDIyMzQ5MDBaMBQxEjAQBgNVBAMTCWlzbm90Lm9yZzCCASIwDQYJ
|
||||
KoZIhvcNAQEBBQADggEPADCCAQoCggEBALyudqLKcIdWZ5VaK1fuhlEDbZtvs2E+
|
||||
slm4dmSS1nFve7MdlZ69K0gdtnhkiPQ0wGQTligeDZ8fY8iL87GZO0tp5f7S+QJN
|
||||
NYCiYw6j4qp5JBy/zG22kJz1Quu7/vXMYLzLvK6x6YixiWAWyqqvlUVBLS1r4W3h
|
||||
A5Z+F1EIsXeyz7TJe3lAzIWAAxpfH9OviIz2rEDotuCdU771USLLNSw4qJojNlTx
|
||||
UpZG6lGFs8KGb8tqROXknaMKE4PvN3SITixSUTFbktt1Wz60moWbNdLMKvgkzuUP
|
||||
r4viO2P4SO5slNAY0ZeEssPpVAelN3EvrAcEZtoKmG5fnQDVo8uVag0CAwEAAaOC
|
||||
AhIwggIOMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYB
|
||||
BQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUqhI4u6aaPrcYQnmypxV8Tap8
|
||||
L54wHwYDVR0jBBgwFoAU+3hPEvlgFYMsnxd/NBmzLjbqQYkweAYIKwYBBQUHAQEE
|
||||
bDBqMDMGCCsGAQUFBzABhidodHRwOi8vb2NzcC5zdGFnaW5nLXgxLmxldHNlbmNy
|
||||
eXB0Lm9yZy8wMwYIKwYBBQUHMAKGJ2h0dHA6Ly9jZXJ0LnN0YWdpbmcteDEubGV0
|
||||
c2VuY3J5cHQub3JnLzAUBgNVHREEDTALgglpc25vdC5vcmcwgf4GA1UdIASB9jCB
|
||||
8zAIBgZngQwBAgEwgeYGCysGAQQBgt8TAQEBMIHWMCYGCCsGAQUFBwIBFhpodHRw
|
||||
Oi8vY3BzLmxldHNlbmNyeXB0Lm9yZzCBqwYIKwYBBQUHAgIwgZ4MgZtUaGlzIENl
|
||||
cnRpZmljYXRlIG1heSBvbmx5IGJlIHJlbGllZCB1cG9uIGJ5IFJlbHlpbmcgUGFy
|
||||
dGllcyBhbmQgb25seSBpbiBhY2NvcmRhbmNlIHdpdGggdGhlIENlcnRpZmljYXRl
|
||||
IFBvbGljeSBmb3VuZCBhdCBodHRwczovL2xldHNlbmNyeXB0Lm9yZy9yZXBvc2l0
|
||||
b3J5LzANBgkqhkiG9w0BAQsFAAOCAQEAbAhX6FfQwELayneY4l5RvYSdw/Jj5CRy
|
||||
KzrM7ISld7x9YPpxX6Pmht/YyMhLWrtxvFUR2+RNhSIYB8IjQEjmKjvR7UNeiUve
|
||||
jzPEAuTg/9m3i0FJpPHc2aKGzlLFQCMm5/RrvnXI6ljIcyhocLvMiN46iexcExI2
|
||||
Ese3w8GoH6wARYKxU/QBexfoXQLgtAbYzNRE6EgKWtB+txV+7+d2MgbhCEit5VwU
|
||||
+ydT8inp9URsA7iKM03hDdGOBysddkrm1/yEhVy/Oo6bT9WMAUHVvz61hHekWcSf
|
||||
rAQ6BayubvWOUx06eTowXr1gln/rl+WXOxcsJeag127NuhmHOCXZxQ==
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDETCCAfmgAwIBAgIJAJzxkS6o1QkIMA0GCSqGSIb3DQEBCwUAMB8xHTAbBgNV
|
||||
BAMMFGhhcHB5IGhhY2tlciBmYWtlIENBMB4XDTE1MDQwNzIzNTAzOFoXDTI1MDQw
|
||||
NDIzNTAzOFowHzEdMBsGA1UEAwwUaGFwcHkgaGFja2VyIGZha2UgQ0EwggEiMA0G
|
||||
CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDCCkd5mgXFErJ3F2M0E9dw+Ta/md5i
|
||||
8TDId01HberAApqmydG7UZYF3zLTSzNjlNSOmtybvrSGUnZ9r9tSQcL8VM6WUOM8
|
||||
tnIpiIjEA2QkBycMwvRmZ/B2ltPdYs/R9BqNwO1g18GDZrHSzUYtNKNeFI6Glamj
|
||||
7GK2Vr0SmiEamlNIR5ktAFsEErzf/d4jCF7sosMsJpMCm1p58QkP4LHLShVLXDa8
|
||||
BMfVoI+ipYcA08iNUFkgW8VWDclIDxcysa0psDDtMjX3+4aPkE/cefmP+1xOfUuD
|
||||
HOGV8XFynsP4EpTfVOZr0/g9gYQ7ZArqXX7GTQkFqduwPm/w5qxSPTarAgMBAAGj
|
||||
UDBOMB0GA1UdDgQWBBT7eE8S+WAVgyyfF380GbMuNupBiTAfBgNVHSMEGDAWgBT7
|
||||
eE8S+WAVgyyfF380GbMuNupBiTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUA
|
||||
A4IBAQAd9Da+Zv+TjMv7NTAmliqnWHY6d3UxEZN3hFEJ58IQVHbBZVZdW7zhRktB
|
||||
vR05Kweac0HJeK91TKmzvXl21IXLvh0gcNLU/uweD3no/snfdB4OoFompljThmgl
|
||||
zBqiqWoKBJQrLCA8w5UB+ReomRYd/EYXF/6TAfzm6hr//Xt5mPiUHPdvYt75lMAo
|
||||
vRxLSbF8TSQ6b7BYxISWjPgFASNNqJNHEItWsmQMtAjjwzb9cs01XH9pChVAWn9L
|
||||
oeMKa+SlHSYrWG93+EcrIH/dGU76uNOiaDzBSKvaehG53h25MHuO1anNICJvZovW
|
||||
rFo4Uv1EnkKJm3vJFe50eJGhEKlx
|
||||
-----END CERTIFICATE-----
|
||||
@@ -1,28 +0,0 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC8rnaiynCHVmeV
|
||||
WitX7oZRA22bb7NhPrJZuHZkktZxb3uzHZWevStIHbZ4ZIj0NMBkE5YoHg2fH2PI
|
||||
i/OxmTtLaeX+0vkCTTWAomMOo+KqeSQcv8xttpCc9ULru/71zGC8y7yusemIsYlg
|
||||
Fsqqr5VFQS0ta+Ft4QOWfhdRCLF3ss+0yXt5QMyFgAMaXx/Tr4iM9qxA6LbgnVO+
|
||||
9VEiyzUsOKiaIzZU8VKWRupRhbPChm/LakTl5J2jChOD7zd0iE4sUlExW5LbdVs+
|
||||
tJqFmzXSzCr4JM7lD6+L4jtj+EjubJTQGNGXhLLD6VQHpTdxL6wHBGbaCphuX50A
|
||||
1aPLlWoNAgMBAAECggEAfKKWFWS6PnwSAnNErFoQeZVVItb/XB5JO8EA2+CvLNFi
|
||||
mefR/MCixYlzDkYCvaXW7ISPrMJlZxYaGNBx0oAQzfkPB2wfNqj/zY/29SXGxast
|
||||
8puzk0mEb1oHsaZGfeFaiXvfkFpPlI8J2uJTT7qaVNv/1sArciSv9QonpsyiRhlB
|
||||
yqT49juNVoR1tJHyXzkkRfHKTG8OlJd4kuFOl3fM9dTFPQ/ft0kTNAQ/B4SFvSwF
|
||||
RJsbLbsbFGsUdV9ekE6UX6oWD/Ah707rvgtCyS0Bc+0O3t2EKwmm3RXPRUMHCVxE
|
||||
bKdTxRB4etbjMVXMuVhB8Y4GbfrtMCy+qxZQ6znCAQKBgQDr7bcYAZVZp/nBMVB+
|
||||
lBO9w73J6lnEWm6bZ9728KlGAKETaRhxZQSi6TN6MWwNwnk6rinyz4uVwVr9ZRCs
|
||||
WkB1TbvW0JNcWdr3YClwsKXAt8X22bjGe0LagDJHG6r1TPS+MdovOS2M6IMaxlbT
|
||||
rzFhSJ8ojLX3tqnOsmc7YAFLjQKBgQDMu8E9hoJt82lQzOGrjHmGzGEu2GLx9WKO
|
||||
e4nkj335kX6fIhMMqSXBFbTJZwXoYvk5J8ZnaARbYG0m5nxDCwRjX5HWa8q0B2Po
|
||||
ta53w01sKKznzlPjUhsdhEthun7MCFfLZpgvcZ9xVzOXo3/Zfn2+RrsPSjrVDqBy
|
||||
hj+k5mW4gQKBgHFWKf3LTO7cBdvsD8ou4mjn7nVgMi1kb/wR4wdnxzmMtdR4STi4
|
||||
GYkVVBhgQ5M8mDY7UoWFdH3FfCt8cI0Lcimn5ROl8RSNSeZKeL3c7lNtNRmHr/8R
|
||||
WaVTrlOAlBjxFiWEF1dWNW6ah9jF7RIV+DfOxj6ZkhTk2CAmjfb1AMpFAoGABf96
|
||||
KdNG/vGipDtcYSo8ZTaXoke0nmISARqdb5TEnAsnKoJVDInoEUARi9T411YO9x2z
|
||||
MlRZzFOG3xzhhxVLi53BKAcAaUXOJ4MrGVcfbYvDhQcGbiJ5qOO3UaWlEVUtPUhE
|
||||
LR+nDCsB1+9yT2zlQi3QTSJflt5W1QQZ2TrmwAECgYEAvQ7+sTcHs1K9yKj7koEu
|
||||
A19FbMA0IwvrVRcV/VqmlsoW6e6wW2YND+GtaDbKdD0aBPivqLJwpNFrsRA+W0iB
|
||||
vzmML6sKhhL+j7tjSgq+iQdBkKz0j9PyReuhe9CRnljMmyun+4qKEk0KUvxBrjPY
|
||||
Skn+ML18qyUoEPnmbpfHxCs=
|
||||
-----END PRIVATE KEY-----
|
||||
-14
@@ -1,14 +0,0 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICFjCCAcCgAwIBAgICBTkwDQYJKoZIhvcNAQELBQAwdzELMAkGA1UEBhMCVVMx
|
||||
ETAPBgNVBAgMCE1pY2hpZ2FuMRIwEAYDVQQHDAlBbm4gQXJib3IxKzApBgNVBAoM
|
||||
IlVuaXZlcnNpdHkgb2YgTWljaGlnYW4gYW5kIHRoZSBFRkYxFDASBgNVBAMMC2V4
|
||||
YW1wbGUuY29tMB4XDTE0MTIxMTIyMzQ0NVoXDTE0MTIxODIyMzQ0NVowdzELMAkG
|
||||
A1UEBhMCVVMxETAPBgNVBAgMCE1pY2hpZ2FuMRIwEAYDVQQHDAlBbm4gQXJib3Ix
|
||||
KzApBgNVBAoMIlVuaXZlcnNpdHkgb2YgTWljaGlnYW4gYW5kIHRoZSBFRkYxFDAS
|
||||
BgNVBAMMC2V4YW1wbGUuY29tMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKx1c7RR
|
||||
7R/drnBSQ/zfx1vQLHUbFLh1AQQQ5R8DZUXd36efNK79vukFhN9HFoHZiUvOjm0c
|
||||
+pVE6K+EdE/twuUCAwEAAaM2MDQwCQYDVR0TBAIwADAnBgNVHREEIDAeggtleGFt
|
||||
cGxlLmNvbYIPd3d3LmV4YW1wbGUuY29tMA0GCSqGSIb3DQEBCwUAA0EASuvNKFTF
|
||||
nTJsvnSXn52f4BMZJJ2id/kW7+r+FJRm+L20gKQ1aqq8d3e/lzRUrv5SMf1TAOe7
|
||||
RDjyGMKy5ZgM2w==
|
||||
-----END CERTIFICATE-----
|
||||
-1
@@ -1 +0,0 @@
|
||||
MIIB3jCCAYigAwIBAgICBTkwDQYJKoZIhvcNAQELBQAwdzELMAkGA1UEBhMCVVMxETAPBgNVBAgMCE1pY2hpZ2FuMRIwEAYDVQQHDAlBbm4gQXJib3IxKzApBgNVBAoMIlVuaXZlcnNpdHkgb2YgTWljaGlnYW4gYW5kIHRoZSBFRkYxFDASBgNVBAMMC2V4YW1wbGUuY29tMB4XDTE0MTIxMTIyMzQ0NVoXDTE0MTIxODIyMzQ0NVowdzELMAkGA1UEBhMCVVMxETAPBgNVBAgMCE1pY2hpZ2FuMRIwEAYDVQQHDAlBbm4gQXJib3IxKzApBgNVBAoMIlVuaXZlcnNpdHkgb2YgTWljaGlnYW4gYW5kIHRoZSBFRkYxFDASBgNVBAMMC2V4YW1wbGUuY29tMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKx1c7RR7R_drnBSQ_zfx1vQLHUbFLh1AQQQ5R8DZUXd36efNK79vukFhN9HFoHZiUvOjm0c-pVE6K-EdE_twuUCAwEAATANBgkqhkiG9w0BAQsFAANBAC24z0IdwIVKSlntksllvr6zJepBH5fMndfk3XJp10jT6VE-14KNtjh02a56GoraAvJAT5_H67E8GvJ_ocNnB_o
|
||||
Vendored
BIN
Binary file not shown.
Vendored
-13
@@ -1,13 +0,0 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIB3jCCAYigAwIBAgICBTkwDQYJKoZIhvcNAQELBQAwdzELMAkGA1UEBhMCVVMx
|
||||
ETAPBgNVBAgMCE1pY2hpZ2FuMRIwEAYDVQQHDAlBbm4gQXJib3IxKzApBgNVBAoM
|
||||
IlVuaXZlcnNpdHkgb2YgTWljaGlnYW4gYW5kIHRoZSBFRkYxFDASBgNVBAMMC2V4
|
||||
YW1wbGUuY29tMB4XDTE0MTIxMTIyMzQ0NVoXDTE0MTIxODIyMzQ0NVowdzELMAkG
|
||||
A1UEBhMCVVMxETAPBgNVBAgMCE1pY2hpZ2FuMRIwEAYDVQQHDAlBbm4gQXJib3Ix
|
||||
KzApBgNVBAoMIlVuaXZlcnNpdHkgb2YgTWljaGlnYW4gYW5kIHRoZSBFRkYxFDAS
|
||||
BgNVBAMMC2V4YW1wbGUuY29tMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKx1c7RR
|
||||
7R/drnBSQ/zfx1vQLHUbFLh1AQQQ5R8DZUXd36efNK79vukFhN9HFoHZiUvOjm0c
|
||||
+pVE6K+EdE/twuUCAwEAATANBgkqhkiG9w0BAQsFAANBAC24z0IdwIVKSlntksll
|
||||
vr6zJepBH5fMndfk3XJp10jT6VE+14KNtjh02a56GoraAvJAT5/H67E8GvJ/ocNn
|
||||
B/o=
|
||||
-----END CERTIFICATE-----
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
agree-dev-preview = True
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
-----BEGIN CERTIFICATE REQUEST-----
|
||||
MIIBuzCCAWUCAQAweTELMAkGA1UEBhMCVVMxETAPBgNVBAgTCE1pY2hpZ2FuMRIw
|
||||
EAYDVQQHEwlBbm4gQXJib3IxDDAKBgNVBAoTA0VGRjEfMB0GA1UECxMWVW5pdmVy
|
||||
c2l0eSBvZiBNaWNoaWdhbjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wXDANBgkqhkiG
|
||||
9w0BAQEFAANLADBIAkEA9LYRcVE3Nr+qleecEcX8JwVDnjeG1X7ucsCasuuZM0e0
|
||||
9cmYuUzxIkMjO/9x4AVcvXXRXPEV+LzWWkfkTlzRMwIDAQABoIGGMIGDBgkqhkiG
|
||||
9w0BCQ4xdjB0MHIGA1UdEQRrMGmCC2V4YW1wbGUuY29tggtleGFtcGxlLm9yZ4IL
|
||||
ZXhhbXBsZS5uZXSCDGV4YW1wbGUuaW5mb4IVc3ViZG9tYWluLmV4YW1wbGUuY29t
|
||||
ghtvdGhlci5zdWJkb21haW4uZXhhbXBsZS5jb20wDQYJKoZIhvcNAQELBQADQQBd
|
||||
k4BE5qvEvkYoZM/2++Xd9RrQ6wsdj0QiJQCozfsI4lQx6ZJnbtNc7HpDrX4W6XIv
|
||||
IvzVBz/nD11drfz/RNuX
|
||||
-----END CERTIFICATE REQUEST-----
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
-----BEGIN CERTIFICATE REQUEST-----
|
||||
MIIBFTCBwAIBADBbMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEh
|
||||
MB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRQwEgYDVQQDDAtleGFt
|
||||
cGxlLm9yZzBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQD0thFxUTc2v6qV55wRxfwn
|
||||
BUOeN4bVfu5ywJqy65kzR7T1yZi5TPEiQyM7/3HgBVy9ddFc8RX4vNZaR+ROXNEz
|
||||
AgMBAAGgADANBgkqhkiG9w0BAQsFAANBAMikGL8Ch7hQCStXH7chhDp6+pt2+VSo
|
||||
wgsrPQ2Bw4veDMlSemUrH+4e0TwbbntHfvXTDHWs9P3BiIDJLxFrjuA=
|
||||
-----END CERTIFICATE REQUEST-----
|
||||
BIN
Binary file not shown.
-10
@@ -1,10 +0,0 @@
|
||||
-----BEGIN CERTIFICATE REQUEST-----
|
||||
MIIBbjCCARgCAQAweTELMAkGA1UEBhMCVVMxETAPBgNVBAgMCE1pY2hpZ2FuMRIw
|
||||
EAYDVQQHDAlBbm4gQXJib3IxDDAKBgNVBAoMA0VGRjEfMB0GA1UECwwWVW5pdmVy
|
||||
c2l0eSBvZiBNaWNoaWdhbjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wXDANBgkqhkiG
|
||||
9w0BAQEFAANLADBIAkEArHVztFHtH92ucFJD/N/HW9AsdRsUuHUBBBDlHwNlRd3f
|
||||
p580rv2+6QWE30cWgdmJS86ObRz6lUTor4R0T+3C5QIDAQABoDowOAYJKoZIhvcN
|
||||
AQkOMSswKTAnBgNVHREEIDAeggtleGFtcGxlLmNvbYIPd3d3LmV4YW1wbGUuY29t
|
||||
MA0GCSqGSIb3DQEBCwUAA0EAZGBM8J1rRs7onFgtc76mOeoT1c3v0ZsEmxQfb2Wy
|
||||
tmReY6X1N4cs38D9VSow+VMRu2LWkKvzS7RUFSaTaeQz1A==
|
||||
-----END CERTIFICATE REQUEST-----
|
||||
Vendored
BIN
Binary file not shown.
Vendored
-10
@@ -1,10 +0,0 @@
|
||||
-----BEGIN CERTIFICATE REQUEST-----
|
||||
MIIBXTCCAQcCAQAweTELMAkGA1UEBhMCVVMxETAPBgNVBAgMCE1pY2hpZ2FuMRIw
|
||||
EAYDVQQHDAlBbm4gQXJib3IxDDAKBgNVBAoMA0VGRjEfMB0GA1UECwwWVW5pdmVy
|
||||
c2l0eSBvZiBNaWNoaWdhbjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wXDANBgkqhkiG
|
||||
9w0BAQEFAANLADBIAkEArHVztFHtH92ucFJD/N/HW9AsdRsUuHUBBBDlHwNlRd3f
|
||||
p580rv2+6QWE30cWgdmJS86ObRz6lUTor4R0T+3C5QIDAQABoCkwJwYJKoZIhvcN
|
||||
AQkOMRowGDAWBgNVHREEDzANggtleGFtcGxlLmNvbTANBgkqhkiG9w0BAQsFAANB
|
||||
AHJH/O6BtC9aGzEVCMGOZ7z9iIRHWSzr9x/bOzn7hLwsbXPAgO1QxEwL+X+4g20G
|
||||
n9XBE1N9W6HCIEut2d8wACg=
|
||||
-----END CERTIFICATE REQUEST-----
|
||||
-14
@@ -1,14 +0,0 @@
|
||||
-----BEGIN DSA PARAMETERS-----
|
||||
MIGdAkEAwebEoGBfokKQeALHHnAZMQwYU35ILEBdV8oUmzv7qpSVUoHihyqfn6GC
|
||||
OixAKSP8EJYcTilIqPbFbfFyOPlbLwIVANoFHEDiQgknAvKrG78pHzAJdQSPAkEA
|
||||
qfka5Bnl+CeEMpzVZGrOVqZE/LFdZK9eT6YtWjzqtIkf3hwXUVxJsTnBG4xmrfvl
|
||||
41pgNJpgu99YOYqPpS0g7A==
|
||||
-----END DSA PARAMETERS-----
|
||||
-----BEGIN DSA PRIVATE KEY-----
|
||||
MIH5AgEAAkEAwebEoGBfokKQeALHHnAZMQwYU35ILEBdV8oUmzv7qpSVUoHihyqf
|
||||
n6GCOixAKSP8EJYcTilIqPbFbfFyOPlbLwIVANoFHEDiQgknAvKrG78pHzAJdQSP
|
||||
AkEAqfka5Bnl+CeEMpzVZGrOVqZE/LFdZK9eT6YtWjzqtIkf3hwXUVxJsTnBG4xm
|
||||
rfvl41pgNJpgu99YOYqPpS0g7AJATQ2LUzjGQSM6UljcPY5I2OD9THkUR9kH2tth
|
||||
zZd70UoI9btrVaTizgqYShuok94glSQNK0H92JgUk3scJPaAkAIVAMDn61h6vrCE
|
||||
mNv063So6E+eYaIN
|
||||
-----END DSA PRIVATE KEY-----
|
||||
-17
@@ -1,17 +0,0 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICuDCCAnWgAwIBAgIJAPjmErVMzwVLMAsGCWCGSAFlAwQDAjB3MQswCQYDVQQG
|
||||
EwJVUzERMA8GA1UECAwITWljaGlnYW4xEjAQBgNVBAcMCUFubiBBcmJvcjErMCkG
|
||||
A1UECgwiVW5pdmVyc2l0eSBvZiBNaWNoaWdhbiBhbmQgdGhlIEVGRjEUMBIGA1UE
|
||||
AwwLZXhhbXBsZS5jb20wHhcNMTUwNTEyMTUzOTQzWhcNMTUwNjExMTUzOTQzWjB3
|
||||
MQswCQYDVQQGEwJVUzERMA8GA1UECAwITWljaGlnYW4xEjAQBgNVBAcMCUFubiBB
|
||||
cmJvcjErMCkGA1UECgwiVW5pdmVyc2l0eSBvZiBNaWNoaWdhbiBhbmQgdGhlIEVG
|
||||
RjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wgfEwgakGByqGSM44BAEwgZ0CQQDB5sSg
|
||||
YF+iQpB4AscecBkxDBhTfkgsQF1XyhSbO/uqlJVSgeKHKp+foYI6LEApI/wQlhxO
|
||||
KUio9sVt8XI4+VsvAhUA2gUcQOJCCScC8qsbvykfMAl1BI8CQQCp+RrkGeX4J4Qy
|
||||
nNVkas5WpkT8sV1kr15Ppi1aPOq0iR/eHBdRXEmxOcEbjGat++XjWmA0mmC731g5
|
||||
io+lLSDsA0MAAkBNDYtTOMZBIzpSWNw9jkjY4P1MeRRH2Qfa22HNl3vRSgj1u2tV
|
||||
pOLOCphKG6iT3iCVJA0rQf3YmBSTexwk9oCQo1AwTjAdBgNVHQ4EFgQUZ2DlTDGU
|
||||
PMwTUt0KztM6IyX61BcwHwYDVR0jBBgwFoAUZ2DlTDGUPMwTUt0KztM6IyX61Bcw
|
||||
DAYDVR0TBAUwAwEB/zALBglghkgBZQMEAwIDMAAwLQIVAIbMgGx+KwBr4rgqZ2Lh
|
||||
AAO8TegHAhQsuxpIIIphiReoWEtEJk4TqEIz/A==
|
||||
-----END CERTIFICATE-----
|
||||
@@ -1 +0,0 @@
|
||||
../../archive/sample-renewal/cert1.pem
|
||||
@@ -1 +0,0 @@
|
||||
../../archive/sample-renewal/chain1.pem
|
||||
@@ -1 +0,0 @@
|
||||
../../archive/sample-renewal/fullchain1.pem
|
||||
@@ -1 +0,0 @@
|
||||
../../archive/sample-renewal/privkey1.pem
|
||||
-14
@@ -1,14 +0,0 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICNzCCAeGgAwIBAgIJALizm9Y3q620MA0GCSqGSIb3DQEBCwUAMHcxCzAJBgNV
|
||||
BAYTAlVTMREwDwYDVQQIDAhNaWNoaWdhbjESMBAGA1UEBwwJQW5uIEFyYm9yMSsw
|
||||
KQYDVQQKDCJVbml2ZXJzaXR5IG9mIE1pY2hpZ2FuIGFuZCB0aGUgRUZGMRQwEgYD
|
||||
VQQDDAtleGFtcGxlLmNvbTAeFw0xNTA1MDkwMDI0NTJaFw0xNjA1MDgwMDI0NTJa
|
||||
MHcxCzAJBgNVBAYTAlVTMREwDwYDVQQIDAhNaWNoaWdhbjESMBAGA1UEBwwJQW5u
|
||||
IEFyYm9yMSswKQYDVQQKDCJVbml2ZXJzaXR5IG9mIE1pY2hpZ2FuIGFuZCB0aGUg
|
||||
RUZGMRQwEgYDVQQDDAtleGFtcGxlLmNvbTBcMA0GCSqGSIb3DQEBAQUAA0sAMEgC
|
||||
QQD0thFxUTc2v6qV55wRxfwnBUOeN4bVfu5ywJqy65kzR7T1yZi5TPEiQyM7/3Hg
|
||||
BVy9ddFc8RX4vNZaR+ROXNEzAgMBAAGjUDBOMB0GA1UdDgQWBBRJieHEVSHKmBk0
|
||||
mTExx1erzlylCjAfBgNVHSMEGDAWgBRJieHEVSHKmBk0mTExx1erzlylCjAMBgNV
|
||||
HRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA0EABT/nlpqOaanFSLZmWIrKv0zt63k4
|
||||
bmWNMA8fYT45KYpLomsW8qXdpC82IlVKfNk7fW0UYT3HOeDSJRcycxNCTQ==
|
||||
-----END CERTIFICATE-----
|
||||
-6
@@ -1,6 +0,0 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIGrAgEAAiEAm2Fylv+Uz7trgTW8EBHP3FQSMeZs2GNQ6VRo1sIVJEkCAwEAAQIh
|
||||
AJT0BA/xD01dFCAXzSNyj9nfSZa3NpqzJZZn/eOm7vghAhEAzUVNZn4lLLBD1R6N
|
||||
E8TKNQIRAMHHyn3O5JeY36lwKwkUlEUCEAliRauN0L0+QZuYjfJ9aJECEGx4dru3
|
||||
rTPCyighdqWNlHUCEQCiLjlwSRtWgmMBudCkVjzt
|
||||
-----END RSA PRIVATE KEY-----
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIBOgIBAAJBAKx1c7RR7R/drnBSQ/zfx1vQLHUbFLh1AQQQ5R8DZUXd36efNK79
|
||||
vukFhN9HFoHZiUvOjm0c+pVE6K+EdE/twuUCAwEAAQJAMbrEnJCrQe8YqAbw1/Bn
|
||||
elAzIamndfE3U8bTavf9sgFpS4HL83rhd6PDbvx81ucaJAT/5x048fM/nFl4fzAc
|
||||
mQIhAOF/a9o3EIsDKEmUl+Z1OaOiUxDF3kqWSmALEsmvDhwXAiEAw8ljV5RO/rUp
|
||||
Zu2YMDFq3MKpyyMgBIJ8CxmGRc6gCmMCIGRQzkcmhfqBrhOFwkmozrqIBRIKJIjj
|
||||
8TRm2LXWZZ2DAiAqVO7PztdNpynugUy4jtbGKKjBrTSNBRGA7OHlUgm0dQIhALQq
|
||||
6oGU29Vxlvt3k0vmiRKU4AVfLyNXIGtcWcNG46h/
|
||||
-----END RSA PRIVATE KEY-----
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIBOwIBAAJBAPS2EXFRNza/qpXnnBHF/CcFQ543htV+7nLAmrLrmTNHtPXJmLlM
|
||||
8SJDIzv/ceAFXL110VzxFfi81lpH5E5c0TMCAwEAAQJBALmppYQ/JVARjWBcsEm/
|
||||
1/bXBJ127YLv4gQIY5baL4r6IdEE33OXMTTmD9wf+ajuq1eaH0htHkwhOvREu0sz
|
||||
bskCIQD/Cg+xhEVLcwK3pFp3afPIhj1IPFiL3Uy/nqyMZ6O/RQIhAPWiDBofp7Cp
|
||||
J4dGZs+hkRySq/IOeeRJlNK1Pq64nToXAiBZ7+te1100YSd5KT051SRB94zO13EG
|
||||
SZESFduVW8rz3QIgK+tLiqg6TYYRQUi/PUTAM4GuKNuZw828RGiPyqHLywUCIQCd
|
||||
pkZrNphL/y0D7HSbPIfZzD90M2V8tUjlK0BTqk1bHA==
|
||||
-----END RSA PRIVATE KEY-----
|
||||
@@ -1,75 +0,0 @@
|
||||
cert = MAGICDIR/live/sample-renewal/cert.pem
|
||||
privkey = MAGICDIR/live/sample-renewal/privkey.pem
|
||||
chain = MAGICDIR/live/sample-renewal/chain.pem
|
||||
fullchain = MAGICDIR/live/sample-renewal/fullchain.pem
|
||||
renew_before_expiry = 1 year
|
||||
|
||||
# Options and defaults used in the renewal process
|
||||
[renewalparams]
|
||||
no_self_upgrade = False
|
||||
apache_enmod = a2enmod
|
||||
no_verify_ssl = False
|
||||
ifaces = None
|
||||
apache_dismod = a2dismod
|
||||
register_unsafely_without_email = False
|
||||
apache_handle_modules = True
|
||||
uir = None
|
||||
installer = None
|
||||
nginx_ctl = nginx
|
||||
config_dir = MAGICDIR
|
||||
text_mode = False
|
||||
func = <function obtain_cert at 0x7f093a163c08>
|
||||
staging = True
|
||||
prepare = False
|
||||
work_dir = /var/lib/letsencrypt
|
||||
tos = False
|
||||
init = False
|
||||
http01_port = 80
|
||||
duplicate = False
|
||||
noninteractive_mode = True
|
||||
key_path = None
|
||||
nginx = False
|
||||
nginx_server_root = /etc/nginx
|
||||
fullchain_path = /home/ubuntu/letsencrypt/chain.pem
|
||||
email = None
|
||||
csr = None
|
||||
agree_dev_preview = None
|
||||
redirect = None
|
||||
verb = certonly
|
||||
verbose_count = -3
|
||||
config_file = None
|
||||
renew_by_default = False
|
||||
hsts = False
|
||||
apache_handle_sites = True
|
||||
authenticator = webroot
|
||||
domains = isnot.org,
|
||||
rsa_key_size = 2048
|
||||
apache_challenge_location = /etc/apache2
|
||||
checkpoints = 1
|
||||
manual_test_mode = False
|
||||
apache = False
|
||||
cert_path = /home/ubuntu/letsencrypt/cert.pem
|
||||
webroot_path = /var/www/
|
||||
reinstall = False
|
||||
expand = False
|
||||
strict_permissions = False
|
||||
apache_server_root = /etc/apache2
|
||||
account = None
|
||||
dry_run = False
|
||||
manual_public_ip_logging_ok = False
|
||||
chain_path = /home/ubuntu/letsencrypt/chain.pem
|
||||
break_my_certs = False
|
||||
standalone = True
|
||||
manual = False
|
||||
server = https://acme-staging.api.letsencrypt.org/directory
|
||||
standalone_supported_challenges = "tls-sni-01,http-01"
|
||||
webroot = True
|
||||
os_packages_only = False
|
||||
apache_init_script = None
|
||||
user_agent = None
|
||||
apache_le_vhost_ext = -le-ssl.conf
|
||||
debug = False
|
||||
tls_sni_01_port = 443
|
||||
logs_dir = /var/log/letsencrypt
|
||||
apache_vhost_root = /etc/apache2/sites-available
|
||||
configurator = None
|
||||
-76
@@ -1,76 +0,0 @@
|
||||
cert = MAGICDIR/live/sample-renewal/cert.pem
|
||||
privkey = MAGICDIR/live/sample-renewal/privkey.pem
|
||||
chain = MAGICDIR/live/sample-renewal/chain.pem
|
||||
fullchain = MAGICDIR/live/sample-renewal/fullchain.pem
|
||||
renew_before_expiry = 4 years
|
||||
|
||||
# Options and defaults used in the renewal process
|
||||
[renewalparams]
|
||||
no_self_upgrade = False
|
||||
apache_enmod = a2enmod
|
||||
no_verify_ssl = False
|
||||
ifaces = None
|
||||
apache_dismod = a2dismod
|
||||
register_unsafely_without_email = False
|
||||
apache_handle_modules = True
|
||||
uir = None
|
||||
installer = None
|
||||
nginx_ctl = nginx
|
||||
config_dir = MAGICDIR
|
||||
text_mode = False
|
||||
func = <function obtain_cert at 0x7f093a163c08>
|
||||
staging = True
|
||||
prepare = False
|
||||
work_dir = /var/lib/letsencrypt
|
||||
tos = False
|
||||
init = False
|
||||
http01_port = 80
|
||||
duplicate = False
|
||||
noninteractive_mode = True
|
||||
key_path = None
|
||||
nginx = False
|
||||
nginx_server_root = /etc/nginx
|
||||
fullchain_path = /home/ubuntu/letsencrypt/chain.pem
|
||||
email = None
|
||||
csr = None
|
||||
agree_dev_preview = None
|
||||
redirect = None
|
||||
verb = certonly
|
||||
verbose_count = -3
|
||||
config_file = None
|
||||
renew_by_default = False
|
||||
hsts = False
|
||||
apache_handle_sites = True
|
||||
authenticator = standalone
|
||||
domains = isnot.org,
|
||||
rsa_key_size = 2048
|
||||
apache_challenge_location = /etc/apache2
|
||||
checkpoints = 1
|
||||
manual_test_mode = False
|
||||
apache = False
|
||||
cert_path = /home/ubuntu/letsencrypt/cert.pem
|
||||
webroot_path = None
|
||||
reinstall = False
|
||||
expand = False
|
||||
strict_permissions = False
|
||||
apache_server_root = /etc/apache2
|
||||
account = None
|
||||
dry_run = False
|
||||
manual_public_ip_logging_ok = False
|
||||
chain_path = /home/ubuntu/letsencrypt/chain.pem
|
||||
break_my_certs = False
|
||||
standalone = True
|
||||
manual = False
|
||||
server = https://acme-staging.api.letsencrypt.org/directory
|
||||
standalone_supported_challenges = "tls-sni-01,http-01"
|
||||
webroot = False
|
||||
os_packages_only = False
|
||||
apache_init_script = None
|
||||
user_agent = None
|
||||
apache_le_vhost_ext = -le-ssl.conf
|
||||
debug = False
|
||||
tls_sni_01_port = 443
|
||||
logs_dir = /var/log/letsencrypt
|
||||
apache_vhost_root = /etc/apache2/sites-available
|
||||
configurator = None
|
||||
[[webroot_map]]
|
||||
@@ -1,3 +0,0 @@
|
||||
webroot
|
||||
webroot-path = /tmp
|
||||
domains = eg.com, eg2.com
|
||||
Reference in New Issue
Block a user