mirror of
https://github.com/certbot/certbot.git
synced 2026-07-31 18:34:41 +02:00
Enhance verb (#5596)
* Add the cli parameters * Tests and error messages * Requested fixes * Only handle SSL vhosts * Interactive cert-name selection if not defined on CLI * Address PR comments * Address review comments * Added tests and addressed review comments * Move cert manager tests to correct file * Add display ops tests * Use display util constants instead of hardcoded values in tests
This commit is contained in:
committed by
Brad Warren
parent
261d063b10
commit
a024aaf59d
@@ -569,5 +569,103 @@ class MatchAndCheckOverlaps(storage_test.BaseRenewableCertTest):
|
||||
self.assertRaises(errors.OverlappingMatchFound, self._call, self.config, None, None, None)
|
||||
|
||||
|
||||
class GetCertnameTest(unittest.TestCase):
|
||||
"""Tests for certbot.cert_manager."""
|
||||
|
||||
def setUp(self):
|
||||
self.get_utility_patch = test_util.patch_get_utility()
|
||||
self.mock_get_utility = self.get_utility_patch.start()
|
||||
self.config = mock.MagicMock()
|
||||
self.config.certname = None
|
||||
|
||||
def tearDown(self):
|
||||
self.get_utility_patch.stop()
|
||||
|
||||
@mock.patch('certbot.storage.renewal_conf_files')
|
||||
@mock.patch('certbot.storage.lineagename_for_filename')
|
||||
def test_get_certnames(self, mock_name, mock_files):
|
||||
mock_files.return_value = ['example.com.conf']
|
||||
mock_name.return_value = 'example.com'
|
||||
from certbot import cert_manager
|
||||
prompt = "Which certificate would you"
|
||||
self.mock_get_utility().menu.return_value = (display_util.OK, 0)
|
||||
self.assertEquals(
|
||||
cert_manager.get_certnames(
|
||||
self.config, "verb", allow_multiple=False), ['example.com'])
|
||||
self.assertTrue(
|
||||
prompt in self.mock_get_utility().menu.call_args[0][0])
|
||||
|
||||
@mock.patch('certbot.storage.renewal_conf_files')
|
||||
@mock.patch('certbot.storage.lineagename_for_filename')
|
||||
def test_get_certnames_custom_prompt(self, mock_name, mock_files):
|
||||
mock_files.return_value = ['example.com.conf']
|
||||
mock_name.return_value = 'example.com'
|
||||
from certbot import cert_manager
|
||||
prompt = "custom prompt"
|
||||
self.mock_get_utility().menu.return_value = (display_util.OK, 0)
|
||||
self.assertEquals(
|
||||
cert_manager.get_certnames(
|
||||
self.config, "verb", allow_multiple=False, custom_prompt=prompt),
|
||||
['example.com'])
|
||||
self.assertEquals(self.mock_get_utility().menu.call_args[0][0],
|
||||
prompt)
|
||||
|
||||
@mock.patch('certbot.storage.renewal_conf_files')
|
||||
@mock.patch('certbot.storage.lineagename_for_filename')
|
||||
def test_get_certnames_user_abort(self, mock_name, mock_files):
|
||||
mock_files.return_value = ['example.com.conf']
|
||||
mock_name.return_value = 'example.com'
|
||||
from certbot import cert_manager
|
||||
self.mock_get_utility().menu.return_value = (display_util.CANCEL, 0)
|
||||
self.assertRaises(
|
||||
errors.Error,
|
||||
cert_manager.get_certnames,
|
||||
self.config, "erroring_anyway", allow_multiple=False)
|
||||
|
||||
@mock.patch('certbot.storage.renewal_conf_files')
|
||||
@mock.patch('certbot.storage.lineagename_for_filename')
|
||||
def test_get_certnames_allow_multiple(self, mock_name, mock_files):
|
||||
mock_files.return_value = ['example.com.conf']
|
||||
mock_name.return_value = 'example.com'
|
||||
from certbot import cert_manager
|
||||
prompt = "Which certificate(s) would you"
|
||||
self.mock_get_utility().checklist.return_value = (display_util.OK,
|
||||
['example.com'])
|
||||
self.assertEquals(
|
||||
cert_manager.get_certnames(
|
||||
self.config, "verb", allow_multiple=True), ['example.com'])
|
||||
self.assertTrue(
|
||||
prompt in self.mock_get_utility().checklist.call_args[0][0])
|
||||
|
||||
@mock.patch('certbot.storage.renewal_conf_files')
|
||||
@mock.patch('certbot.storage.lineagename_for_filename')
|
||||
def test_get_certnames_allow_multiple_custom_prompt(self, mock_name, mock_files):
|
||||
mock_files.return_value = ['example.com.conf']
|
||||
mock_name.return_value = 'example.com'
|
||||
from certbot import cert_manager
|
||||
prompt = "custom prompt"
|
||||
self.mock_get_utility().checklist.return_value = (display_util.OK,
|
||||
['example.com'])
|
||||
self.assertEquals(
|
||||
cert_manager.get_certnames(
|
||||
self.config, "verb", allow_multiple=True, custom_prompt=prompt),
|
||||
['example.com'])
|
||||
self.assertEquals(
|
||||
self.mock_get_utility().checklist.call_args[0][0],
|
||||
prompt)
|
||||
|
||||
@mock.patch('certbot.storage.renewal_conf_files')
|
||||
@mock.patch('certbot.storage.lineagename_for_filename')
|
||||
def test_get_certnames_allow_multiple_user_abort(self, mock_name, mock_files):
|
||||
mock_files.return_value = ['example.com.conf']
|
||||
mock_name.return_value = 'example.com'
|
||||
from certbot import cert_manager
|
||||
self.mock_get_utility().checklist.return_value = (display_util.CANCEL, [])
|
||||
self.assertRaises(
|
||||
errors.Error,
|
||||
cert_manager.get_certnames,
|
||||
self.config, "erroring_anyway", allow_multiple=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main() # pragma: no cover
|
||||
|
||||
@@ -433,6 +433,22 @@ class EnhanceConfigTest(ClientTestCommon):
|
||||
self.client.installer.enhance.assert_not_called()
|
||||
mock_enhancements.ask.assert_not_called()
|
||||
|
||||
@mock.patch("certbot.client.logger")
|
||||
def test_already_exists_header(self, mock_log):
|
||||
self.config.hsts = True
|
||||
self._test_with_already_existing()
|
||||
self.assertTrue(mock_log.warning.called)
|
||||
self.assertEquals(mock_log.warning.call_args[0][1],
|
||||
'Strict-Transport-Security')
|
||||
|
||||
@mock.patch("certbot.client.logger")
|
||||
def test_already_exists_redirect(self, mock_log):
|
||||
self.config.redirect = True
|
||||
self._test_with_already_existing()
|
||||
self.assertTrue(mock_log.warning.called)
|
||||
self.assertEquals(mock_log.warning.call_args[0][1],
|
||||
'redirect')
|
||||
|
||||
def test_no_ask_hsts(self):
|
||||
self.config.hsts = True
|
||||
self._test_with_all_supported()
|
||||
@@ -508,6 +524,13 @@ class EnhanceConfigTest(ClientTestCommon):
|
||||
self.assertEqual(self.client.installer.save.call_count, 1)
|
||||
self.assertEqual(self.client.installer.restart.call_count, 1)
|
||||
|
||||
def _test_with_already_existing(self):
|
||||
self.client.installer = mock.MagicMock()
|
||||
self.client.installer.supported_enhancements.return_value = [
|
||||
"ensure-http-header", "redirect", "staple-ocsp"]
|
||||
self.client.installer.enhance.side_effect = errors.PluginEnhancementAlreadyPresent()
|
||||
self.client.enhance_config([self.domain], None)
|
||||
|
||||
|
||||
class RollbackTest(unittest.TestCase):
|
||||
"""Tests for certbot.client.rollback."""
|
||||
|
||||
@@ -207,9 +207,9 @@ class ChooseNamesTest(unittest.TestCase):
|
||||
self.mock_install = mock.MagicMock()
|
||||
|
||||
@classmethod
|
||||
def _call(cls, installer):
|
||||
def _call(cls, installer, question=None):
|
||||
from certbot.display.ops import choose_names
|
||||
return choose_names(installer)
|
||||
return choose_names(installer, question)
|
||||
|
||||
@mock.patch("certbot.display.ops._choose_names_manually")
|
||||
def test_no_installer(self, mock_manual):
|
||||
@@ -281,6 +281,15 @@ class ChooseNamesTest(unittest.TestCase):
|
||||
self.assertEqual(names, ["example.com"])
|
||||
self.assertEqual(mock_util().checklist.call_count, 1)
|
||||
|
||||
@test_util.patch_get_utility("certbot.display.ops.z_util")
|
||||
def test_filter_namees_override_question(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, "Custom")
|
||||
self.assertEqual(names, ["example.com"])
|
||||
self.assertEqual(mock_util().checklist.call_count, 1)
|
||||
self.assertEqual(mock_util().checklist.call_args[0][0], "Custom")
|
||||
|
||||
@test_util.patch_get_utility("certbot.display.ops.z_util")
|
||||
def test_filter_names_nothing_selected(self, mock_util):
|
||||
self.mock_install.get_all_names.return_value = set(["example.com"])
|
||||
@@ -481,5 +490,42 @@ class ValidatorTests(unittest.TestCase):
|
||||
self.__validator, "msg", default="")
|
||||
|
||||
|
||||
class ChooseValuesTest(unittest.TestCase):
|
||||
"""Test choose_values."""
|
||||
@classmethod
|
||||
def _call(cls, values, question):
|
||||
from certbot.display.ops import choose_values
|
||||
return choose_values(values, question)
|
||||
|
||||
@test_util.patch_get_utility("certbot.display.ops.z_util")
|
||||
def test_choose_names_success(self, mock_util):
|
||||
items = ["first", "second", "third"]
|
||||
mock_util().checklist.return_value = (display_util.OK, [items[2]])
|
||||
result = self._call(items, None)
|
||||
self.assertEquals(result, [items[2]])
|
||||
self.assertTrue(mock_util().checklist.called)
|
||||
self.assertEquals(mock_util().checklist.call_args[0][0], None)
|
||||
|
||||
@test_util.patch_get_utility("certbot.display.ops.z_util")
|
||||
def test_choose_names_success_question(self, mock_util):
|
||||
items = ["first", "second", "third"]
|
||||
question = "Which one?"
|
||||
mock_util().checklist.return_value = (display_util.OK, [items[1]])
|
||||
result = self._call(items, question)
|
||||
self.assertEquals(result, [items[1]])
|
||||
self.assertTrue(mock_util().checklist.called)
|
||||
self.assertEquals(mock_util().checklist.call_args[0][0], question)
|
||||
|
||||
@test_util.patch_get_utility("certbot.display.ops.z_util")
|
||||
def test_choose_names_user_cancel(self, mock_util):
|
||||
items = ["first", "second", "third"]
|
||||
question = "Want to cancel?"
|
||||
mock_util().checklist.return_value = (display_util.CANCEL, [])
|
||||
result = self._call(items, question)
|
||||
self.assertEquals(result, [])
|
||||
self.assertTrue(mock_util().checklist.called)
|
||||
self.assertEquals(mock_util().checklist.call_args[0][0], question)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main() # pragma: no cover
|
||||
|
||||
@@ -1533,5 +1533,114 @@ class MakeOrVerifyNeededDirs(test_util.ConfigTestCase):
|
||||
strict=self.config.strict_permissions)
|
||||
|
||||
|
||||
class EnhanceTest(unittest.TestCase):
|
||||
"""Tests for certbot.main.enhance."""
|
||||
|
||||
def setUp(self):
|
||||
self.get_utility_patch = test_util.patch_get_utility()
|
||||
self.mock_get_utility = self.get_utility_patch.start()
|
||||
|
||||
def tearDown(self):
|
||||
self.get_utility_patch.stop()
|
||||
|
||||
def _call(self, args):
|
||||
plugins = disco.PluginsRegistry.find_all()
|
||||
config = configuration.NamespaceConfig(
|
||||
cli.prepare_and_parse_args(plugins, args))
|
||||
|
||||
with mock.patch('certbot.cert_manager.get_certnames') as mock_certs:
|
||||
mock_certs.return_value = ['example.com']
|
||||
with mock.patch('certbot.cert_manager.domains_for_certname') as mock_dom:
|
||||
mock_dom.return_value = ['example.com']
|
||||
with mock.patch('certbot.main._init_le_client') as mock_init:
|
||||
mock_client = mock.MagicMock()
|
||||
mock_client.config = config
|
||||
mock_init.return_value = mock_client
|
||||
main.enhance(config, plugins)
|
||||
return mock_client # returns the client
|
||||
|
||||
@mock.patch('certbot.main.plug_sel.record_chosen_plugins')
|
||||
@mock.patch('certbot.cert_manager.lineage_for_certname')
|
||||
@mock.patch('certbot.main.display_ops.choose_values')
|
||||
@mock.patch('certbot.main._find_domains_or_certname')
|
||||
def test_selection_question(self, mock_find, mock_choose, mock_lineage, _rec):
|
||||
mock_lineage.return_value = mock.MagicMock(chain_path="/tmp/nonexistent")
|
||||
mock_choose.return_value = ['example.com']
|
||||
mock_find.return_value = (None, None)
|
||||
with mock.patch('certbot.main.plug_sel.pick_installer') as mock_pick:
|
||||
self._call(['enhance', '--redirect'])
|
||||
self.assertTrue(mock_pick.called)
|
||||
# Check that the message includes "enhancements"
|
||||
self.assertTrue("enhancements" in mock_pick.call_args[0][3])
|
||||
|
||||
@mock.patch('certbot.main.plug_sel.record_chosen_plugins')
|
||||
@mock.patch('certbot.cert_manager.lineage_for_certname')
|
||||
@mock.patch('certbot.main.display_ops.choose_values')
|
||||
@mock.patch('certbot.main._find_domains_or_certname')
|
||||
def test_selection_auth_warning(self, mock_find, mock_choose, mock_lineage, _rec):
|
||||
mock_lineage.return_value = mock.MagicMock(chain_path="/tmp/nonexistent")
|
||||
mock_choose.return_value = ["example.com"]
|
||||
mock_find.return_value = (None, None)
|
||||
with mock.patch('certbot.main.plug_sel.pick_installer'):
|
||||
with mock.patch('certbot.main.plug_sel.logger.warning') as mock_log:
|
||||
mock_client = self._call(['enhance', '-a', 'webroot', '--redirect'])
|
||||
self.assertTrue(mock_log.called)
|
||||
self.assertTrue("make sense" in mock_log.call_args[0][0])
|
||||
self.assertTrue(mock_client.enhance_config.called)
|
||||
|
||||
@mock.patch('certbot.cert_manager.lineage_for_certname')
|
||||
@mock.patch('certbot.main.display_ops.choose_values')
|
||||
@mock.patch('certbot.main.plug_sel.record_chosen_plugins')
|
||||
def test_enhance_config_call(self, _rec, mock_choose, mock_lineage):
|
||||
mock_lineage.return_value = mock.MagicMock(chain_path="/tmp/nonexistent")
|
||||
mock_choose.return_value = ["example.com"]
|
||||
with mock.patch('certbot.main.plug_sel.pick_installer'):
|
||||
mock_client = self._call(['enhance', '--redirect', '--hsts'])
|
||||
req_enh = ["redirect", "hsts"]
|
||||
not_req_enh = ["uir"]
|
||||
self.assertTrue(mock_client.enhance_config.called)
|
||||
self.assertTrue(
|
||||
all([getattr(mock_client.config, e) for e in req_enh]))
|
||||
self.assertFalse(
|
||||
any([getattr(mock_client.config, e) for e in not_req_enh]))
|
||||
self.assertTrue(
|
||||
"example.com" in mock_client.enhance_config.call_args[0][0])
|
||||
|
||||
@mock.patch('certbot.cert_manager.lineage_for_certname')
|
||||
@mock.patch('certbot.main.display_ops.choose_values')
|
||||
@mock.patch('certbot.main.plug_sel.record_chosen_plugins')
|
||||
def test_enhance_noninteractive(self, _rec, mock_choose, mock_lineage):
|
||||
mock_lineage.return_value = mock.MagicMock(
|
||||
chain_path="/tmp/nonexistent")
|
||||
mock_choose.return_value = ["example.com"]
|
||||
with mock.patch('certbot.main.plug_sel.pick_installer'):
|
||||
mock_client = self._call(['enhance', '--redirect',
|
||||
'--hsts', '--non-interactive'])
|
||||
self.assertTrue(mock_client.enhance_config.called)
|
||||
self.assertFalse(mock_choose.called)
|
||||
|
||||
@mock.patch('certbot.main.display_ops.choose_values')
|
||||
@mock.patch('certbot.main.plug_sel.record_chosen_plugins')
|
||||
def test_user_abort_domains(self, _rec, mock_choose):
|
||||
mock_choose.return_value = []
|
||||
with mock.patch('certbot.main.plug_sel.pick_installer'):
|
||||
self.assertRaises(errors.Error,
|
||||
self._call,
|
||||
['enhance', '--redirect', '--hsts'])
|
||||
|
||||
def test_no_enhancements_defined(self):
|
||||
self.assertRaises(errors.MisconfigurationError,
|
||||
self._call, ['enhance'])
|
||||
|
||||
@mock.patch('certbot.main.plug_sel.choose_configurator_plugins')
|
||||
@mock.patch('certbot.main.display_ops.choose_values')
|
||||
@mock.patch('certbot.main.plug_sel.record_chosen_plugins')
|
||||
def test_plugin_selection_error(self, _rec, mock_choose, mock_pick):
|
||||
mock_choose.return_value = ["example.com"]
|
||||
mock_pick.return_value = (None, None)
|
||||
mock_pick.side_effect = errors.PluginSelectionError()
|
||||
mock_client = self._call(['enhance', '--hsts'])
|
||||
self.assertFalse(mock_client.enhance_config.called)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main() # pragma: no cover
|
||||
|
||||
Reference in New Issue
Block a user