2 # -*- coding: utf-8 -*-
3 # This tests the password changes over LDAP for AD implementations
5 # Copyright Matthias Dieter Wallnoefer 2010
7 # Notice: This tests will also work against Windows Server if the connection is
8 # secured enough (SASL with a minimum of 128 Bit encryption) - consider
17 sys.path.append("bin/python")
19 samba.ensure_external_module("testtools", "testtools")
20 samba.ensure_external_module("subunit", "subunit/python")
22 import samba.getopt as options
24 from samba.auth import system_session
25 from samba.credentials import Credentials
26 from ldb import SCOPE_BASE, LdbError
27 from ldb import ERR_NO_SUCH_OBJECT, ERR_ATTRIBUTE_OR_VALUE_EXISTS
28 from ldb import ERR_UNWILLING_TO_PERFORM, ERR_INSUFFICIENT_ACCESS_RIGHTS
29 from ldb import ERR_NO_SUCH_ATTRIBUTE
30 from ldb import ERR_CONSTRAINT_VIOLATION
31 from ldb import Message, MessageElement, Dn
32 from ldb import FLAG_MOD_ADD, FLAG_MOD_REPLACE, FLAG_MOD_DELETE
33 from samba import gensec
34 from samba.samdb import SamDB
36 from subunit.run import SubunitTestRunner
39 parser = optparse.OptionParser("passwords.py [options] <host>")
40 sambaopts = options.SambaOptions(parser)
41 parser.add_option_group(sambaopts)
42 parser.add_option_group(options.VersionOptions(parser))
43 # use command line creds if available
44 credopts = options.CredentialsOptions(parser)
45 parser.add_option_group(credopts)
46 opts, args = parser.parse_args()
54 lp = sambaopts.get_loadparm()
55 creds = credopts.get_credentials(lp)
57 # Force an encrypted connection
58 creds.set_gensec_features(creds.get_gensec_features() | gensec.FEATURE_SEAL)
64 class PasswordTests(samba.tests.TestCase):
66 def delete_force(self, ldb, dn):
69 except LdbError, (num, _):
70 self.assertEquals(num, ERR_NO_SUCH_OBJECT)
73 super(PasswordTests, self).setUp()
75 self.base_dn = ldb.domain_dn()
77 # (Re)adds the test user "testuser" with no password atm
78 self.delete_force(self.ldb, "cn=testuser,cn=users," + self.base_dn)
80 "dn": "cn=testuser,cn=users," + self.base_dn,
81 "objectclass": "user",
82 "sAMAccountName": "testuser"})
84 # Tests a password change when we don't have any password yet with a
87 self.ldb.modify_ldif("""
88 dn: cn=testuser,cn=users,""" + self.base_dn + """
91 userPassword: noPassword
93 userPassword: thatsAcomplPASS2
96 except LdbError, (num, msg):
97 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
98 # Windows (2008 at least) seems to have some small bug here: it
99 # returns "0000056A" on longer (always wrong) previous passwords.
100 self.assertTrue('00000056' in msg)
102 # Sets the initial user password with a "special" password change
103 # I think that this internally is a password set operation and it can
104 # only be performed by someone which has password set privileges on the
105 # account (at least in s4 we do handle it like that).
106 self.ldb.modify_ldif("""
107 dn: cn=testuser,cn=users,""" + self.base_dn + """
111 userPassword: thatsAcomplPASS1
114 # But in the other way around this special syntax doesn't work
116 self.ldb.modify_ldif("""
117 dn: cn=testuser,cn=users,""" + self.base_dn + """
120 userPassword: thatsAcomplPASS1
124 except LdbError, (num, _):
125 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
127 # Enables the user account
128 self.ldb.enable_account("(sAMAccountName=testuser)")
130 # Open a second LDB connection with the user credentials. Use the
131 # command line credentials for informations like the domain, the realm
132 # and the workstation.
133 creds2 = Credentials()
134 creds2.set_username("testuser")
135 creds2.set_password("thatsAcomplPASS1")
136 creds2.set_domain(creds.get_domain())
137 creds2.set_realm(creds.get_realm())
138 creds2.set_workstation(creds.get_workstation())
139 creds2.set_gensec_features(creds2.get_gensec_features()
140 | gensec.FEATURE_SEAL)
141 self.ldb2 = SamDB(url=host, credentials=creds2, lp=lp)
143 def test_unicodePwd_hash_set(self):
144 print "Performs a password hash set operation on 'unicodePwd' which should be prevented"
145 # Notice: Direct hash password sets should never work
148 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
149 m["unicodePwd"] = MessageElement("XXXXXXXXXXXXXXXX", FLAG_MOD_REPLACE,
154 except LdbError, (num, _):
155 self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
157 def test_unicodePwd_hash_change(self):
158 print "Performs a password hash change operation on 'unicodePwd' which should be prevented"
159 # Notice: Direct hash password changes should never work
161 # Hash password changes should never work
163 self.ldb2.modify_ldif("""
164 dn: cn=testuser,cn=users,""" + self.base_dn + """
167 unicodePwd: XXXXXXXXXXXXXXXX
169 unicodePwd: YYYYYYYYYYYYYYYY
172 except LdbError, (num, _):
173 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
175 def test_unicodePwd_clear_set(self):
176 print "Performs a password cleartext set operation on 'unicodePwd'"
179 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
180 m["unicodePwd"] = MessageElement("\"thatsAcomplPASS2\"".encode('utf-16-le'),
181 FLAG_MOD_REPLACE, "unicodePwd")
184 def test_unicodePwd_clear_change(self):
185 print "Performs a password cleartext change operation on 'unicodePwd'"
187 self.ldb2.modify_ldif("""
188 dn: cn=testuser,cn=users,""" + self.base_dn + """
191 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS1\"".encode('utf-16-le')) + """
193 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
198 self.ldb2.modify_ldif("""
199 dn: cn=testuser,cn=users,""" + self.base_dn + """
202 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS3\"".encode('utf-16-le')) + """
204 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS4\"".encode('utf-16-le')) + """
207 except LdbError, (num, msg):
208 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
209 self.assertTrue('00000056' in msg)
211 # A change to the same password again will not work (password history)
213 self.ldb2.modify_ldif("""
214 dn: cn=testuser,cn=users,""" + self.base_dn + """
217 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
219 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
222 except LdbError, (num, msg):
223 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
224 self.assertTrue('0000052D' in msg)
226 def test_dBCSPwd_hash_set(self):
227 print "Performs a password hash set operation on 'dBCSPwd' which should be prevented"
228 # Notice: Direct hash password sets should never work
231 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
232 m["dBCSPwd"] = MessageElement("XXXXXXXXXXXXXXXX", FLAG_MOD_REPLACE,
237 except LdbError, (num, _):
238 self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
240 def test_dBCSPwd_hash_change(self):
241 print "Performs a password hash change operation on 'dBCSPwd' which should be prevented"
242 # Notice: Direct hash password changes should never work
245 self.ldb2.modify_ldif("""
246 dn: cn=testuser,cn=users,""" + self.base_dn + """
249 dBCSPwd: XXXXXXXXXXXXXXXX
251 dBCSPwd: YYYYYYYYYYYYYYYY
254 except LdbError, (num, _):
255 self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
257 def test_userPassword_clear_set(self):
258 print "Performs a password cleartext set operation on 'userPassword'"
259 # Notice: This works only against Windows if "dSHeuristics" has been set
263 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
264 m["userPassword"] = MessageElement("thatsAcomplPASS2", FLAG_MOD_REPLACE,
268 def test_userPassword_clear_change(self):
269 print "Performs a password cleartext change operation on 'userPassword'"
270 # Notice: This works only against Windows if "dSHeuristics" has been set
273 self.ldb2.modify_ldif("""
274 dn: cn=testuser,cn=users,""" + self.base_dn + """
277 userPassword: thatsAcomplPASS1
279 userPassword: thatsAcomplPASS2
284 self.ldb2.modify_ldif("""
285 dn: cn=testuser,cn=users,""" + self.base_dn + """
288 userPassword: thatsAcomplPASS3
290 userPassword: thatsAcomplPASS4
293 except LdbError, (num, msg):
294 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
295 self.assertTrue('00000056' in msg)
297 # A change to the same password again will not work (password history)
299 self.ldb2.modify_ldif("""
300 dn: cn=testuser,cn=users,""" + self.base_dn + """
303 userPassword: thatsAcomplPASS2
305 userPassword: thatsAcomplPASS2
308 except LdbError, (num, msg):
309 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
310 self.assertTrue('0000052D' in msg)
312 def test_clearTextPassword_clear_set(self):
313 print "Performs a password cleartext set operation on 'clearTextPassword'"
314 # Notice: This never works against Windows - only supported by us
318 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
319 m["clearTextPassword"] = MessageElement("thatsAcomplPASS2".encode('utf-16-le'),
320 FLAG_MOD_REPLACE, "clearTextPassword")
322 # this passes against s4
323 except LdbError, (num, msg):
324 # "NO_SUCH_ATTRIBUTE" is returned by Windows -> ignore it
325 if num != ERR_NO_SUCH_ATTRIBUTE:
326 raise LdbError(num, msg)
328 def test_clearTextPassword_clear_change(self):
329 print "Performs a password cleartext change operation on 'clearTextPassword'"
330 # Notice: This never works against Windows - only supported by us
333 self.ldb2.modify_ldif("""
334 dn: cn=testuser,cn=users,""" + self.base_dn + """
336 delete: clearTextPassword
337 clearTextPassword:: """ + base64.b64encode("thatsAcomplPASS1".encode('utf-16-le')) + """
338 add: clearTextPassword
339 clearTextPassword:: """ + base64.b64encode("thatsAcomplPASS2".encode('utf-16-le')) + """
341 # this passes against s4
342 except LdbError, (num, msg):
343 # "NO_SUCH_ATTRIBUTE" is returned by Windows -> ignore it
344 if num != ERR_NO_SUCH_ATTRIBUTE:
345 raise LdbError(num, msg)
349 self.ldb2.modify_ldif("""
350 dn: cn=testuser,cn=users,""" + self.base_dn + """
352 delete: clearTextPassword
353 clearTextPassword:: """ + base64.b64encode("thatsAcomplPASS3".encode('utf-16-le')) + """
354 add: clearTextPassword
355 clearTextPassword:: """ + base64.b64encode("thatsAcomplPASS4".encode('utf-16-le')) + """
358 except LdbError, (num, msg):
359 # "NO_SUCH_ATTRIBUTE" is returned by Windows -> ignore it
360 if num != ERR_NO_SUCH_ATTRIBUTE:
361 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
362 self.assertTrue('00000056' in msg)
364 # A change to the same password again will not work (password history)
366 self.ldb2.modify_ldif("""
367 dn: cn=testuser,cn=users,""" + self.base_dn + """
369 delete: clearTextPassword
370 clearTextPassword:: """ + base64.b64encode("thatsAcomplPASS2".encode('utf-16-le')) + """
371 add: clearTextPassword
372 clearTextPassword:: """ + base64.b64encode("thatsAcomplPASS2".encode('utf-16-le')) + """
375 except LdbError, (num, msg):
376 # "NO_SUCH_ATTRIBUTE" is returned by Windows -> ignore it
377 if num != ERR_NO_SUCH_ATTRIBUTE:
378 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
379 self.assertTrue('0000052D' in msg)
381 def test_failures(self):
382 print "Performs some failure testing"
386 dn: cn=testuser,cn=users,""" + self.base_dn + """
389 userPassword: thatsAcomplPASS1
392 except LdbError, (num, _):
393 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
396 self.ldb2.modify_ldif("""
397 dn: cn=testuser,cn=users,""" + self.base_dn + """
400 userPassword: thatsAcomplPASS1
403 except LdbError, (num, _):
404 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
408 dn: cn=testuser,cn=users,""" + self.base_dn + """
413 except LdbError, (num, _):
414 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
417 self.ldb2.modify_ldif("""
418 dn: cn=testuser,cn=users,""" + self.base_dn + """
423 except LdbError, (num, _):
424 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
428 dn: cn=testuser,cn=users,""" + self.base_dn + """
431 userPassword: thatsAcomplPASS1
434 except LdbError, (num, _):
435 self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
438 self.ldb2.modify_ldif("""
439 dn: cn=testuser,cn=users,""" + self.base_dn + """
442 userPassword: thatsAcomplPASS1
445 except LdbError, (num, _):
446 self.assertEquals(num, ERR_INSUFFICIENT_ACCESS_RIGHTS)
450 dn: cn=testuser,cn=users,""" + self.base_dn + """
453 userPassword: thatsAcomplPASS1
455 userPassword: thatsAcomplPASS2
456 userPassword: thatsAcomplPASS2
459 except LdbError, (num, _):
460 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
463 self.ldb2.modify_ldif("""
464 dn: cn=testuser,cn=users,""" + self.base_dn + """
467 userPassword: thatsAcomplPASS1
469 userPassword: thatsAcomplPASS2
470 userPassword: thatsAcomplPASS2
473 except LdbError, (num, _):
474 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
478 dn: cn=testuser,cn=users,""" + self.base_dn + """
481 userPassword: thatsAcomplPASS1
482 userPassword: thatsAcomplPASS1
484 userPassword: thatsAcomplPASS2
487 except LdbError, (num, _):
488 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
491 self.ldb2.modify_ldif("""
492 dn: cn=testuser,cn=users,""" + self.base_dn + """
495 userPassword: thatsAcomplPASS1
496 userPassword: thatsAcomplPASS1
498 userPassword: thatsAcomplPASS2
501 except LdbError, (num, _):
502 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
506 dn: cn=testuser,cn=users,""" + self.base_dn + """
509 userPassword: thatsAcomplPASS1
511 userPassword: thatsAcomplPASS2
513 userPassword: thatsAcomplPASS2
516 except LdbError, (num, _):
517 self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
520 self.ldb2.modify_ldif("""
521 dn: cn=testuser,cn=users,""" + self.base_dn + """
524 userPassword: thatsAcomplPASS1
526 userPassword: thatsAcomplPASS2
528 userPassword: thatsAcomplPASS2
531 except LdbError, (num, _):
532 self.assertEquals(num, ERR_INSUFFICIENT_ACCESS_RIGHTS)
536 dn: cn=testuser,cn=users,""" + self.base_dn + """
539 userPassword: thatsAcomplPASS1
541 userPassword: thatsAcomplPASS1
543 userPassword: thatsAcomplPASS2
546 except LdbError, (num, _):
547 self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
550 self.ldb2.modify_ldif("""
551 dn: cn=testuser,cn=users,""" + self.base_dn + """
554 userPassword: thatsAcomplPASS1
556 userPassword: thatsAcomplPASS1
558 userPassword: thatsAcomplPASS2
561 except LdbError, (num, _):
562 self.assertEquals(num, ERR_INSUFFICIENT_ACCESS_RIGHTS)
566 dn: cn=testuser,cn=users,""" + self.base_dn + """
569 userPassword: thatsAcomplPASS1
571 userPassword: thatsAcomplPASS2
572 replace: userPassword
573 userPassword: thatsAcomplPASS3
576 except LdbError, (num, _):
577 self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
580 self.ldb2.modify_ldif("""
581 dn: cn=testuser,cn=users,""" + self.base_dn + """
584 userPassword: thatsAcomplPASS1
586 userPassword: thatsAcomplPASS2
587 replace: userPassword
588 userPassword: thatsAcomplPASS3
591 except LdbError, (num, _):
592 self.assertEquals(num, ERR_INSUFFICIENT_ACCESS_RIGHTS)
594 # Reverse order does work
595 self.ldb2.modify_ldif("""
596 dn: cn=testuser,cn=users,""" + self.base_dn + """
599 userPassword: thatsAcomplPASS2
601 userPassword: thatsAcomplPASS1
605 self.ldb2.modify_ldif("""
606 dn: cn=testuser,cn=users,""" + self.base_dn + """
609 userPassword: thatsAcomplPASS2
611 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS3\"".encode('utf-16-le')) + """
613 # this passes against s4
614 except LdbError, (num, _):
615 self.assertEquals(num, ERR_ATTRIBUTE_OR_VALUE_EXISTS)
618 self.ldb2.modify_ldif("""
619 dn: cn=testuser,cn=users,""" + self.base_dn + """
622 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS3\"".encode('utf-16-le')) + """
624 userPassword: thatsAcomplPASS4
626 # this passes against s4
627 except LdbError, (num, _):
628 self.assertEquals(num, ERR_NO_SUCH_ATTRIBUTE)
630 # Several password changes at once are allowed
632 dn: cn=testuser,cn=users,""" + self.base_dn + """
634 replace: userPassword
635 userPassword: thatsAcomplPASS1
636 userPassword: thatsAcomplPASS2
639 # Several password changes at once are allowed
641 dn: cn=testuser,cn=users,""" + self.base_dn + """
643 replace: userPassword
644 userPassword: thatsAcomplPASS1
645 userPassword: thatsAcomplPASS2
646 replace: userPassword
647 userPassword: thatsAcomplPASS3
648 replace: userPassword
649 userPassword: thatsAcomplPASS4
652 # This surprisingly should work
653 self.delete_force(self.ldb, "cn=testuser2,cn=users," + self.base_dn)
655 "dn": "cn=testuser2,cn=users," + self.base_dn,
656 "objectclass": "user",
657 "userPassword": ["thatsAcomplPASS1", "thatsAcomplPASS2"] })
659 # This surprisingly should work
660 self.delete_force(self.ldb, "cn=testuser2,cn=users," + self.base_dn)
662 "dn": "cn=testuser2,cn=users," + self.base_dn,
663 "objectclass": "user",
664 "userPassword": ["thatsAcomplPASS1", "thatsAcomplPASS1"] })
666 def test_empty_passwords(self):
667 print "Performs some empty passwords testing"
671 "dn": "cn=testuser2,cn=users," + self.base_dn,
672 "objectclass": "user",
675 except LdbError, (num, _):
676 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
680 "dn": "cn=testuser2,cn=users," + self.base_dn,
681 "objectclass": "user",
684 except LdbError, (num, _):
685 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
689 "dn": "cn=testuser2,cn=users," + self.base_dn,
690 "objectclass": "user",
691 "userPassword": [] })
693 except LdbError, (num, _):
694 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
698 "dn": "cn=testuser2,cn=users," + self.base_dn,
699 "objectclass": "user",
700 "clearTextPassword": [] })
702 except LdbError, (num, _):
703 self.assertTrue(num == ERR_CONSTRAINT_VIOLATION or
704 num == ERR_NO_SUCH_ATTRIBUTE) # for Windows
706 self.delete_force(self.ldb, "cn=testuser2,cn=users," + self.base_dn)
709 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
710 m["unicodePwd"] = MessageElement([], FLAG_MOD_ADD, "unicodePwd")
714 except LdbError, (num, _):
715 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
718 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
719 m["dBCSPwd"] = MessageElement([], FLAG_MOD_ADD, "dBCSPwd")
723 except LdbError, (num, _):
724 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
727 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
728 m["userPassword"] = MessageElement([], FLAG_MOD_ADD, "userPassword")
732 except LdbError, (num, _):
733 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
736 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
737 m["clearTextPassword"] = MessageElement([], FLAG_MOD_ADD, "clearTextPassword")
741 except LdbError, (num, _):
742 self.assertTrue(num == ERR_CONSTRAINT_VIOLATION or
743 num == ERR_NO_SUCH_ATTRIBUTE) # for Windows
746 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
747 m["unicodePwd"] = MessageElement([], FLAG_MOD_REPLACE, "unicodePwd")
751 except LdbError, (num, _):
752 self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
755 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
756 m["dBCSPwd"] = MessageElement([], FLAG_MOD_REPLACE, "dBCSPwd")
760 except LdbError, (num, _):
761 self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
764 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
765 m["userPassword"] = MessageElement([], FLAG_MOD_REPLACE, "userPassword")
769 except LdbError, (num, _):
770 self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
773 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
774 m["clearTextPassword"] = MessageElement([], FLAG_MOD_REPLACE, "clearTextPassword")
778 except LdbError, (num, _):
779 self.assertTrue(num == ERR_UNWILLING_TO_PERFORM or
780 num == ERR_NO_SUCH_ATTRIBUTE) # for Windows
783 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
784 m["unicodePwd"] = MessageElement([], FLAG_MOD_DELETE, "unicodePwd")
788 except LdbError, (num, _):
789 self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
792 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
793 m["dBCSPwd"] = MessageElement([], FLAG_MOD_DELETE, "dBCSPwd")
797 except LdbError, (num, _):
798 self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
801 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
802 m["userPassword"] = MessageElement([], FLAG_MOD_DELETE, "userPassword")
806 except LdbError, (num, _):
807 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
810 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
811 m["clearTextPassword"] = MessageElement([], FLAG_MOD_DELETE, "clearTextPassword")
815 except LdbError, (num, _):
816 self.assertTrue(num == ERR_CONSTRAINT_VIOLATION or
817 num == ERR_NO_SUCH_ATTRIBUTE) # for Windows
819 def test_plain_userPassword(self):
820 print "Performs testing about the standard 'userPassword' behaviour"
822 # Delete the "dSHeuristics"
824 m.dn = Dn(ldb, "CN=Directory Service, CN=Windows NT, CN=Services, "
826 m["dSHeuristics"] = MessageElement([], FLAG_MOD_DELETE, "dsHeuristics")
829 time.sleep(1) # This switching time is strictly needed!
832 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
833 m["userPassword"] = MessageElement("myPassword", FLAG_MOD_ADD,
837 res = ldb.search("cn=testuser,cn=users," + self.base_dn,
838 scope=SCOPE_BASE, attrs=["userPassword"])
839 self.assertTrue(len(res) == 1)
840 self.assertTrue("userPassword" in res[0])
841 self.assertEquals(res[0]["userPassword"][0], "myPassword")
844 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
845 m["userPassword"] = MessageElement("myPassword2", FLAG_MOD_REPLACE,
849 res = ldb.search("cn=testuser,cn=users," + self.base_dn,
850 scope=SCOPE_BASE, attrs=["userPassword"])
851 self.assertTrue(len(res) == 1)
852 self.assertTrue("userPassword" in res[0])
853 self.assertEquals(res[0]["userPassword"][0], "myPassword2")
856 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
857 m["userPassword"] = MessageElement([], FLAG_MOD_DELETE,
861 res = ldb.search("cn=testuser,cn=users," + self.base_dn,
862 scope=SCOPE_BASE, attrs=["userPassword"])
863 self.assertTrue(len(res) == 1)
864 self.assertFalse("userPassword" in res[0])
866 # Set the test "dSHeuristics" to deactivate "userPassword" pwd changes
868 m.dn = Dn(ldb, "CN=Directory Service, CN=Windows NT, CN=Services, "
870 m["dSHeuristics"] = MessageElement("000000000", FLAG_MOD_REPLACE,
875 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
876 m["userPassword"] = MessageElement("myPassword3", FLAG_MOD_REPLACE,
880 res = ldb.search("cn=testuser,cn=users," + self.base_dn,
881 scope=SCOPE_BASE, attrs=["userPassword"])
882 self.assertTrue(len(res) == 1)
883 self.assertTrue("userPassword" in res[0])
884 self.assertEquals(res[0]["userPassword"][0], "myPassword3")
886 # Set the test "dSHeuristics" to deactivate "userPassword" pwd changes
888 m.dn = Dn(ldb, "CN=Directory Service, CN=Windows NT, CN=Services, "
890 m["dSHeuristics"] = MessageElement("000000002", FLAG_MOD_REPLACE,
895 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
896 m["userPassword"] = MessageElement("myPassword4", FLAG_MOD_REPLACE,
900 res = ldb.search("cn=testuser,cn=users," + self.base_dn,
901 scope=SCOPE_BASE, attrs=["userPassword"])
902 self.assertTrue(len(res) == 1)
903 self.assertTrue("userPassword" in res[0])
904 self.assertEquals(res[0]["userPassword"][0], "myPassword4")
906 # Reset the test "dSHeuristics" (reactivate "userPassword" pwd changes)
908 m.dn = Dn(ldb, "CN=Directory Service, CN=Windows NT, CN=Services, "
910 m["dSHeuristics"] = MessageElement("000000001", FLAG_MOD_REPLACE,
915 super(PasswordTests, self).tearDown()
916 self.delete_force(self.ldb, "cn=testuser,cn=users," + self.base_dn)
917 self.delete_force(self.ldb, "cn=testuser2,cn=users," + self.base_dn)
918 # Close the second LDB connection (with the user credentials)
921 if not "://" in host:
922 if os.path.isfile(host):
923 host = "tdb://%s" % host
925 host = "ldap://%s" % host
927 ldb = SamDB(url=host, session_info=system_session(), credentials=creds, lp=lp)
929 # Gets back the basedn
930 base_dn = ldb.domain_dn()
931 # Gets back the configuration basedn
932 configuration_dn = ldb.get_config_basedn().get_linearized()
934 # Get the old "dSHeuristics" if it was set
935 res = ldb.search("CN=Directory Service, CN=Windows NT, CN=Services, "
936 + configuration_dn, scope=SCOPE_BASE, attrs=["dSHeuristics"])
937 if "dSHeuristics" in res[0]:
938 dsheuristics = res[0]["dSHeuristics"][0]
942 # Set the "dSHeuristics" to activate the correct "userPassword" behaviour
944 m.dn = Dn(ldb, "CN=Directory Service, CN=Windows NT, CN=Services, "
946 m["dSHeuristics"] = MessageElement("000000001", FLAG_MOD_REPLACE,
950 # Get the old "minPwdAge"
951 minPwdAge = ldb.get_minPwdAge()
952 # Set it temporarely to "0"
953 ldb.set_minPwdAge("0")
955 runner = SubunitTestRunner()
957 if not runner.run(unittest.makeSuite(PasswordTests)).wasSuccessful():
960 # Reset the "dSHeuristics" as they were before
962 m.dn = Dn(ldb, "CN=Directory Service, CN=Windows NT, CN=Services, "
964 if dsheuristics is not None:
965 m["dSHeuristics"] = MessageElement(dsheuristics, FLAG_MOD_REPLACE,
968 m["dSHeuristics"] = MessageElement([], FLAG_MOD_DELETE, "dsHeuristics")
971 # Reset the "minPwdAge" as it was before
972 ldb.set_minPwdAge(minPwdAge)