1"""Test for certbot_apache._internal.configurator for CentOS 6 overrides"""
2import unittest
3from unittest import mock
4
5from certbot.compat import os
6from certbot.errors import MisconfigurationError
7from certbot_apache._internal import obj
8from certbot_apache._internal import override_centos
9from certbot_apache._internal import parser
10import util
11
12
13def get_vh_truth(temp_dir, config_name):
14    """Return the ground truth for the specified directory."""
15    prefix = os.path.join(
16        temp_dir, config_name, "httpd/conf.d")
17
18    aug_pre = "/files" + prefix
19    vh_truth = [
20        obj.VirtualHost(
21            os.path.join(prefix, "test.example.com.conf"),
22            os.path.join(aug_pre, "test.example.com.conf/VirtualHost"),
23            {obj.Addr.fromstring("*:80")},
24            False, True, "test.example.com"),
25        obj.VirtualHost(
26            os.path.join(prefix, "ssl.conf"),
27            os.path.join(aug_pre, "ssl.conf/VirtualHost"),
28            {obj.Addr.fromstring("_default_:443")},
29            True, True, None)
30    ]
31    return vh_truth
32
33class CentOS6Tests(util.ApacheTest):
34    """Tests for CentOS 6"""
35
36    def setUp(self):  # pylint: disable=arguments-differ
37        test_dir = "centos6_apache/apache"
38        config_root = "centos6_apache/apache/httpd"
39        vhost_root = "centos6_apache/apache/httpd/conf.d"
40        super().setUp(test_dir=test_dir,
41                      config_root=config_root,
42                      vhost_root=vhost_root)
43
44        self.config = util.get_apache_configurator(
45            self.config_path, self.vhost_path, self.config_dir, self.work_dir,
46            version=(2, 2, 15), os_info="centos")
47        self.vh_truth = get_vh_truth(
48            self.temp_dir, "centos6_apache/apache")
49
50    def test_get_parser(self):
51        self.assertTrue(isinstance(self.config.parser,
52                                   override_centos.CentOSParser))
53
54    def test_get_virtual_hosts(self):
55        """Make sure all vhosts are being properly found."""
56        vhs = self.config.get_virtual_hosts()
57        self.assertEqual(len(vhs), 2)
58        found = 0
59
60        for vhost in vhs:
61            for centos_truth in self.vh_truth:
62                if vhost == centos_truth:
63                    found += 1
64                    break
65            else:
66                raise Exception("Missed: %s" % vhost)  # pragma: no cover
67        self.assertEqual(found, 2)
68
69    @mock.patch("certbot_apache._internal.configurator.display_util.notify")
70    def test_loadmod_default(self, unused_mock_notify):
71        ssl_loadmods = self.config.parser.find_dir(
72            "LoadModule", "ssl_module", exclude=False)
73        self.assertEqual(len(ssl_loadmods), 1)
74        # Make sure the LoadModule ssl_module is in ssl.conf (default)
75        self.assertTrue("ssl.conf" in ssl_loadmods[0])
76        # ...and that it's not inside of <IfModule>
77        self.assertFalse("IfModule" in ssl_loadmods[0])
78
79        # Get the example vhost
80        self.config.assoc["test.example.com"] = self.vh_truth[0]
81        self.config.deploy_cert(
82            "random.demo", "example/cert.pem", "example/key.pem",
83            "example/cert_chain.pem", "example/fullchain.pem")
84        self.config.save()
85
86        post_loadmods = self.config.parser.find_dir(
87            "LoadModule", "ssl_module", exclude=False)
88
89        # We should now have LoadModule ssl_module in root conf and ssl.conf
90        self.assertEqual(len(post_loadmods), 2)
91        for lm in post_loadmods:
92            # lm[:-7] removes "/arg[#]" from the path
93            arguments = self.config.parser.get_all_args(lm[:-7])
94            self.assertEqual(arguments, ["ssl_module", "modules/mod_ssl.so"])
95            # ...and both of them should be wrapped in <IfModule !mod_ssl.c>
96            # lm[:-17] strips off /directive/arg[1] from the path.
97            ifmod_args = self.config.parser.get_all_args(lm[:-17])
98            self.assertTrue("!mod_ssl.c" in ifmod_args)
99
100    @mock.patch("certbot_apache._internal.configurator.display_util.notify")
101    def test_loadmod_multiple(self, unused_mock_notify):
102        sslmod_args = ["ssl_module", "modules/mod_ssl.so"]
103        # Adds another LoadModule to main httpd.conf in addtition to ssl.conf
104        self.config.parser.add_dir(self.config.parser.loc["default"], "LoadModule",
105                                   sslmod_args)
106        self.config.save()
107        pre_loadmods = self.config.parser.find_dir(
108            "LoadModule", "ssl_module", exclude=False)
109        # LoadModules are not within IfModule blocks
110        self.assertFalse(any("ifmodule" in m.lower() for m in pre_loadmods))
111        self.config.assoc["test.example.com"] = self.vh_truth[0]
112        self.config.deploy_cert(
113            "random.demo", "example/cert.pem", "example/key.pem",
114            "example/cert_chain.pem", "example/fullchain.pem")
115        post_loadmods = self.config.parser.find_dir(
116            "LoadModule", "ssl_module", exclude=False)
117
118        for mod in post_loadmods:
119            self.assertTrue(self.config.parser.not_modssl_ifmodule(mod))  #pylint: disable=no-member
120
121    @mock.patch("certbot_apache._internal.configurator.display_util.notify")
122    def test_loadmod_rootconf_exists(self, unused_mock_notify):
123        sslmod_args = ["ssl_module", "modules/mod_ssl.so"]
124        rootconf_ifmod = self.config.parser.get_ifmod(
125            parser.get_aug_path(self.config.parser.loc["default"]),
126            "!mod_ssl.c", beginning=True)
127        self.config.parser.add_dir(rootconf_ifmod[:-1], "LoadModule", sslmod_args)
128        self.config.save()
129        # Get the example vhost
130        self.config.assoc["test.example.com"] = self.vh_truth[0]
131        self.config.deploy_cert(
132            "random.demo", "example/cert.pem", "example/key.pem",
133            "example/cert_chain.pem", "example/fullchain.pem")
134        self.config.save()
135
136        root_loadmods = self.config.parser.find_dir(
137            "LoadModule", "ssl_module",
138            start=parser.get_aug_path(self.config.parser.loc["default"]),
139            exclude=False)
140
141        mods = [lm for lm in root_loadmods if self.config.parser.loc["default"] in lm]
142
143        self.assertEqual(len(mods), 1)
144        # [:-7] removes "/arg[#]" from the path
145        self.assertEqual(
146            self.config.parser.get_all_args(mods[0][:-7]),
147            sslmod_args)
148
149    @mock.patch("certbot_apache._internal.configurator.display_util.notify")
150    def test_neg_loadmod_already_on_path(self, unused_mock_notify):
151        loadmod_args = ["ssl_module", "modules/mod_ssl.so"]
152        ifmod = self.config.parser.get_ifmod(
153            self.vh_truth[1].path, "!mod_ssl.c", beginning=True)
154        self.config.parser.add_dir(ifmod[:-1], "LoadModule", loadmod_args)
155        self.config.parser.add_dir(self.vh_truth[1].path, "LoadModule", loadmod_args)
156        self.config.save()
157        pre_loadmods = self.config.parser.find_dir(
158            "LoadModule", "ssl_module", start=self.vh_truth[1].path, exclude=False)
159        self.assertEqual(len(pre_loadmods), 2)
160        # The ssl.conf now has two LoadModule directives, one inside of
161        # !mod_ssl.c IfModule
162        self.config.assoc["test.example.com"] = self.vh_truth[0]
163        self.config.deploy_cert(
164            "random.demo", "example/cert.pem", "example/key.pem",
165            "example/cert_chain.pem", "example/fullchain.pem")
166        self.config.save()
167        # Ensure that the additional LoadModule wasn't written into the IfModule
168        post_loadmods = self.config.parser.find_dir(
169            "LoadModule", "ssl_module", start=self.vh_truth[1].path, exclude=False)
170        self.assertEqual(len(post_loadmods), 1)
171
172    def test_loadmod_non_duplicate(self):
173        # the modules/mod_ssl.so exists in ssl.conf
174        sslmod_args = ["ssl_module", "modules/mod_somethingelse.so"]
175        rootconf_ifmod = self.config.parser.get_ifmod(
176            parser.get_aug_path(self.config.parser.loc["default"]),
177            "!mod_ssl.c", beginning=True)
178        self.config.parser.add_dir(rootconf_ifmod[:-1], "LoadModule", sslmod_args)
179        self.config.save()
180        self.config.assoc["test.example.com"] = self.vh_truth[0]
181        pre_matches = self.config.parser.find_dir("LoadModule",
182                                                  "ssl_module", exclude=False)
183
184        self.assertRaises(MisconfigurationError, self.config.deploy_cert,
185                "random.demo", "example/cert.pem", "example/key.pem",
186                "example/cert_chain.pem", "example/fullchain.pem")
187
188        post_matches = self.config.parser.find_dir("LoadModule",
189                                                   "ssl_module", exclude=False)
190        # Make sure that none was changed
191        self.assertEqual(pre_matches, post_matches)
192
193    @mock.patch("certbot_apache._internal.configurator.display_util.notify")
194    def test_loadmod_not_found(self, unused_mock_notify):
195        # Remove all existing LoadModule ssl_module... directives
196        orig_loadmods = self.config.parser.find_dir("LoadModule",
197                                                    "ssl_module",
198                                                    exclude=False)
199        for mod in orig_loadmods:
200            noarg_path = mod.rpartition("/")[0]
201            self.config.parser.aug.remove(noarg_path)
202        self.config.save()
203        self.config.deploy_cert(
204            "random.demo", "example/cert.pem", "example/key.pem",
205            "example/cert_chain.pem", "example/fullchain.pem")
206
207        post_loadmods = self.config.parser.find_dir("LoadModule",
208                                                    "ssl_module",
209                                                    exclude=False)
210        self.assertFalse(post_loadmods)
211
212    def test_no_ifmod_search_false(self):
213        #pylint: disable=no-member
214
215        self.assertFalse(self.config.parser.not_modssl_ifmodule(
216            "/path/does/not/include/ifmod"
217        ))
218        self.assertFalse(self.config.parser.not_modssl_ifmodule(
219            ""
220        ))
221        self.assertFalse(self.config.parser.not_modssl_ifmodule(
222            "/path/includes/IfModule/but/no/arguments"
223        ))
224
225
226if __name__ == "__main__":
227    unittest.main()  # pragma: no cover
228