1 # Copyright (c) Twisted Matrix Laboratories.
2 # See LICENSE for details.
5 Tests for L{twisted.python.fakepwd}.
19 from operator import getitem
21 from twisted.trial.unittest import TestCase
22 from twisted.python.fakepwd import UserDatabase, ShadowDatabase
23 from twisted.python.compat import set
26 class UserDatabaseTestsMixin:
28 L{UserDatabaseTestsMixin} defines tests which apply to any user database
29 implementation. Subclasses should mix it in, implement C{setUp} to create
30 C{self.database} bound to a user database instance, and implement
31 C{getExistingUserInfo} to return information about a user (such information
32 should be unique per test method).
34 def test_getpwuid(self):
36 I{getpwuid} accepts a uid and returns the user record associated with
40 # Get some user which exists in the database.
41 username, password, uid, gid, gecos, dir, shell = self.getExistingUserInfo()
43 # Now try to look it up and make sure the result is correct.
44 entry = self.database.getpwuid(uid)
45 self.assertEqual(entry.pw_name, username)
46 self.assertEqual(entry.pw_passwd, password)
47 self.assertEqual(entry.pw_uid, uid)
48 self.assertEqual(entry.pw_gid, gid)
49 self.assertEqual(entry.pw_gecos, gecos)
50 self.assertEqual(entry.pw_dir, dir)
51 self.assertEqual(entry.pw_shell, shell)
54 def test_noSuchUID(self):
56 I{getpwuid} raises L{KeyError} when passed a uid which does not exist
59 self.assertRaises(KeyError, self.database.getpwuid, -13)
62 def test_getpwnam(self):
64 I{getpwnam} accepts a username and returns the user record associated
68 # Get some user which exists in the database.
69 username, password, uid, gid, gecos, dir, shell = self.getExistingUserInfo()
71 # Now try to look it up and make sure the result is correct.
72 entry = self.database.getpwnam(username)
73 self.assertEqual(entry.pw_name, username)
74 self.assertEqual(entry.pw_passwd, password)
75 self.assertEqual(entry.pw_uid, uid)
76 self.assertEqual(entry.pw_gid, gid)
77 self.assertEqual(entry.pw_gecos, gecos)
78 self.assertEqual(entry.pw_dir, dir)
79 self.assertEqual(entry.pw_shell, shell)
82 def test_noSuchName(self):
84 I{getpwnam} raises L{KeyError} when passed a username which does not
85 exist in the user database.
88 KeyError, self.database.getpwnam,
89 'no' 'such' 'user' 'exists' 'the' 'name' 'is' 'too' 'long' 'and' 'has'
93 def test_recordLength(self):
95 The user record returned by I{getpwuid}, I{getpwnam}, and I{getpwall}
99 username, password, uid, gid, gecos, dir, shell = self.getExistingUserInfo()
100 for entry in [db.getpwuid(uid), db.getpwnam(username), db.getpwall()[0]]:
101 self.assertIsInstance(len(entry), int)
102 self.assertEquals(len(entry), 7)
105 def test_recordIndexable(self):
107 The user record returned by I{getpwuid}, I{getpwnam}, and I{getpwall}
108 is indexable, with successive indexes starting from 0 corresponding to
109 the values of the C{pw_name}, C{pw_passwd}, C{pw_uid}, C{pw_gid},
110 C{pw_gecos}, C{pw_dir}, and C{pw_shell} attributes, respectively.
113 username, password, uid, gid, gecos, dir, shell = self.getExistingUserInfo()
114 for entry in [db.getpwuid(uid), db.getpwnam(username), db.getpwall()[0]]:
115 self.assertEqual(entry[0], username)
116 self.assertEqual(entry[1], password)
117 self.assertEqual(entry[2], uid)
118 self.assertEqual(entry[3], gid)
119 self.assertEqual(entry[4], gecos)
120 self.assertEqual(entry[5], dir)
121 self.assertEqual(entry[6], shell)
123 self.assertEqual(len(entry), len(list(entry)))
124 self.assertRaises(IndexError, getitem, entry, 7)
128 class UserDatabaseTests(TestCase, UserDatabaseTestsMixin):
130 Tests for L{UserDatabase}.
134 Create a L{UserDatabase} with no user data in it.
136 self.database = UserDatabase()
140 def getExistingUserInfo(self):
142 Add a new user to C{self.database} and return its information.
145 suffix = '_' + str(self._counter)
146 username = 'username' + suffix
147 password = 'password' + suffix
149 gid = self._counter + 1000
150 gecos = 'gecos' + suffix
152 shell = 'shell' + suffix
154 self.database.addUser(username, password, uid, gid, gecos, dir, shell)
155 return (username, password, uid, gid, gecos, dir, shell)
158 def test_addUser(self):
160 L{UserDatabase.addUser} accepts seven arguments, one for each field of
161 a L{pwd.struct_passwd}, and makes the new record available via
162 L{UserDatabase.getpwuid}, L{UserDatabase.getpwnam}, and
163 L{UserDatabase.getpwall}.
170 home = '/users/alice'
171 shell = '/usr/bin/foosh'
174 db.addUser(username, password, uid, gid, gecos, home, shell)
176 for [entry] in [[db.getpwuid(uid)], [db.getpwnam(username)],
178 self.assertEqual(entry.pw_name, username)
179 self.assertEqual(entry.pw_passwd, password)
180 self.assertEqual(entry.pw_uid, uid)
181 self.assertEqual(entry.pw_gid, gid)
182 self.assertEqual(entry.pw_gecos, gecos)
183 self.assertEqual(entry.pw_dir, home)
184 self.assertEqual(entry.pw_shell, shell)
188 class PwdModuleTests(TestCase, UserDatabaseTestsMixin):
190 L{PwdModuleTests} runs the tests defined by L{UserDatabaseTestsMixin}
191 against the built-in C{pwd} module. This serves to verify that
192 L{UserDatabase} is really a fake of that API.
195 skip = "Cannot verify UserDatabase against pwd without pwd"
200 self._users = iter(self.database.getpwall())
204 def getExistingUserInfo(self):
206 Read and return the next record from C{self._users}, filtering out
207 any records with previously seen uid values (as these cannot be
208 found with C{getpwuid} and only cause trouble).
211 entry = self._users.next()
213 if uid not in self._uids:
219 class ShadowDatabaseTestsMixin:
221 L{ShadowDatabaseTestsMixin} defines tests which apply to any shadow user
222 database implementation. Subclasses should mix it in, implement C{setUp} to
223 create C{self.database} bound to a shadow user database instance, and
224 implement C{getExistingUserInfo} to return information about a user (such
225 information should be unique per test method).
227 def test_getspnam(self):
229 L{getspnam} accepts a username and returns the user record associated
233 # Get some user which exists in the database.
234 (username, password, lastChange, min, max, warn, inact, expire,
235 flag) = self.getExistingUserInfo()
237 entry = self.database.getspnam(username)
238 self.assertEquals(entry.sp_nam, username)
239 self.assertEquals(entry.sp_pwd, password)
240 self.assertEquals(entry.sp_lstchg, lastChange)
241 self.assertEquals(entry.sp_min, min)
242 self.assertEquals(entry.sp_max, max)
243 self.assertEquals(entry.sp_warn, warn)
244 self.assertEquals(entry.sp_inact, inact)
245 self.assertEquals(entry.sp_expire, expire)
246 self.assertEquals(entry.sp_flag, flag)
249 def test_noSuchName(self):
251 I{getspnam} raises L{KeyError} when passed a username which does not
252 exist in the user database.
254 self.assertRaises(KeyError, self.database.getspnam, "alice")
257 def test_recordLength(self):
259 The shadow user record returned by I{getspnam} and I{getspall} has a
263 username = self.getExistingUserInfo()[0]
264 for entry in [db.getspnam(username), db.getspall()[0]]:
265 self.assertIsInstance(len(entry), int)
266 self.assertEquals(len(entry), 9)
269 def test_recordIndexable(self):
271 The shadow user record returned by I{getpwnam} and I{getspall} is
272 indexable, with successive indexes starting from 0 corresponding to the
273 values of the C{sp_nam}, C{sp_pwd}, C{sp_lstchg}, C{sp_min}, C{sp_max},
274 C{sp_warn}, C{sp_inact}, C{sp_expire}, and C{sp_flag} attributes,
278 (username, password, lastChange, min, max, warn, inact, expire,
279 flag) = self.getExistingUserInfo()
280 for entry in [db.getspnam(username), db.getspall()[0]]:
281 self.assertEquals(entry[0], username)
282 self.assertEquals(entry[1], password)
283 self.assertEquals(entry[2], lastChange)
284 self.assertEquals(entry[3], min)
285 self.assertEquals(entry[4], max)
286 self.assertEquals(entry[5], warn)
287 self.assertEquals(entry[6], inact)
288 self.assertEquals(entry[7], expire)
289 self.assertEquals(entry[8], flag)
291 self.assertEquals(len(entry), len(list(entry)))
292 self.assertRaises(IndexError, getitem, entry, 9)
296 class ShadowDatabaseTests(TestCase, ShadowDatabaseTestsMixin):
298 Tests for L{ShadowDatabase}.
302 Create a L{ShadowDatabase} with no user data in it.
304 self.database = ShadowDatabase()
308 def getExistingUserInfo(self):
310 Add a new user to C{self.database} and return its information.
313 suffix = '_' + str(self._counter)
314 username = 'username' + suffix
315 password = 'password' + suffix
316 lastChange = self._counter + 1
317 min = self._counter + 2
318 max = self._counter + 3
319 warn = self._counter + 4
320 inact = self._counter + 5
321 expire = self._counter + 6
322 flag = self._counter + 7
324 self.database.addUser(username, password, lastChange, min, max, warn,
326 return (username, password, lastChange, min, max, warn, inact,
330 def test_addUser(self):
332 L{UserDatabase.addUser} accepts seven arguments, one for each field of
333 a L{pwd.struct_passwd}, and makes the new record available via
334 L{UserDatabase.getpwuid}, L{UserDatabase.getpwnam}, and
335 L{UserDatabase.getpwall}.
348 db.addUser(username, password, lastChange, min, max, warn, inact,
351 for [entry] in [[db.getspnam(username)], db.getspall()]:
352 self.assertEquals(entry.sp_nam, username)
353 self.assertEquals(entry.sp_pwd, password)
354 self.assertEquals(entry.sp_lstchg, lastChange)
355 self.assertEquals(entry.sp_min, min)
356 self.assertEquals(entry.sp_max, max)
357 self.assertEquals(entry.sp_warn, warn)
358 self.assertEquals(entry.sp_inact, inact)
359 self.assertEquals(entry.sp_expire, expire)
360 self.assertEquals(entry.sp_flag, flag)
364 class SPwdModuleTests(TestCase, ShadowDatabaseTestsMixin):
366 L{SPwdModuleTests} runs the tests defined by L{ShadowDatabaseTestsMixin}
367 against the built-in C{spwd} module. This serves to verify that
368 L{ShadowDatabase} is really a fake of that API.
371 skip = "Cannot verify ShadowDatabase against spwd without spwd"
372 elif os.getuid() != 0:
373 skip = "Cannot access shadow user database except as root"
378 self._users = iter(self.database.getspall())
381 def getExistingUserInfo(self):
383 Read and return the next record from C{self._users}.
385 return self._users.next()