1"""Test for certbot_apache._internal.configurator for Centos overrides"""
2import unittest
3
4try:
5    import mock
6except ImportError: # pragma: no cover
7    from unittest import mock # type: ignore
8
9from certbot import errors
10from certbot.compat import filesystem
11from certbot.compat import os
12from certbot_apache._internal import obj
13from certbot_apache._internal import override_centos
14import util
15
16
17def get_vh_truth(temp_dir, config_name):
18    """Return the ground truth for the specified directory."""
19    prefix = os.path.join(
20        temp_dir, config_name, "httpd/conf.d")
21
22    aug_pre = "/files" + prefix
23    vh_truth = [
24        obj.VirtualHost(
25            os.path.join(prefix, "centos.example.com.conf"),
26            os.path.join(aug_pre, "centos.example.com.conf/VirtualHost"),
27            {obj.Addr.fromstring("*:80")},
28            False, True, "centos.example.com"),
29        obj.VirtualHost(
30            os.path.join(prefix, "ssl.conf"),
31            os.path.join(aug_pre, "ssl.conf/VirtualHost"),
32            {obj.Addr.fromstring("_default_:443")},
33            True, True, None)
34    ]
35    return vh_truth
36
37class FedoraRestartTest(util.ApacheTest):
38    """Tests for Fedora specific self-signed certificate override"""
39
40    def setUp(self):  # pylint: disable=arguments-differ
41        test_dir = "centos7_apache/apache"
42        config_root = "centos7_apache/apache/httpd"
43        vhost_root = "centos7_apache/apache/httpd/conf.d"
44        super().setUp(test_dir=test_dir,
45                      config_root=config_root,
46                      vhost_root=vhost_root)
47        self.config = util.get_apache_configurator(
48            self.config_path, self.vhost_path, self.config_dir, self.work_dir,
49            os_info="fedora_old")
50        self.vh_truth = get_vh_truth(
51            self.temp_dir, "centos7_apache/apache")
52
53    def _run_fedora_test(self):
54        self.assertIsInstance(self.config, override_centos.CentOSConfigurator)
55        with mock.patch("certbot.util.get_os_info") as mock_info:
56            mock_info.return_value = ["fedora", "28"]
57            self.config.config_test()
58
59    def test_non_fedora_error(self):
60        c_test = "certbot_apache._internal.configurator.ApacheConfigurator.config_test"
61        with mock.patch(c_test) as mock_test:
62            mock_test.side_effect = errors.MisconfigurationError
63            with mock.patch("certbot.util.get_os_info") as mock_info:
64                mock_info.return_value = ["not_fedora"]
65                self.assertRaises(errors.MisconfigurationError,
66                                  self.config.config_test)
67
68    def test_fedora_restart_error(self):
69        c_test = "certbot_apache._internal.configurator.ApacheConfigurator.config_test"
70        with mock.patch(c_test) as mock_test:
71            # First call raises error, second doesn't
72            mock_test.side_effect = [errors.MisconfigurationError, '']
73            with mock.patch("certbot.util.run_script") as mock_run:
74                mock_run.side_effect = errors.SubprocessError
75                self.assertRaises(errors.MisconfigurationError,
76                                  self._run_fedora_test)
77
78    def test_fedora_restart(self):
79        c_test = "certbot_apache._internal.configurator.ApacheConfigurator.config_test"
80        with mock.patch(c_test) as mock_test:
81            with mock.patch("certbot.util.run_script") as mock_run:
82                # First call raises error, second doesn't
83                mock_test.side_effect = [errors.MisconfigurationError, '']
84                self._run_fedora_test()
85                self.assertEqual(mock_test.call_count, 2)
86                self.assertEqual(mock_run.call_args[0][0],
87                                ['systemctl', 'restart', 'httpd'])
88
89
90class MultipleVhostsTestCentOS(util.ApacheTest):
91    """Multiple vhost tests for CentOS / RHEL family of distros"""
92
93    _multiprocess_can_split_ = True
94
95    def setUp(self):  # pylint: disable=arguments-differ
96        test_dir = "centos7_apache/apache"
97        config_root = "centos7_apache/apache/httpd"
98        vhost_root = "centos7_apache/apache/httpd/conf.d"
99        super().setUp(test_dir=test_dir,
100                      config_root=config_root,
101                      vhost_root=vhost_root)
102
103        self.config = util.get_apache_configurator(
104            self.config_path, self.vhost_path, self.config_dir, self.work_dir,
105            os_info="centos")
106        self.vh_truth = get_vh_truth(
107            self.temp_dir, "centos7_apache/apache")
108
109    def test_get_parser(self):
110        self.assertIsInstance(self.config.parser, override_centos.CentOSParser)
111
112    @mock.patch("certbot_apache._internal.apache_util._get_runtime_cfg")
113    def test_opportunistic_httpd_runtime_parsing(self, mock_get):
114        define_val = (
115            'Define: TEST1\n'
116            'Define: TEST2\n'
117            'Define: DUMP_RUN_CFG\n'
118        )
119        mod_val = (
120            'Loaded Modules:\n'
121            ' mock_module (static)\n'
122            ' another_module (static)\n'
123        )
124        def mock_get_cfg(command):
125            """Mock httpd process stdout"""
126            if command == ['apachectl', '-t', '-D', 'DUMP_RUN_CFG']:
127                return define_val
128            elif command == ['apachectl', '-t', '-D', 'DUMP_MODULES']:
129                return mod_val
130            return ""
131        mock_get.side_effect = mock_get_cfg
132        self.config.parser.modules = {}
133        self.config.parser.variables = {}
134
135        with mock.patch("certbot.util.get_os_info") as mock_osi:
136            # Make sure we have the have the CentOS httpd constants
137            mock_osi.return_value = ("centos", "7")
138            self.config.parser.update_runtime_variables()
139
140        self.assertEqual(mock_get.call_count, 3)
141        self.assertEqual(len(self.config.parser.modules), 4)
142        self.assertEqual(len(self.config.parser.variables), 2)
143        self.assertTrue("TEST2" in self.config.parser.variables)
144        self.assertTrue("mod_another.c" in self.config.parser.modules)
145
146    def test_get_virtual_hosts(self):
147        """Make sure all vhosts are being properly found."""
148        vhs = self.config.get_virtual_hosts()
149        self.assertEqual(len(vhs), 2)
150        found = 0
151
152        for vhost in vhs:
153            for centos_truth in self.vh_truth:
154                if vhost == centos_truth:
155                    found += 1
156                    break
157            else:
158                raise Exception("Missed: %s" % vhost)  # pragma: no cover
159        self.assertEqual(found, 2)
160
161    @mock.patch("certbot_apache._internal.apache_util._get_runtime_cfg")
162    def test_get_sysconfig_vars(self, mock_cfg):
163        """Make sure we read the sysconfig OPTIONS variable correctly"""
164        # Return nothing for the process calls
165        mock_cfg.return_value = ""
166        self.config.parser.sysconfig_filep = filesystem.realpath(
167            os.path.join(self.config.parser.root, "../sysconfig/httpd"))
168        self.config.parser.variables = {}
169
170        with mock.patch("certbot.util.get_os_info") as mock_osi:
171            # Make sure we have the have the CentOS httpd constants
172            mock_osi.return_value = ("centos", "7")
173            self.config.parser.update_runtime_variables()
174
175        self.assertTrue("mock_define" in self.config.parser.variables)
176        self.assertTrue("mock_define_too" in self.config.parser.variables)
177        self.assertTrue("mock_value" in self.config.parser.variables)
178        self.assertEqual("TRUE", self.config.parser.variables["mock_value"])
179        self.assertTrue("MOCK_NOSEP" in self.config.parser.variables)
180        self.assertEqual("NOSEP_VAL", self.config.parser.variables["NOSEP_TWO"])
181
182    @mock.patch("certbot_apache._internal.configurator.util.run_script")
183    def test_alt_restart_works(self, mock_run_script):
184        mock_run_script.side_effect = [None, errors.SubprocessError, None]
185        self.config.restart()
186        self.assertEqual(mock_run_script.call_count, 3)
187
188    @mock.patch("certbot_apache._internal.configurator.util.run_script")
189    def test_alt_restart_errors(self, mock_run_script):
190        mock_run_script.side_effect = [None,
191                                       errors.SubprocessError,
192                                       errors.SubprocessError]
193        self.assertRaises(errors.MisconfigurationError, self.config.restart)
194
195
196if __name__ == "__main__":
197    unittest.main()  # pragma: no cover
198