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 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(), 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
)