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_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 samba.tests import delete_force
37 from subunit.run import SubunitTestRunner
40 parser = optparse.OptionParser("passwords.py [options] <host>")
41 sambaopts = options.SambaOptions(parser)
42 parser.add_option_group(sambaopts)
43 parser.add_option_group(options.VersionOptions(parser))
44 # use command line creds if available
45 credopts = options.CredentialsOptions(parser)
46 parser.add_option_group(credopts)
47 opts, args = parser.parse_args()
55 lp = sambaopts.get_loadparm()
56 creds = credopts.get_credentials(lp)
58 # Force an encrypted connection
59 creds.set_gensec_features(creds.get_gensec_features() | gensec.FEATURE_SEAL)
65 class PasswordTests(samba.tests.TestCase):
68 super(PasswordTests, self).setUp()
70 self.base_dn = ldb.domain_dn()
72 # (Re)adds the test user "testuser" with no password atm
73 delete_force(self.ldb, "cn=testuser,cn=users," + self.base_dn)
75 "dn": "cn=testuser,cn=users," + self.base_dn,
76 "objectclass": "user",
77 "sAMAccountName": "testuser"})
79 # Tests a password change when we don't have any password yet with a
82 self.ldb.modify_ldif("""
83 dn: cn=testuser,cn=users,""" + self.base_dn + """
86 userPassword: noPassword
88 userPassword: thatsAcomplPASS2
91 except LdbError, (num, msg):
92 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
93 # Windows (2008 at least) seems to have some small bug here: it
94 # returns "0000056A" on longer (always wrong) previous passwords.
95 self.assertTrue('00000056' in msg)
97 # Sets the initial user password with a "special" password change
98 # I think that this internally is a password set operation and it can
99 # only be performed by someone which has password set privileges on the
100 # account (at least in s4 we do handle it like that).
101 self.ldb.modify_ldif("""
102 dn: cn=testuser,cn=users,""" + self.base_dn + """
106 userPassword: thatsAcomplPASS1
109 # But in the other way around this special syntax doesn't work
111 self.ldb.modify_ldif("""
112 dn: cn=testuser,cn=users,""" + self.base_dn + """
115 userPassword: thatsAcomplPASS1
119 except LdbError, (num, _):
120 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
122 # Enables the user account
123 self.ldb.enable_account("(sAMAccountName=testuser)")
125 # Open a second LDB connection with the user credentials. Use the
126 # command line credentials for informations like the domain, the realm
127 # and the workstation.
128 creds2 = Credentials()
129 creds2.set_username("testuser")
130 creds2.set_password("thatsAcomplPASS1")
131 creds2.set_domain(creds.get_domain())
132 creds2.set_realm(creds.get_realm())
133 creds2.set_workstation(creds.get_workstation())
134 creds2.set_gensec_features(creds2.get_gensec_features()
135 | gensec.FEATURE_SEAL)
136 self.ldb2 = SamDB(url=host, credentials=creds2, lp=lp)
138 def test_unicodePwd_hash_set(self):
139 print "Performs a password hash set operation on 'unicodePwd' which should be prevented"
140 # Notice: Direct hash password sets should never work
143 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
144 m["unicodePwd"] = MessageElement("XXXXXXXXXXXXXXXX", FLAG_MOD_REPLACE,
149 except LdbError, (num, _):
150 self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
152 def test_unicodePwd_hash_change(self):
153 print "Performs a password hash change operation on 'unicodePwd' which should be prevented"
154 # Notice: Direct hash password changes should never work
156 # Hash password changes should never work
158 self.ldb2.modify_ldif("""
159 dn: cn=testuser,cn=users,""" + self.base_dn + """
162 unicodePwd: XXXXXXXXXXXXXXXX
164 unicodePwd: YYYYYYYYYYYYYYYY
167 except LdbError, (num, _):
168 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
170 def test_unicodePwd_clear_set(self):
171 print "Performs a password cleartext set operation on 'unicodePwd'"
174 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
175 m["unicodePwd"] = MessageElement("\"thatsAcomplPASS2\"".encode('utf-16-le'),
176 FLAG_MOD_REPLACE, "unicodePwd")
179 def test_unicodePwd_clear_change(self):
180 print "Performs a password cleartext change operation on 'unicodePwd'"
182 self.ldb2.modify_ldif("""
183 dn: cn=testuser,cn=users,""" + self.base_dn + """
186 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS1\"".encode('utf-16-le')) + """
188 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
193 self.ldb2.modify_ldif("""
194 dn: cn=testuser,cn=users,""" + self.base_dn + """
197 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS3\"".encode('utf-16-le')) + """
199 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS4\"".encode('utf-16-le')) + """
202 except LdbError, (num, msg):
203 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
204 self.assertTrue('00000056' in msg)
206 # A change to the same password again will not work (password history)
208 self.ldb2.modify_ldif("""
209 dn: cn=testuser,cn=users,""" + self.base_dn + """
212 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
214 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
217 except LdbError, (num, msg):
218 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
219 self.assertTrue('0000052D' in msg)
221 def test_dBCSPwd_hash_set(self):
222 print "Performs a password hash set operation on 'dBCSPwd' which should be prevented"
223 # Notice: Direct hash password sets should never work
226 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
227 m["dBCSPwd"] = MessageElement("XXXXXXXXXXXXXXXX", FLAG_MOD_REPLACE,
232 except LdbError, (num, _):
233 self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
235 def test_dBCSPwd_hash_change(self):
236 print "Performs a password hash change operation on 'dBCSPwd' which should be prevented"
237 # Notice: Direct hash password changes should never work
240 self.ldb2.modify_ldif("""
241 dn: cn=testuser,cn=users,""" + self.base_dn + """
244 dBCSPwd: XXXXXXXXXXXXXXXX
246 dBCSPwd: YYYYYYYYYYYYYYYY
249 except LdbError, (num, _):
250 self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
252 def test_userPassword_clear_set(self):
253 print "Performs a password cleartext set operation on 'userPassword'"
254 # Notice: This works only against Windows if "dSHeuristics" has been set
258 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
259 m["userPassword"] = MessageElement("thatsAcomplPASS2", FLAG_MOD_REPLACE,
263 def test_userPassword_clear_change(self):
264 print "Performs a password cleartext change operation on 'userPassword'"
265 # Notice: This works only against Windows if "dSHeuristics" has been set
268 self.ldb2.modify_ldif("""
269 dn: cn=testuser,cn=users,""" + self.base_dn + """
272 userPassword: thatsAcomplPASS1
274 userPassword: thatsAcomplPASS2
279 self.ldb2.modify_ldif("""
280 dn: cn=testuser,cn=users,""" + self.base_dn + """
283 userPassword: thatsAcomplPASS3
285 userPassword: thatsAcomplPASS4
288 except LdbError, (num, msg):
289 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
290 self.assertTrue('00000056' in msg)
292 # A change to the same password again will not work (password history)
294 self.ldb2.modify_ldif("""
295 dn: cn=testuser,cn=users,""" + self.base_dn + """
298 userPassword: thatsAcomplPASS2
300 userPassword: thatsAcomplPASS2
303 except LdbError, (num, msg):
304 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
305 self.assertTrue('0000052D' in msg)
307 def test_clearTextPassword_clear_set(self):
308 print "Performs a password cleartext set operation on 'clearTextPassword'"
309 # Notice: This never works against Windows - only supported by us
313 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
314 m["clearTextPassword"] = MessageElement("thatsAcomplPASS2".encode('utf-16-le'),
315 FLAG_MOD_REPLACE, "clearTextPassword")
317 # this passes against s4
318 except LdbError, (num, msg):
319 # "NO_SUCH_ATTRIBUTE" is returned by Windows -> ignore it
320 if num != ERR_NO_SUCH_ATTRIBUTE:
321 raise LdbError(num, msg)
323 def test_clearTextPassword_clear_change(self):
324 print "Performs a password cleartext change operation on 'clearTextPassword'"
325 # Notice: This never works against Windows - only supported by us
328 self.ldb2.modify_ldif("""
329 dn: cn=testuser,cn=users,""" + self.base_dn + """
331 delete: clearTextPassword
332 clearTextPassword:: """ + base64.b64encode("thatsAcomplPASS1".encode('utf-16-le')) + """
333 add: clearTextPassword
334 clearTextPassword:: """ + base64.b64encode("thatsAcomplPASS2".encode('utf-16-le')) + """
336 # this passes against s4
337 except LdbError, (num, msg):
338 # "NO_SUCH_ATTRIBUTE" is returned by Windows -> ignore it
339 if num != ERR_NO_SUCH_ATTRIBUTE:
340 raise LdbError(num, msg)
344 self.ldb2.modify_ldif("""
345 dn: cn=testuser,cn=users,""" + self.base_dn + """
347 delete: clearTextPassword
348 clearTextPassword:: """ + base64.b64encode("thatsAcomplPASS3".encode('utf-16-le')) + """
349 add: clearTextPassword
350 clearTextPassword:: """ + base64.b64encode("thatsAcomplPASS4".encode('utf-16-le')) + """
353 except LdbError, (num, msg):
354 # "NO_SUCH_ATTRIBUTE" is returned by Windows -> ignore it
355 if num != ERR_NO_SUCH_ATTRIBUTE:
356 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
357 self.assertTrue('00000056' in msg)
359 # A change to the same password again will not work (password history)
361 self.ldb2.modify_ldif("""
362 dn: cn=testuser,cn=users,""" + self.base_dn + """
364 delete: clearTextPassword
365 clearTextPassword:: """ + base64.b64encode("thatsAcomplPASS2".encode('utf-16-le')) + """
366 add: clearTextPassword
367 clearTextPassword:: """ + base64.b64encode("thatsAcomplPASS2".encode('utf-16-le')) + """
370 except LdbError, (num, msg):
371 # "NO_SUCH_ATTRIBUTE" is returned by Windows -> ignore it
372 if num != ERR_NO_SUCH_ATTRIBUTE:
373 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
374 self.assertTrue('0000052D' in msg)
376 def test_failures(self):
377 print "Performs some failure testing"
381 dn: cn=testuser,cn=users,""" + self.base_dn + """
384 userPassword: thatsAcomplPASS1
387 except LdbError, (num, _):
388 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
391 self.ldb2.modify_ldif("""
392 dn: cn=testuser,cn=users,""" + self.base_dn + """
395 userPassword: thatsAcomplPASS1
398 except LdbError, (num, _):
399 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
403 dn: cn=testuser,cn=users,""" + self.base_dn + """
408 except LdbError, (num, _):
409 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
412 self.ldb2.modify_ldif("""
413 dn: cn=testuser,cn=users,""" + self.base_dn + """
418 except LdbError, (num, _):
419 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
423 dn: cn=testuser,cn=users,""" + self.base_dn + """
426 userPassword: thatsAcomplPASS1
429 except LdbError, (num, _):
430 self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
433 self.ldb2.modify_ldif("""
434 dn: cn=testuser,cn=users,""" + self.base_dn + """
437 userPassword: thatsAcomplPASS1
440 except LdbError, (num, _):
441 self.assertEquals(num, ERR_INSUFFICIENT_ACCESS_RIGHTS)
445 dn: cn=testuser,cn=users,""" + self.base_dn + """
448 userPassword: thatsAcomplPASS1
450 userPassword: thatsAcomplPASS2
451 userPassword: thatsAcomplPASS2
454 except LdbError, (num, _):
455 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
458 self.ldb2.modify_ldif("""
459 dn: cn=testuser,cn=users,""" + self.base_dn + """
462 userPassword: thatsAcomplPASS1
464 userPassword: thatsAcomplPASS2
465 userPassword: thatsAcomplPASS2
468 except LdbError, (num, _):
469 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
473 dn: cn=testuser,cn=users,""" + self.base_dn + """
476 userPassword: thatsAcomplPASS1
477 userPassword: thatsAcomplPASS1
479 userPassword: thatsAcomplPASS2
482 except LdbError, (num, _):
483 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
486 self.ldb2.modify_ldif("""
487 dn: cn=testuser,cn=users,""" + self.base_dn + """
490 userPassword: thatsAcomplPASS1
491 userPassword: thatsAcomplPASS1
493 userPassword: thatsAcomplPASS2
496 except LdbError, (num, _):
497 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
501 dn: cn=testuser,cn=users,""" + self.base_dn + """
504 userPassword: thatsAcomplPASS1
506 userPassword: thatsAcomplPASS2
508 userPassword: thatsAcomplPASS2
511 except LdbError, (num, _):
512 self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
515 self.ldb2.modify_ldif("""
516 dn: cn=testuser,cn=users,""" + self.base_dn + """
519 userPassword: thatsAcomplPASS1
521 userPassword: thatsAcomplPASS2
523 userPassword: thatsAcomplPASS2
526 except LdbError, (num, _):
527 self.assertEquals(num, ERR_INSUFFICIENT_ACCESS_RIGHTS)
531 dn: cn=testuser,cn=users,""" + self.base_dn + """
534 userPassword: thatsAcomplPASS1
536 userPassword: thatsAcomplPASS1
538 userPassword: thatsAcomplPASS2
541 except LdbError, (num, _):
542 self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
545 self.ldb2.modify_ldif("""
546 dn: cn=testuser,cn=users,""" + self.base_dn + """
549 userPassword: thatsAcomplPASS1
551 userPassword: thatsAcomplPASS1
553 userPassword: thatsAcomplPASS2
556 except LdbError, (num, _):
557 self.assertEquals(num, ERR_INSUFFICIENT_ACCESS_RIGHTS)
561 dn: cn=testuser,cn=users,""" + self.base_dn + """
564 userPassword: thatsAcomplPASS1
566 userPassword: thatsAcomplPASS2
567 replace: userPassword
568 userPassword: thatsAcomplPASS3
571 except LdbError, (num, _):
572 self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
575 self.ldb2.modify_ldif("""
576 dn: cn=testuser,cn=users,""" + self.base_dn + """
579 userPassword: thatsAcomplPASS1
581 userPassword: thatsAcomplPASS2
582 replace: userPassword
583 userPassword: thatsAcomplPASS3
586 except LdbError, (num, _):
587 self.assertEquals(num, ERR_INSUFFICIENT_ACCESS_RIGHTS)
589 # Reverse order does work
590 self.ldb2.modify_ldif("""
591 dn: cn=testuser,cn=users,""" + self.base_dn + """
594 userPassword: thatsAcomplPASS2
596 userPassword: thatsAcomplPASS1
600 self.ldb2.modify_ldif("""
601 dn: cn=testuser,cn=users,""" + self.base_dn + """
604 userPassword: thatsAcomplPASS2
606 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS3\"".encode('utf-16-le')) + """
608 # this passes against s4
609 except LdbError, (num, _):
610 self.assertEquals(num, ERR_ATTRIBUTE_OR_VALUE_EXISTS)
613 self.ldb2.modify_ldif("""
614 dn: cn=testuser,cn=users,""" + self.base_dn + """
617 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS3\"".encode('utf-16-le')) + """
619 userPassword: thatsAcomplPASS4
621 # this passes against s4
622 except LdbError, (num, _):
623 self.assertEquals(num, ERR_NO_SUCH_ATTRIBUTE)
625 # Several password changes at once are allowed
627 dn: cn=testuser,cn=users,""" + self.base_dn + """
629 replace: userPassword
630 userPassword: thatsAcomplPASS1
631 userPassword: thatsAcomplPASS2
634 # Several password changes at once are allowed
636 dn: cn=testuser,cn=users,""" + self.base_dn + """
638 replace: userPassword
639 userPassword: thatsAcomplPASS1
640 userPassword: thatsAcomplPASS2
641 replace: userPassword
642 userPassword: thatsAcomplPASS3
643 replace: userPassword
644 userPassword: thatsAcomplPASS4
647 # This surprisingly should work
648 delete_force(self.ldb, "cn=testuser2,cn=users," + self.base_dn)
650 "dn": "cn=testuser2,cn=users," + self.base_dn,
651 "objectclass": "user",
652 "userPassword": ["thatsAcomplPASS1", "thatsAcomplPASS2"] })
654 # This surprisingly should work
655 delete_force(self.ldb, "cn=testuser2,cn=users," + self.base_dn)
657 "dn": "cn=testuser2,cn=users," + self.base_dn,
658 "objectclass": "user",
659 "userPassword": ["thatsAcomplPASS1", "thatsAcomplPASS1"] })
661 def test_empty_passwords(self):
662 print "Performs some empty passwords testing"
666 "dn": "cn=testuser2,cn=users," + self.base_dn,
667 "objectclass": "user",
670 except LdbError, (num, _):
671 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
675 "dn": "cn=testuser2,cn=users," + self.base_dn,
676 "objectclass": "user",
679 except LdbError, (num, _):
680 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
684 "dn": "cn=testuser2,cn=users," + self.base_dn,
685 "objectclass": "user",
686 "userPassword": [] })
688 except LdbError, (num, _):
689 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
693 "dn": "cn=testuser2,cn=users," + self.base_dn,
694 "objectclass": "user",
695 "clearTextPassword": [] })
697 except LdbError, (num, _):
698 self.assertTrue(num == ERR_CONSTRAINT_VIOLATION or
699 num == ERR_NO_SUCH_ATTRIBUTE) # for Windows
701 delete_force(self.ldb, "cn=testuser2,cn=users," + self.base_dn)
704 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
705 m["unicodePwd"] = MessageElement([], FLAG_MOD_ADD, "unicodePwd")
709 except LdbError, (num, _):
710 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
713 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
714 m["dBCSPwd"] = MessageElement([], FLAG_MOD_ADD, "dBCSPwd")
718 except LdbError, (num, _):
719 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
722 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
723 m["userPassword"] = MessageElement([], FLAG_MOD_ADD, "userPassword")
727 except LdbError, (num, _):
728 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
731 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
732 m["clearTextPassword"] = MessageElement([], FLAG_MOD_ADD, "clearTextPassword")
736 except LdbError, (num, _):
737 self.assertTrue(num == ERR_CONSTRAINT_VIOLATION or
738 num == ERR_NO_SUCH_ATTRIBUTE) # for Windows
741 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
742 m["unicodePwd"] = MessageElement([], FLAG_MOD_REPLACE, "unicodePwd")
746 except LdbError, (num, _):
747 self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
750 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
751 m["dBCSPwd"] = MessageElement([], FLAG_MOD_REPLACE, "dBCSPwd")
755 except LdbError, (num, _):
756 self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
759 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
760 m["userPassword"] = MessageElement([], FLAG_MOD_REPLACE, "userPassword")
764 except LdbError, (num, _):
765 self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
768 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
769 m["clearTextPassword"] = MessageElement([], FLAG_MOD_REPLACE, "clearTextPassword")
773 except LdbError, (num, _):
774 self.assertTrue(num == ERR_UNWILLING_TO_PERFORM or
775 num == ERR_NO_SUCH_ATTRIBUTE) # for Windows
778 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
779 m["unicodePwd"] = MessageElement([], FLAG_MOD_DELETE, "unicodePwd")
783 except LdbError, (num, _):
784 self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
787 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
788 m["dBCSPwd"] = MessageElement([], FLAG_MOD_DELETE, "dBCSPwd")
792 except LdbError, (num, _):
793 self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
796 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
797 m["userPassword"] = MessageElement([], FLAG_MOD_DELETE, "userPassword")
801 except LdbError, (num, _):
802 self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
805 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
806 m["clearTextPassword"] = MessageElement([], FLAG_MOD_DELETE, "clearTextPassword")
810 except LdbError, (num, _):
811 self.assertTrue(num == ERR_CONSTRAINT_VIOLATION or
812 num == ERR_NO_SUCH_ATTRIBUTE) # for Windows
814 def test_plain_userPassword(self):
815 print "Performs testing about the standard 'userPassword' behaviour"
817 # Delete the "dSHeuristics"
818 ldb.set_dsheuristics(None)
820 time.sleep(1) # This switching time is strictly needed!
823 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
824 m["userPassword"] = MessageElement("myPassword", FLAG_MOD_ADD,
828 res = ldb.search("cn=testuser,cn=users," + self.base_dn,
829 scope=SCOPE_BASE, attrs=["userPassword"])
830 self.assertTrue(len(res) == 1)
831 self.assertTrue("userPassword" in res[0])
832 self.assertEquals(res[0]["userPassword"][0], "myPassword")
835 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
836 m["userPassword"] = MessageElement("myPassword2", FLAG_MOD_REPLACE,
840 res = ldb.search("cn=testuser,cn=users," + self.base_dn,
841 scope=SCOPE_BASE, attrs=["userPassword"])
842 self.assertTrue(len(res) == 1)
843 self.assertTrue("userPassword" in res[0])
844 self.assertEquals(res[0]["userPassword"][0], "myPassword2")
847 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
848 m["userPassword"] = MessageElement([], FLAG_MOD_DELETE,
852 res = ldb.search("cn=testuser,cn=users," + self.base_dn,
853 scope=SCOPE_BASE, attrs=["userPassword"])
854 self.assertTrue(len(res) == 1)
855 self.assertFalse("userPassword" in res[0])
857 # Set the test "dSHeuristics" to deactivate "userPassword" pwd changes
858 ldb.set_dsheuristics("000000000")
861 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
862 m["userPassword"] = MessageElement("myPassword3", FLAG_MOD_REPLACE,
866 res = ldb.search("cn=testuser,cn=users," + self.base_dn,
867 scope=SCOPE_BASE, attrs=["userPassword"])
868 self.assertTrue(len(res) == 1)
869 self.assertTrue("userPassword" in res[0])
870 self.assertEquals(res[0]["userPassword"][0], "myPassword3")
872 # Set the test "dSHeuristics" to deactivate "userPassword" pwd changes
873 ldb.set_dsheuristics("000000002")
876 m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
877 m["userPassword"] = MessageElement("myPassword4", FLAG_MOD_REPLACE,
881 res = ldb.search("cn=testuser,cn=users," + self.base_dn,
882 scope=SCOPE_BASE, attrs=["userPassword"])
883 self.assertTrue(len(res) == 1)
884 self.assertTrue("userPassword" in res[0])
885 self.assertEquals(res[0]["userPassword"][0], "myPassword4")
887 # Reset the test "dSHeuristics" (reactivate "userPassword" pwd changes)
888 ldb.set_dsheuristics("000000001")
890 def test_zero_length(self):
891 # Get the old "minPwdLength"
892 minPwdLength = ldb.get_minPwdLength()
893 # Set it temporarely to "0"
894 ldb.set_minPwdLength("0")
896 # Get the old "pwdProperties"
897 pwdProperties = ldb.get_pwdProperties()
898 # Set them temporarely to "0" (to deactivate eventually the complexity)
899 ldb.set_pwdProperties("0")
901 ldb.setpassword("(sAMAccountName=testuser)", "")
903 # Reset the "pwdProperties" as they were before
904 ldb.set_pwdProperties(pwdProperties)
906 # Reset the "minPwdLength" as it was before
907 ldb.set_minPwdLength(minPwdLength)
910 super(PasswordTests, self).tearDown()
911 delete_force(self.ldb, "cn=testuser,cn=users," + self.base_dn)
912 delete_force(self.ldb, "cn=testuser2,cn=users," + self.base_dn)
913 # Close the second LDB connection (with the user credentials)
916 if not "://" in host:
917 if os.path.isfile(host):
918 host = "tdb://%s" % host
920 host = "ldap://%s" % host
922 ldb = SamDB(url=host, session_info=system_session(lp), credentials=creds, lp=lp)
924 # Gets back the basedn
925 base_dn = ldb.domain_dn()
927 # Gets back the configuration basedn
928 configuration_dn = ldb.get_config_basedn().get_linearized()
930 # Get the old "dSHeuristics" if it was set
931 dsheuristics = ldb.get_dsheuristics()
933 # Set the "dSHeuristics" to activate the correct "userPassword" behaviour
934 ldb.set_dsheuristics("000000001")
936 # Get the old "minPwdAge"
937 minPwdAge = ldb.get_minPwdAge()
939 # Set it temporarely to "0"
940 ldb.set_minPwdAge("0")
942 runner = SubunitTestRunner()
944 if not runner.run(unittest.makeSuite(PasswordTests)).wasSuccessful():
947 # Reset the "dSHeuristics" as they were before
948 ldb.set_dsheuristics(dsheuristics)
950 # Reset the "minPwdAge" as it was before
951 ldb.set_minPwdAge(minPwdAge)