1# Copyright 2007 Google Inc.
2#
3# This program is free software; you can redistribute it and/or
4# modify it under the terms of the GNU General Public License
5# as published by the Free Software Foundation; either version 2
6# of the License, or (at your option) any later version.
7#
8# This program is distributed in the hope that it will be useful,
9# but WITHOUT ANY WARRANTY; without even the implied warranty of
10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11# GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License
14# along with this program; if not, write to the Free Software Foundation,
15# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
16"""Unit tests for nss_cache/config.py."""
17
18__author__ = 'vasilios@google.com (Vasilios Hoffman)'
19
20import os
21import shutil
22import tempfile
23import unittest
24
25from nss_cache import config
26
27
28class TestConfig(unittest.TestCase):
29    """Unit tests for config.Config()."""
30
31    def testConfigInit(self):
32        env = {'NSSCACHE_CONFIG': 'test.conf'}
33        conf = config.Config(env)
34
35        self.assertEqual(conf.config_file,
36                         env['NSSCACHE_CONFIG'],
37                         msg='Failed to override NSSCACHE_CONFIG.')
38
39
40class TestMapOptions(unittest.TestCase):
41    """Unit tests for config.MapOptions()."""
42
43    def testMapOptionsInit(self):
44        mapconfig = config.MapOptions()
45        self.assertTrue(isinstance(mapconfig.cache, dict))
46        self.assertTrue(isinstance(mapconfig.source, dict))
47
48
49class TestClassMethods(unittest.TestCase):
50    """Unit tests for class-level methods in config.py."""
51
52    def setUp(self):
53        # create a directory with a writeable copy of nsscache.conf in it
54        self.workdir = tempfile.mkdtemp()
55        conf_filename = 'nsscache.conf'
56        self.conf_filename = os.path.join(self.workdir, conf_filename)
57        shutil.copy(conf_filename, self.conf_filename)
58        os.chmod(self.conf_filename, 0o640)
59
60        # prepare a config object with this config
61        self.conf = config.Config({})
62        self.conf.config_file = self.conf_filename
63
64    def tearDown(self):
65        shutil.rmtree(self.workdir)
66
67    def testLoadConfigSingleMap(self):
68        conf_file = open(self.conf_filename, 'w')
69        conf_file.write('[DEFAULT]\n'
70                        'source = foo\n'
71                        'cache = foo\n'
72                        'maps = foo\n'
73                        'timestamp_dir = foo\n')
74        conf_file.close()
75
76        config.LoadConfig(self.conf)
77
78        self.assertEqual(['foo'], self.conf.maps)
79
80    def testLoadConfigTwoMaps(self):
81        conf_file = open(self.conf_filename, 'w')
82        conf_file.write('[DEFAULT]\n'
83                        'source = foo\n'
84                        'cache = foo\n'
85                        'maps = foo, bar\n'
86                        'timestamp_dir = foo\n')
87        conf_file.close()
88
89        config.LoadConfig(self.conf)
90
91        self.assertEqual(['foo', 'bar'], self.conf.maps)
92
93    def testLoadConfigMapsWhitespace(self):
94        conf_file = open(self.conf_filename, 'w')
95        conf_file.write('[DEFAULT]\n'
96                        'source = foo\n'
97                        'cache = foo\n'
98                        'maps = foo,  bar  , baz\n'
99                        'timestamp_dir = foo\n')
100        conf_file.close()
101
102        config.LoadConfig(self.conf)
103
104        self.assertEqual(['foo', 'bar', 'baz'], self.conf.maps)
105
106    def testLoadConfigExample(self):
107        """Test that we parse and load the example config.
108
109        Note that this also tests MapOptions() creation and our overriding
110        of defaults in LoadConfig.
111
112        This requires that nsscache.conf exists in the top of the source tree.
113        Changes to the configuration options may break this test.
114        """
115        conf = self.conf
116        config.LoadConfig(conf)
117        passwd = conf.options['passwd']
118        group = conf.options['group']
119        shadow = conf.options['shadow']
120        automount = conf.options['automount']
121
122        self.assertTrue(isinstance(passwd, config.MapOptions))
123        self.assertTrue(isinstance(group, config.MapOptions))
124        self.assertTrue(isinstance(shadow, config.MapOptions))
125        self.assertTrue(isinstance(automount, config.MapOptions))
126
127        self.assertEqual(passwd.source['name'], 'ldap')
128        self.assertEqual(group.source['name'], 'ldap')
129        self.assertEqual(shadow.source['name'], 'ldap')
130        self.assertEqual(automount.source['name'], 'ldap')
131
132        self.assertEqual(passwd.cache['name'], 'files')
133        self.assertEqual(group.cache['name'], 'files')
134        self.assertEqual(shadow.cache['name'], 'files')
135        self.assertEqual(automount.cache['name'], 'files')
136
137        self.assertEqual(passwd.source['base'], 'ou=people,dc=example,dc=com')
138        self.assertEqual(passwd.source['filter'], '(objectclass=posixAccount)')
139
140        self.assertEqual(group.source['base'], 'ou=group,dc=example,dc=com')
141        self.assertEqual(group.source['filter'], '(objectclass=posixGroup)')
142
143    def testLoadConfigOptionalDefaults(self):
144        conf_file = open(self.conf_filename, 'w')
145        conf_file.write('[DEFAULT]\n'
146                        'source = foo\n'
147                        'cache = foo\n'
148                        'maps = foo,  bar  , baz\n'
149                        'lockfile = foo\n'
150                        'timestamp_dir = foo\n')
151        conf_file.close()
152
153        config.LoadConfig(self.conf)
154
155        self.assertEqual(self.conf.lockfile, 'foo')
156
157    def testLoadConfigStripQuotesFromStrings(self):
158        conf_file = open(self.conf_filename, 'w')
159        conf_file.write('[DEFAULT]\n'
160                        'source = "ldap"\n'  # needs to be ldap due to magic
161                        'cache = \'b\'ar\'\n'
162                        'maps = quux\n'
163                        'timestamp_dir = foo\n'
164                        'ldap_tls_require_cert = \'blah\'\n'
165                        '[quux]\n'
166                        'ldap_klingon = "qep\'a\' wa\'maH loS\'DIch"\n')
167        conf_file.close()
168        config.LoadConfig(self.conf)
169        self.assertEqual('ldap', self.conf.options['quux'].source['name'])
170        self.assertEqual('b\'ar', self.conf.options['quux'].cache['name'])
171        self.assertEqual('blah',
172                         self.conf.options['quux'].source['tls_require_cert'])
173        self.assertEqual('qep\'a\' wa\'maH loS\'DIch',
174                         self.conf.options['quux'].source['klingon'])
175
176    def testLoadConfigConvertsNumbers(self):
177        conf_file = open(self.conf_filename, 'w')
178        conf_file.write('[DEFAULT]\n'
179                        'source = foo\n'
180                        'cache = foo\n'
181                        'maps = foo\n'
182                        'timestamp_dir = foo\n'
183                        'foo_string = test\n'
184                        'foo_float = 1.23\n'
185                        'foo_int = 1\n')
186        conf_file.close()
187
188        config.LoadConfig(self.conf)
189
190        foo_dict = self.conf.options['foo'].source
191        self.assertTrue(isinstance(foo_dict['string'], str))
192        self.assertTrue(isinstance(foo_dict['float'], float))
193        self.assertTrue(isinstance(foo_dict['int'], int))
194        self.assertEqual(foo_dict['string'], 'test')
195        self.assertEqual(foo_dict['float'], 1.23)
196        self.assertEqual(foo_dict['int'], 1)
197
198    def testOptions(self):
199        # check the empty case.
200        options = config.Options([], 'foo')
201        self.assertEqual(options, {})
202
203        # create a list like from ConfigParser.items()
204        items = [('maps', 'foo, bar, foobar'), ('nssdb_dir', '/path/to/dir'),
205                 ('ldap_uri', 'TEST_URI'), ('source', 'foo'), ('cache', 'bar'),
206                 ('ldap_base', 'TEST_BASE'), ('ldap_filter', 'TEST_FILTER')]
207
208        options = config.Options(items, 'ldap')
209
210        self.assertTrue('uri' in options)
211        self.assertTrue('base' in options)
212        self.assertTrue('filter' in options)
213
214        self.assertEqual(options['uri'], 'TEST_URI')
215        self.assertEqual(options['base'], 'TEST_BASE')
216        self.assertEqual(options['filter'], 'TEST_FILTER')
217
218    def testParseNSSwitchConf(self):
219        nsswitch_filename = os.path.join(self.workdir, 'nsswitch.conf')
220        nsswitch_file = open(nsswitch_filename, 'w')
221        nsswitch_file.write('passwd: files db\n')
222        nsswitch_file.write('group: files db\n')
223        nsswitch_file.write('shadow: files db\n')
224        nsswitch_file.close()
225        expected_switch = {
226            'passwd': ['files', 'db'],
227            'group': ['files', 'db'],
228            'shadow': ['files', 'db']
229        }
230        self.assertEqual(expected_switch,
231                         config.ParseNSSwitchConf(nsswitch_filename))
232        os.unlink(nsswitch_filename)
233
234    def testVerifyConfiguration(self):
235        conf_file = open(self.conf_filename, 'w')
236        conf_file.write('[DEFAULT]\n'
237                        'source = foo\n'
238                        'cache = foo\n'
239                        'maps = passwd, group, shadow\n'
240                        'timestamp_dir = foo\n')
241        conf_file.close()
242        config.LoadConfig(self.conf)
243        nsswitch_filename = os.path.join(self.workdir, 'nsswitch.conf')
244        nsswitch_file = open(nsswitch_filename, 'w')
245        nsswitch_file.write('passwd: files db\n')
246        nsswitch_file.write('group: files db\n')
247        nsswitch_file.write('shadow: files db\n')
248        nsswitch_file.close()
249        self.assertEqual((0, 0),
250                         config.VerifyConfiguration(self.conf,
251                                                    nsswitch_filename))
252        os.unlink(nsswitch_filename)
253
254    def testVerifyConfigurationWithCache(self):
255        conf_file = open(self.conf_filename, 'w')
256        conf_file.write('[DEFAULT]\n'
257                        'source = foo\n'
258                        'cache = files\n'
259                        'maps = passwd, group, shadow\n'
260                        'timestamp_dir = foo\n'
261                        'files_cache_filename_suffix = cache')
262        conf_file.close()
263        config.LoadConfig(self.conf)
264        nsswitch_filename = os.path.join(self.workdir, 'nsswitch.conf')
265        nsswitch_file = open(nsswitch_filename, 'w')
266        nsswitch_file.write('passwd: cache\n')
267        nsswitch_file.write('group: cache\n')
268        nsswitch_file.write('shadow: cache\n')
269        nsswitch_file.close()
270        self.assertEqual((0, 0),
271                         config.VerifyConfiguration(self.conf,
272                                                    nsswitch_filename))
273        os.unlink(nsswitch_filename)
274
275    def testVerifyConfigurationWithFiles(self):
276        conf_file = open(self.conf_filename, 'w')
277        conf_file.write('[DEFAULT]\n'
278                        'source = foo\n'
279                        'cache = files\n'
280                        'maps = passwd, group, shadow\n'
281                        'timestamp_dir = foo\n')
282        conf_file.close()
283        config.LoadConfig(self.conf)
284        nsswitch_filename = os.path.join(self.workdir, 'nsswitch.conf')
285        nsswitch_file = open(nsswitch_filename, 'w')
286        nsswitch_file.write('passwd: files\n')
287        nsswitch_file.write('group: files\n')
288        nsswitch_file.write('shadow: files\n')
289        nsswitch_file.close()
290        self.assertEqual((0, 0),
291                         config.VerifyConfiguration(self.conf,
292                                                    nsswitch_filename))
293        os.unlink(nsswitch_filename)
294
295    def testVerifyBadConfigurationWithCache(self):
296        conf_file = open(self.conf_filename, 'w')
297        conf_file.write('[DEFAULT]\n'
298                        'source = foo\n'
299                        'cache = files\n'
300                        'maps = passwd, group, shadow\n'
301                        'timestamp_dir = foo\n'
302                        'files_cache_filename_suffix = cache')
303        conf_file.close()
304        config.LoadConfig(self.conf)
305        nsswitch_filename = os.path.join(self.workdir, 'nsswitch.conf')
306        nsswitch_file = open(nsswitch_filename, 'w')
307        nsswitch_file.write('passwd: files\n')
308        nsswitch_file.write('group: files\n')
309        nsswitch_file.write('shadow: files\n')
310        nsswitch_file.close()
311        self.assertEqual((3, 0),
312                         config.VerifyConfiguration(self.conf,
313                                                    nsswitch_filename))
314        os.unlink(nsswitch_filename)
315
316    def testVerifyBadConfigurationIncrementsWarningCount(self):
317        conf_file = open(self.conf_filename, 'w')
318        conf_file.write('[DEFAULT]\n'
319                        'source = foo\n'
320                        'cache = foo\n'
321                        'maps = passwd, group, shadow\n'
322                        'timestamp_dir = foo\n')
323        conf_file.close()
324        config.LoadConfig(self.conf)
325        nsswitch_filename = os.path.join(self.workdir, 'nsswitch.conf')
326        nsswitch_file = open(nsswitch_filename, 'w')
327        nsswitch_file.write('passwd: files ldap\n')
328        nsswitch_file.write('group: files db\n')
329        nsswitch_file.write('shadow: files db\n')
330        nsswitch_file.close()
331        self.assertEqual((1, 0),
332                         config.VerifyConfiguration(self.conf,
333                                                    nsswitch_filename))
334        os.unlink(nsswitch_filename)
335
336    def testVerifyNoMapConfigurationIsError(self):
337        conf_file = open(self.conf_filename, 'w')
338        conf_file.write('[DEFAULT]\n'
339                        'source = foo\n'
340                        'cache = foo\n'
341                        'maps = \n'
342                        'timestamp_dir = foo\n')
343        conf_file.close()
344        config.LoadConfig(self.conf)
345        nsswitch_filename = os.path.join(self.workdir, 'nsswitch.conf')
346        nsswitch_file = open(nsswitch_filename, 'w')
347        nsswitch_file.write('passwd: files ldap\n')
348        nsswitch_file.close()
349        self.assertEqual((0, 1),
350                         config.VerifyConfiguration(self.conf,
351                                                    nsswitch_filename))
352        os.unlink(nsswitch_filename)
353
354
355if __name__ == '__main__':
356    unittest.main()
357