mirror of
https://github.com/certbot/certbot.git
synced 2026-06-04 14:26:10 -04:00
- Finishing refactor of postconf/postfix command-line utilities - Plugin uses starttls_policy plugin to specify per-domain policies Cleaning up TLS policy code. Print warning when setting configuration parameter that is overridden by master. Update client to use new policy API Cleanup and test fixes Documentation fix smaller fixes Policy is now an enhancement and reverting works Added a README, and small documentation fixes throughout Moving testing infra from starttls repo to certbot-postfix fixing tests and lint Changes against new policy API starttls-everywhere => starttls-policy testing(postfix): Added more varieties of certificates to test against. Moar fixes against policy API. Address comments on README and setup.py Address small comments on postconf and util Address comments in installer Python 3 fixes and Postconf tester extends TempDir test class Mock out postconf calls from tests and test coverage for master overrides More various fixes. Everything minus testing done Remove STARTTLS policy enhancement from this branch. sphinx quickstart 99% test coverage some cleanup and testfixing cleanup leftover files Remove print statement testfix for python 3.4 Revert dockerfile change mypy fix fix(postfix): brad's comments test(postfix): coverage to 100 test(postfix): mypy import mypy types fix(postfix docs): add .rst files and fix build fix(postfix): tls_only and server_only params behave nicely together some cleanup lint fix more comments bump version number
314 lines
14 KiB
Python
314 lines
14 KiB
Python
"""Tests for certbot_postfix.installer."""
|
|
from contextlib import contextmanager
|
|
import copy
|
|
import functools
|
|
import os
|
|
import pkg_resources
|
|
import six
|
|
import unittest
|
|
|
|
import mock
|
|
|
|
from certbot import errors
|
|
from certbot.tests import util as certbot_test_util
|
|
|
|
# pylint: disable=unused-import, no-name-in-module
|
|
from acme.magic_typing import Dict, Tuple, Union
|
|
# pylint: enable=unused-import, no-name-in-module
|
|
|
|
DEFAULT_MAIN_CF = {
|
|
"smtpd_tls_cert_file": "",
|
|
"smtpd_tls_key_file": "",
|
|
"smtpd_tls_dh1024_param_file": "",
|
|
"smtpd_tls_security_level": "none",
|
|
"smtpd_tls_auth_only": "",
|
|
"smtpd_tls_mandatory_protocols": "",
|
|
"smtpd_tls_protocols": "",
|
|
"smtpd_tls_ciphers": "",
|
|
"smtpd_tls_exclude_ciphers": "",
|
|
"smtpd_tls_mandatory_ciphers": "",
|
|
"smtpd_tls_eecdh_grade": "medium",
|
|
"smtp_tls_security_level": "",
|
|
"smtp_tls_ciphers": "",
|
|
"smtp_tls_exclude_ciphers": "",
|
|
"smtp_tls_mandatory_ciphers": "",
|
|
"mail_version": "3.2.3"
|
|
}
|
|
|
|
def _main_cf_with(obj):
|
|
main_cf = copy.copy(DEFAULT_MAIN_CF)
|
|
main_cf.update(obj)
|
|
return main_cf
|
|
|
|
class InstallerTest(certbot_test_util.ConfigTestCase):
|
|
# pylint: disable=too-many-public-methods
|
|
|
|
def setUp(self):
|
|
super(InstallerTest, self).setUp()
|
|
_config_file = pkg_resources.resource_filename("certbot_postfix.tests",
|
|
os.path.join("testdata", "config.json"))
|
|
self.config.postfix_ctl = "postfix"
|
|
self.config.postfix_config_dir = self.tempdir
|
|
self.config.postfix_config_utility = "postconf"
|
|
self.config.postfix_tls_only = False
|
|
self.config.postfix_server_only = False
|
|
self.config.config_dir = self.tempdir
|
|
|
|
@mock.patch("certbot_postfix.installer.util.is_acceptable_value")
|
|
def test_set_vars(self, mock_is_acceptable_value):
|
|
mock_is_acceptable_value.return_value = True
|
|
with create_installer(self.config) as installer:
|
|
installer.prepare()
|
|
mock_is_acceptable_value.return_value = False
|
|
|
|
@mock.patch("certbot_postfix.installer.util.is_acceptable_value")
|
|
def test_acceptable_value(self, mock_is_acceptable_value):
|
|
mock_is_acceptable_value.return_value = True
|
|
with create_installer(self.config) as installer:
|
|
installer.prepare()
|
|
mock_is_acceptable_value.return_value = False
|
|
|
|
@certbot_test_util.patch_get_utility()
|
|
def test_confirm_changes_no_raises_error(self, mock_util):
|
|
mock_util().yesno.return_value = False
|
|
with create_installer(self.config) as installer:
|
|
installer.prepare()
|
|
self.assertRaises(errors.PluginError, installer.deploy_cert,
|
|
"example.com", "cert_path", "key_path",
|
|
"chain_path", "fullchain_path")
|
|
|
|
@certbot_test_util.patch_get_utility()
|
|
def test_save(self, mock_util):
|
|
mock_util().yesno.return_value = True
|
|
with create_installer(self.config) as installer:
|
|
installer.prepare()
|
|
installer.postconf.flush = mock.Mock()
|
|
installer.reverter = mock.Mock()
|
|
installer.deploy_cert("example.com", "cert_path", "key_path",
|
|
"chain_path", "fullchain_path")
|
|
installer.save()
|
|
self.assertEqual(installer.save_notes, [])
|
|
self.assertEqual(installer.postconf.flush.call_count, 1)
|
|
self.assertEqual(installer.reverter.add_to_checkpoint.call_count, 1)
|
|
|
|
@certbot_test_util.patch_get_utility()
|
|
def test_save_with_title(self, mock_util):
|
|
mock_util().yesno.return_value = True
|
|
with create_installer(self.config) as installer:
|
|
installer.prepare()
|
|
installer.postconf.flush = mock.Mock()
|
|
installer.reverter = mock.Mock()
|
|
installer.deploy_cert("example.com", "cert_path", "key_path",
|
|
"chain_path", "fullchain_path")
|
|
installer.save(title="new_file!")
|
|
self.assertEqual(installer.reverter.finalize_checkpoint.call_count, 1)
|
|
|
|
@certbot_test_util.patch_get_utility()
|
|
def test_rollback_checkpoints_resets_postconf(self, mock_util):
|
|
mock_util().yesno.return_value = True
|
|
with create_installer(self.config) as installer:
|
|
installer.prepare()
|
|
installer.deploy_cert("example.com", "cert_path", "key_path",
|
|
"chain_path", "fullchain_path")
|
|
installer.rollback_checkpoints()
|
|
self.assertEqual(installer.postconf.get_changes(), {})
|
|
|
|
@certbot_test_util.patch_get_utility()
|
|
def test_recovery_routine_resets_postconf(self, mock_util):
|
|
mock_util().yesno.return_value = True
|
|
with create_installer(self.config) as installer:
|
|
installer.prepare()
|
|
installer.deploy_cert("example.com", "cert_path", "key_path",
|
|
"chain_path", "fullchain_path")
|
|
installer.recovery_routine()
|
|
self.assertEqual(installer.postconf.get_changes(), {})
|
|
|
|
def test_restart(self):
|
|
with create_installer(self.config) as installer:
|
|
installer.prepare()
|
|
installer.restart()
|
|
self.assertEqual(installer.postfix.restart.call_count, 1)
|
|
|
|
def test_add_parser_arguments(self):
|
|
options = set(("ctl", "config-dir", "config-utility",
|
|
"tls-only", "server-only", "ignore-master-overrides"))
|
|
mock_add = mock.MagicMock()
|
|
|
|
from certbot_postfix import installer
|
|
installer.Installer.add_parser_arguments(mock_add)
|
|
|
|
for call in mock_add.call_args_list:
|
|
self.assertTrue(call[0][0] in options)
|
|
|
|
def test_no_postconf_prepare(self):
|
|
with create_installer(self.config) as installer:
|
|
installer_path = "certbot_postfix.installer"
|
|
exe_exists_path = installer_path + ".certbot_util.exe_exists"
|
|
path_surgery_path = "certbot_postfix.util.plugins_util.path_surgery"
|
|
with mock.patch(path_surgery_path, return_value=False):
|
|
with mock.patch(exe_exists_path, return_value=False):
|
|
self.assertRaises(errors.NoInstallationError,
|
|
installer.prepare)
|
|
|
|
def test_old_version(self):
|
|
with create_installer(self.config, main_cf=_main_cf_with({"mail_version": "0.0.1"}))\
|
|
as installer:
|
|
self.assertRaises(errors.NotSupportedError, installer.prepare)
|
|
|
|
def test_lock_error(self):
|
|
with create_installer(self.config) as installer:
|
|
assert_raises = functools.partial(self.assertRaises,
|
|
errors.PluginError,
|
|
installer.prepare)
|
|
certbot_test_util.lock_and_call(assert_raises, self.tempdir)
|
|
|
|
|
|
@mock.patch('certbot.util.lock_dir_until_exit')
|
|
def test_dir_locked(self, lock_dir):
|
|
with create_installer(self.config) as installer:
|
|
lock_dir.side_effect = errors.LockError
|
|
self.assertRaises(errors.PluginError, installer.prepare)
|
|
|
|
def test_more_info(self):
|
|
with create_installer(self.config) as installer:
|
|
installer.prepare()
|
|
output = installer.more_info()
|
|
self.assertTrue("Postfix" in output)
|
|
self.assertTrue(self.tempdir in output)
|
|
self.assertTrue(DEFAULT_MAIN_CF["mail_version"] in output)
|
|
|
|
def test_get_all_names(self):
|
|
config = {"mydomain": "example.org",
|
|
"myhostname": "mail.example.org",
|
|
"myorigin": "example.org"}
|
|
with create_installer(self.config, main_cf=_main_cf_with(config)) as installer:
|
|
installer.prepare()
|
|
result = installer.get_all_names()
|
|
self.assertEqual(result, set(config.values()))
|
|
|
|
@certbot_test_util.patch_get_utility()
|
|
def test_deploy(self, mock_util):
|
|
mock_util().yesno.return_value = True
|
|
from certbot_postfix import constants
|
|
with create_installer(self.config) as installer:
|
|
installer.prepare()
|
|
|
|
# pylint: disable=protected-access
|
|
installer.deploy_cert("example.com", "cert_path", "key_path",
|
|
"chain_path", "fullchain_path")
|
|
changes = installer.postconf.get_changes()
|
|
expected = {} # type: Dict[str, Tuple[str, ...]]
|
|
expected.update(constants.TLS_SERVER_VARS)
|
|
expected.update(constants.DEFAULT_SERVER_VARS)
|
|
expected.update(constants.DEFAULT_CLIENT_VARS)
|
|
self.assertEqual(changes["smtpd_tls_key_file"], "key_path")
|
|
self.assertEqual(changes["smtpd_tls_cert_file"], "cert_path")
|
|
for name, value in six.iteritems(expected):
|
|
self.assertEqual(changes[name], value[0])
|
|
|
|
@certbot_test_util.patch_get_utility()
|
|
def test_tls_only(self, mock_util):
|
|
mock_util().yesno.return_value = True
|
|
with create_installer(self.config) as installer:
|
|
installer.prepare()
|
|
installer.conf = lambda x: x == "tls_only"
|
|
installer.postconf.set = mock.Mock()
|
|
installer.deploy_cert("example.com", "cert_path", "key_path",
|
|
"chain_path", "fullchain_path")
|
|
self.assertEqual(installer.postconf.set.call_count, 4)
|
|
|
|
@certbot_test_util.patch_get_utility()
|
|
def test_server_only(self, mock_util):
|
|
mock_util().yesno.return_value = True
|
|
with create_installer(self.config) as installer:
|
|
installer.prepare()
|
|
installer.conf = lambda x: x == "server_only"
|
|
installer.postconf.set = mock.Mock()
|
|
installer.deploy_cert("example.com", "cert_path", "key_path",
|
|
"chain_path", "fullchain_path")
|
|
self.assertEqual(installer.postconf.set.call_count, 11)
|
|
|
|
@certbot_test_util.patch_get_utility()
|
|
def test_tls_and_server_only(self, mock_util):
|
|
mock_util().yesno.return_value = True
|
|
with create_installer(self.config) as installer:
|
|
installer.prepare()
|
|
installer.conf = lambda x: True
|
|
installer.postconf.set = mock.Mock()
|
|
installer.deploy_cert("example.com", "cert_path", "key_path",
|
|
"chain_path", "fullchain_path")
|
|
self.assertEqual(installer.postconf.set.call_count, 3)
|
|
|
|
@certbot_test_util.patch_get_utility()
|
|
def test_deploy_twice(self, mock_util):
|
|
# Deploying twice on the same installer shouldn't do anything!
|
|
mock_util().yesno.return_value = True
|
|
with create_installer(self.config) as installer:
|
|
installer.prepare()
|
|
from certbot_postfix.postconf import ConfigMain
|
|
with mock.patch.object(ConfigMain, "set", wraps=installer.postconf.set) as fake_set:
|
|
installer.deploy_cert("example.com", "cert_path", "key_path",
|
|
"chain_path", "fullchain_path")
|
|
self.assertEqual(fake_set.call_count, 15)
|
|
fake_set.reset_mock()
|
|
installer.deploy_cert("example.com", "cert_path", "key_path",
|
|
"chain_path", "fullchain_path")
|
|
fake_set.assert_not_called()
|
|
|
|
@certbot_test_util.patch_get_utility()
|
|
def test_deploy_already_secure(self, mock_util):
|
|
# Should not overwrite "more-secure" parameters
|
|
mock_util().yesno.return_value = True
|
|
more_secure = {
|
|
"smtpd_tls_security_level": "encrypt",
|
|
"smtpd_tls_protocols": "!SSLv3, !SSLv2, !TLSv1",
|
|
"smtpd_tls_eecdh_grade": "strong"
|
|
}
|
|
with create_installer(self.config,\
|
|
main_cf=_main_cf_with(more_secure)) as installer:
|
|
installer.prepare()
|
|
installer.deploy_cert("example.com", "cert_path", "key_path",
|
|
"chain_path", "fullchain_path")
|
|
for param in more_secure.keys():
|
|
self.assertFalse(param in installer.postconf.get_changes())
|
|
|
|
def test_enhance(self):
|
|
with create_installer(self.config) as installer:
|
|
installer.prepare()
|
|
self.assertRaises(errors.PluginError,
|
|
installer.enhance,
|
|
"example.org", "redirect")
|
|
|
|
def test_supported_enhancements(self):
|
|
with create_installer(self.config) as installer:
|
|
installer.prepare()
|
|
self.assertEqual(installer.supported_enhancements(), [])
|
|
|
|
@contextmanager
|
|
def create_installer(config, main_cf=DEFAULT_MAIN_CF):
|
|
# pylint: disable=dangerous-default-value
|
|
"""Creates a Postfix installer with calls to `postconf` and `postfix` mocked out.
|
|
|
|
In particular, creates a ConfigMain object that does regular things, but seeds it
|
|
with values from `main_cf` and `master_cf` dicts.
|
|
"""
|
|
from certbot_postfix.postconf import ConfigMain
|
|
from certbot_postfix import installer
|
|
def _mock_init_postconf(postconf, executable, ignore_master_overrides=False, config_dir=None):
|
|
# pylint: disable=protected-access,unused-argument
|
|
postconf._ignore_master_overrides = ignore_master_overrides
|
|
postconf._db = main_cf
|
|
postconf._master_db = {}
|
|
postconf._updated = {}
|
|
# override get_default to get from main
|
|
postconf.get_default = lambda name: main_cf[name]
|
|
with mock.patch.object(ConfigMain, "__init__", _mock_init_postconf):
|
|
exe_exists_path = "certbot_postfix.installer.certbot_util.exe_exists"
|
|
with mock.patch(exe_exists_path, return_value=True):
|
|
with mock.patch("certbot_postfix.installer.util.PostfixUtil",
|
|
return_value=mock.Mock()):
|
|
yield installer.Installer(config, "postfix")
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main() # pragma: no cover
|
|
|