password_lockout: Move lockoutObservationWindow tests from setUp
[nivanova/samba-autobuild/.git] / source4 / dsdb / tests / python / password_lockout.py
index e6badbce4e611234e65f866dc28de07c5ab6f612..c843e37cb8477f49f8849c2e5fd01d3238d91e06 100755 (executable)
@@ -33,7 +33,7 @@ from samba.tests import delete_force
 from samba.dcerpc import security, samr
 from samba.ndr import ndr_unpack
 
-parser = optparse.OptionParser("passwords.py [options] <host>")
+parser = optparse.OptionParser("password_lockout.py [options] <host>")
 sambaopts = options.SambaOptions(parser)
 parser.add_option_group(sambaopts)
 parser.add_option_group(options.VersionOptions(parser))
@@ -53,45 +53,21 @@ host = args[0]
 lp = sambaopts.get_loadparm()
 global_creds = credopts.get_credentials(lp)
 
-# Force an encrypted connection
-global_creds.set_gensec_features(global_creds.get_gensec_features() |
-                                 gensec.FEATURE_SEAL)
-
-def insta_creds(template=global_creds):
-    # get a copy of the global creds or a the passed in creds
-    c = Credentials()
-    c.set_username("testuser")
-    c.set_password("thatsAcomplPASS1")
-    c.set_domain(template.get_domain())
-    c.set_realm(template.get_realm())
-    c.set_workstation(template.get_workstation())
-    c.set_gensec_features(c.get_gensec_features()
-                          | gensec.FEATURE_SEAL)
-    c.set_kerberos_state(template.get_kerberos_state())
-    return c
+import password_lockout_base
 
 #
 # Tests start here
 #
 
-class PasswordTests(samba.tests.TestCase):
-
-    def _open_samr_user(self, res):
-        self.assertTrue("objectSid" in res[0])
-
-        (domain_sid, rid) = ndr_unpack(security.dom_sid, res[0]["objectSid"][0]).split()
-        self.assertEquals(self.domain_sid, domain_sid)
-
-        return self.samr.OpenUser(self.samr_domain, security.SEC_FLAG_MAXIMUM_ALLOWED, rid)
-
-    def _reset_samr(self, res):
-
-        # Now reset the lockout, by removing ACB_AUTOLOCK (which removes the lock, despite being a generated attribute)
-        samr_user = self._open_samr_user(res)
-        acb_info = self.samr.QueryUserInfo(samr_user, 16)
-        acb_info.acct_flags &= ~samr.ACB_AUTOLOCK
-        self.samr.SetUserInfo(samr_user, 16, acb_info)
-        self.samr.Close(samr_user)
+class PasswordTests(password_lockout_base.BasePasswordTestCase):
+    def setUp(self):
+        self.host = host
+        self.host_url = host_url
+        self.lp = lp
+        self.global_creds = global_creds
+        self.ldb = SamDB(url=self.host_url, session_info=system_session(self.lp),
+                         credentials=self.global_creds, lp=self.lp)
+        super(PasswordTests, self).setUp()
 
     def _reset_ldap_lockoutTime(self, res):
         self.ldb.modify_ldif("""
@@ -128,492 +104,50 @@ userAccountControl: %d
         else:
             self.assertTrue(False, msg="Invalid reset method[%s]" % method)
 
-    def _check_attribute(self, res, name, value):
-        if value is None:
-            self.assertTrue(name not in res[0],
-                            msg="attr[%s]=%r on dn[%s]" %
-                            (name, res[0], res[0].dn))
-            return
-
-        if isinstance(value, tuple):
-            (mode, value) = value
-        else:
-            mode = "equal"
-
-        if mode == "ignore":
-            return
-
-        if mode == "absent":
-            self.assertFalse(name in res[0],
-                            msg="attr[%s] not missing on dn[%s]" %
-                            (name, res[0].dn))
-            return
-
-        self.assertTrue(name in res[0],
-                        msg="attr[%s] missing on dn[%s]" %
-                        (name, res[0].dn))
-        self.assertTrue(len(res[0][name]) == 1,
-                        msg="attr[%s]=%r on dn[%s]" %
-                        (name, res[0][name], res[0].dn))
-
-
-        print  "%s = '%s'" % (name, res[0][name][0])
-
-        if mode == "present":
-            return
-
-        if mode == "equal":
-            v = int(res[0][name][0])
-            value = int(value)
-            msg = ("attr[%s]=[%s] != [%s] on dn[%s]\n"
-                   "(diff %d; actual value is %s than expected)"  %
-                   (name, v, value, res[0].dn, v - value,
-                    ('less' if v < value else 'greater')))
-
-            self.assertTrue(v == value, msg)
-            return
-
-        if mode == "greater":
-            v = int(res[0][name][0])
-            self.assertTrue(v > int(value),
-                            msg="attr[%s]=[%s] <= [%s] on dn[%s] (diff %d)" %
-                            (name, v, int(value), res[0].dn, v - int(value)))
-            return
-        if mode == "less":
-            v = int(res[0][name][0])
-            self.assertTrue(v < int(value),
-                            msg="attr[%s]=[%s] >= [%s] on dn[%s] (diff %d)" %
-                            (name, v, int(value), res[0].dn, v - int(value)))
-            return
-        self.assertEqual(mode, not mode, "Invalid Mode[%s]" % mode)
-
-    def _check_account(self, dn,
-                       badPwdCount=None,
-                       badPasswordTime=None,
-                       lastLogon=None,
-                       lastLogonTimestamp=None,
-                       lockoutTime=None,
-                       userAccountControl=None,
-                       msDSUserAccountControlComputed=None,
-                       effective_bad_password_count=None,
-                       msg=None):
-        print '-=' * 36
-        if msg is not None:
-            print  "\033[01;32m %s \033[00m\n" % msg
-        attrs = [
-           "objectSid",
-           "badPwdCount",
-           "badPasswordTime",
-           "lastLogon",
-           "lastLogonTimestamp",
-           "lockoutTime",
-           "userAccountControl",
-           "msDS-User-Account-Control-Computed"
-        ]
-
-        # in order to prevent some time resolution problems we sleep for
-        # 10 micro second
-        time.sleep(0.01)
-
-        res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs)
-        self.assertTrue(len(res) == 1)
-        self._check_attribute(res, "badPwdCount", badPwdCount)
-        self._check_attribute(res, "badPasswordTime", badPasswordTime)
-        self._check_attribute(res, "lastLogon", lastLogon)
-        self._check_attribute(res, "lastLogonTimestamp", lastLogonTimestamp)
-        self._check_attribute(res, "lockoutTime", lockoutTime)
-        self._check_attribute(res, "userAccountControl", userAccountControl)
-        self._check_attribute(res, "msDS-User-Account-Control-Computed",
-                              msDSUserAccountControlComputed)
-
-        samr_user = self._open_samr_user(res)
-        uinfo3 = self.samr.QueryUserInfo(samr_user, 3)
-        uinfo5 = self.samr.QueryUserInfo(samr_user, 5)
-        uinfo16 = self.samr.QueryUserInfo(samr_user, 16)
-        uinfo21 = self.samr.QueryUserInfo(samr_user, 21)
-        self.samr.Close(samr_user)
-
-        expected_acb_info = 0
-        if userAccountControl & dsdb.UF_NORMAL_ACCOUNT:
-            expected_acb_info |= samr.ACB_NORMAL
-        if userAccountControl & dsdb.UF_ACCOUNTDISABLE:
-            expected_acb_info |= samr.ACB_DISABLED
-        if userAccountControl & dsdb.UF_PASSWD_NOTREQD:
-            expected_acb_info |= samr.ACB_PWNOTREQ
-        if msDSUserAccountControlComputed & dsdb.UF_LOCKOUT:
-            expected_acb_info |= samr.ACB_AUTOLOCK
-        if msDSUserAccountControlComputed & dsdb.UF_PASSWORD_EXPIRED:
-            expected_acb_info |= samr.ACB_PW_EXPIRED
-
-        expected_bad_password_count = 0
-        if badPwdCount is not None:
-            expected_bad_password_count = badPwdCount
-        if effective_bad_password_count is None:
-            effective_bad_password_count = expected_bad_password_count
-
-        self.assertEquals(uinfo3.acct_flags, expected_acb_info)
-        self.assertEquals(uinfo3.bad_password_count, expected_bad_password_count)
-
-        self.assertEquals(uinfo5.acct_flags, expected_acb_info)
-        self.assertEquals(uinfo5.bad_password_count, effective_bad_password_count)
-
-        self.assertEquals(uinfo16.acct_flags, expected_acb_info)
-
-        self.assertEquals(uinfo21.acct_flags, expected_acb_info)
-        self.assertEquals(uinfo21.bad_password_count, effective_bad_password_count)
-
-        # check LDAP again and make sure the samr.QueryUserInfo
-        # doesn't have any impact.
-        res2 = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs)
-        self.assertEquals(res[0], res2[0])
-
-        # in order to prevent some time resolution problems we sleep for
-        # 10 micro second
-        time.sleep(0.01)
-        return res
-
-    def assertLoginFailure(self, url, creds, lp, errno=ERR_INVALID_CREDENTIALS):
-        try:
-            ldb = SamDB(url=url, credentials=creds, lp=lp)
-            self.fail("Login unexpectedly succeeded")
-        except LdbError, (num, msg):
-            if errno is not None:
-                self.assertEquals(num, errno, ("Login failed in the wrong way"
-                                               "(got err %d, expected %d)" %
-                                               (num, errno)))
-
-    def setUp(self):
-        super(PasswordTests, self).setUp()
-
-        self.ldb = SamDB(url=host_url, session_info=system_session(lp),
-                         credentials=global_creds, lp=lp)
-
-        # Gets back the basedn
-        base_dn = self.ldb.domain_dn()
-
-        # Gets back the configuration basedn
-        configuration_dn = self.ldb.get_config_basedn().get_linearized()
-
-        # Get the old "dSHeuristics" if it was set
-        dsheuristics = self.ldb.get_dsheuristics()
-
-        # Reset the "dSHeuristics" as they were before
-        self.addCleanup(self.ldb.set_dsheuristics, dsheuristics)
-
-        res = self.ldb.search(base_dn,
-                         scope=SCOPE_BASE, attrs=["lockoutDuration", "lockOutObservationWindow", "lockoutThreshold"])
-
-        if "lockoutDuration" in res[0]:
-            lockoutDuration = res[0]["lockoutDuration"][0]
-        else:
-            lockoutDuration = 0
-
-        if "lockoutObservationWindow" in res[0]:
-            lockoutObservationWindow = res[0]["lockoutObservationWindow"][0]
-        else:
-            lockoutObservationWindow = 0
+    def _test_userPassword_lockout_with_clear_change(self, creds, other_ldb, method,
+                                                     initial_lastlogon_relation=None):
+        # Notice: This works only against Windows if "dSHeuristics" has been set
+        # properly
+        username = creds.get_username()
+        userpass = creds.get_password()
+        userdn = "cn=%s,cn=users,%s" % (username, self.base_dn)
 
-        if "lockoutThreshold" in res[0]:
-            lockoutThreshold = res[0]["lockoutThreshold"][0]
+        use_kerberos = creds.get_kerberos_state()
+        if use_kerberos == MUST_USE_KERBEROS:
+            logoncount_relation = 'greater'
+            lastlogon_relation = 'greater'
+            print "Performs a password cleartext change operation on 'userPassword' using Kerberos"
         else:
-            lockoutTreshold = 0
-
-        self.addCleanup(self.ldb.modify_ldif, """
-dn: """ + base_dn + """
-changetype: modify
-replace: lockoutDuration
-lockoutDuration: """ + str(lockoutDuration) + """
-replace: lockoutObservationWindow
-lockoutObservationWindow: """ + str(lockoutObservationWindow) + """
-replace: lockoutThreshold
-lockoutThreshold: """ + str(lockoutThreshold) + """
-""")
-
-        m = Message()
-        m.dn = Dn(self.ldb, base_dn)
-
-        self.account_lockout_duration = 10
-        account_lockout_duration_ticks = -int(self.account_lockout_duration * (1e7))
-
-        m["lockoutDuration"] = MessageElement(str(account_lockout_duration_ticks),
-                                              FLAG_MOD_REPLACE, "lockoutDuration")
-
-        account_lockout_threshold = 3
-        m["lockoutThreshold"] = MessageElement(str(account_lockout_threshold),
-                                               FLAG_MOD_REPLACE, "lockoutThreshold")
-
-        self.lockout_observation_window = 5
-        lockout_observation_window_ticks = -int(self.lockout_observation_window * (1e7))
-
-        m["lockOutObservationWindow"] = MessageElement(str(lockout_observation_window_ticks),
-                                                       FLAG_MOD_REPLACE, "lockOutObservationWindow")
-
-        self.ldb.modify(m)
-
-        # Set the "dSHeuristics" to activate the correct "userPassword" behaviour
-        self.ldb.set_dsheuristics("000000001")
-
-        # Get the old "minPwdAge"
-        minPwdAge = self.ldb.get_minPwdAge()
-
-        # Reset the "minPwdAge" as it was before
-        self.addCleanup(self.ldb.set_minPwdAge, minPwdAge)
-
-        # Set it temporarely to "0"
-        self.ldb.set_minPwdAge("0")
-
-        self.base_dn = self.ldb.domain_dn()
-
-        self.domain_sid = security.dom_sid(self.ldb.get_domain_sid())
-        self.samr = samr.samr("ncacn_ip_tcp:%s[sign]" % host, lp, global_creds)
-        self.samr_handle = self.samr.Connect2(None, security.SEC_FLAG_MAXIMUM_ALLOWED)
-        self.samr_domain = self.samr.OpenDomain(self.samr_handle, security.SEC_FLAG_MAXIMUM_ALLOWED, self.domain_sid)
-
-        # (Re)adds the test user "testuser" with no password atm
-        delete_force(self.ldb, "cn=testuser,cn=users," + self.base_dn)
-        self.ldb.add({
-             "dn": "cn=testuser,cn=users," + self.base_dn,
-             "objectclass": "user",
-             "sAMAccountName": "testuser"})
-
-        res = self._check_account("cn=testuser,cn=users," + self.base_dn,
-                                  badPwdCount=0,
-                                  badPasswordTime=0,
-                                  lastLogon=0,
-                                  lastLogonTimestamp=('absent', None),
-                                  userAccountControl=
-                                    dsdb.UF_NORMAL_ACCOUNT |
-                                    dsdb.UF_ACCOUNTDISABLE |
-                                    dsdb.UF_PASSWD_NOTREQD,
-                                  msDSUserAccountControlComputed=
-                                    dsdb.UF_PASSWORD_EXPIRED)
-
-        # SAMR doesn't have any impact if dsdb.UF_LOCKOUT isn't present.
-        # It doesn't create "lockoutTime" = 0.
-        self._reset_samr(res)
-
-        res = self._check_account("cn=testuser,cn=users," + self.base_dn,
-                                  badPwdCount=0,
-                                  badPasswordTime=0,
-                                  lastLogon=0,
-                                  lastLogonTimestamp=('absent', None),
-                                  userAccountControl=
-                                    dsdb.UF_NORMAL_ACCOUNT |
-                                    dsdb.UF_ACCOUNTDISABLE |
-                                    dsdb.UF_PASSWD_NOTREQD,
-                                  msDSUserAccountControlComputed=
-                                    dsdb.UF_PASSWORD_EXPIRED)
-
-        # Tests a password change when we don't have any password yet with a
-        # wrong old password
-        try:
-            self.ldb.modify_ldif("""
-dn: cn=testuser,cn=users,""" + self.base_dn + """
-changetype: modify
-delete: userPassword
-userPassword: noPassword
-add: userPassword
-userPassword: thatsAcomplPASS2
-""")
-            self.fail()
-        except LdbError, (num, msg):
-            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
-            # Windows (2008 at least) seems to have some small bug here: it
-            # returns "0000056A" on longer (always wrong) previous passwords.
-            self.assertTrue('00000056' in msg)
-
-        res = self._check_account("cn=testuser,cn=users," + self.base_dn,
-                                  badPwdCount=1,
-                                  badPasswordTime=("greater", 0),
-                                  lastLogon=0,
-                                  lastLogonTimestamp=('absent', None),
-                                  userAccountControl=
-                                    dsdb.UF_NORMAL_ACCOUNT |
-                                    dsdb.UF_ACCOUNTDISABLE |
-                                    dsdb.UF_PASSWD_NOTREQD,
-                                  msDSUserAccountControlComputed=
-                                    dsdb.UF_PASSWORD_EXPIRED)
-        badPasswordTime = int(res[0]["badPasswordTime"][0])
-
-        # Sets the initial user password with a "special" password change
-        # I think that this internally is a password set operation and it can
-        # only be performed by someone which has password set privileges on the
-        # account (at least in s4 we do handle it like that).
-        self.ldb.modify_ldif("""
-dn: cn=testuser,cn=users,""" + self.base_dn + """
-changetype: modify
-delete: userPassword
-add: userPassword
-userPassword: thatsAcomplPASS1
-""")
-
-        res = self._check_account("cn=testuser,cn=users," + self.base_dn,
-                                  badPwdCount=1,
-                                  badPasswordTime=badPasswordTime,
-                                  lastLogon=0,
-                                  lastLogonTimestamp=('absent', None),
-                                  userAccountControl=
-                                    dsdb.UF_NORMAL_ACCOUNT |
-                                    dsdb.UF_ACCOUNTDISABLE |
-                                    dsdb.UF_PASSWD_NOTREQD,
-                                  msDSUserAccountControlComputed=0)
-
-        # Enables the user account
-        self.ldb.enable_account("(sAMAccountName=testuser)")
-
-        res = self._check_account("cn=testuser,cn=users," + self.base_dn,
-                                  badPwdCount=1,
-                                  badPasswordTime=badPasswordTime,
-                                  lastLogon=0,
-                                  lastLogonTimestamp=('absent', None),
-                                  userAccountControl=
-                                    dsdb.UF_NORMAL_ACCOUNT,
-                                  msDSUserAccountControlComputed=0)
-
-        # Open a second LDB connection with the user credentials. Use the
-        # command line credentials for informations like the domain, the realm
-        # and the workstation.
-        creds2 = insta_creds()
-
-        self.ldb2 = SamDB(url=host_url, credentials=creds2, lp=lp)
-
-        res = self._check_account("cn=testuser,cn=users," + self.base_dn,
-                                  badPwdCount=0,
-                                  badPasswordTime=badPasswordTime,
-                                  lastLogon=('greater', 0),
-                                  lastLogonTimestamp=('greater', 0),
-                                  userAccountControl=
-                                    dsdb.UF_NORMAL_ACCOUNT,
-                                  msDSUserAccountControlComputed=0)
-
-        lastLogon = int(res[0]["lastLogon"][0])
-        self.assertGreater(lastLogon, badPasswordTime)
-
-     # (Re)adds the test user "testuser3" with no password atm
-        delete_force(self.ldb, "cn=testuser3,cn=users," + self.base_dn)
-        self.ldb.add({
-             "dn": "cn=testuser3,cn=users," + self.base_dn,
-             "objectclass": "user",
-             "sAMAccountName": "testuser3"})
-
-        res = self._check_account("cn=testuser3,cn=users," + self.base_dn,
-                                  badPwdCount=0,
-                                  badPasswordTime=0,
-                                  lastLogon=0,
-                                  lastLogonTimestamp=('absent', None),
-                                  userAccountControl=
-                                    dsdb.UF_NORMAL_ACCOUNT |
-                                    dsdb.UF_ACCOUNTDISABLE |
-                                    dsdb.UF_PASSWD_NOTREQD,
-                                  msDSUserAccountControlComputed=
-                                    dsdb.UF_PASSWORD_EXPIRED)
-
-        # Tests a password change when we don't have any password yet with a
-        # wrong old password
-        try:
-            self.ldb.modify_ldif("""
-dn: cn=testuser3,cn=users,""" + self.base_dn + """
-changetype: modify
-delete: userPassword
-userPassword: noPassword
-add: userPassword
-userPassword: thatsAcomplPASS2
-""")
-            self.fail()
-        except LdbError, (num, msg):
-            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
-            # Windows (2008 at least) seems to have some small bug here: it
-            # returns "0000056A" on longer (always wrong) previous passwords.
-            self.assertTrue('00000056' in msg)
-
-        res = self._check_account("cn=testuser3,cn=users," + self.base_dn,
-                                  badPwdCount=1,
-                                  badPasswordTime=("greater", 0),
-                                  lastLogon=0,
-                                  lastLogonTimestamp=('absent', None),
-                                  userAccountControl=
-                                    dsdb.UF_NORMAL_ACCOUNT |
-                                    dsdb.UF_ACCOUNTDISABLE |
-                                    dsdb.UF_PASSWD_NOTREQD,
-                                  msDSUserAccountControlComputed=
-                                    dsdb.UF_PASSWORD_EXPIRED)
-        badPasswordTime3 = int(res[0]["badPasswordTime"][0])
-
-        # Sets the initial user password with a "special" password change
-        # I think that this internally is a password set operation and it can
-        # only be performed by someone which has password set privileges on the
-        # account (at least in s4 we do handle it like that).
-        self.ldb.modify_ldif("""
-dn: cn=testuser3,cn=users,""" + self.base_dn + """
-changetype: modify
-delete: userPassword
-add: userPassword
-userPassword: thatsAcomplPASS1
-""")
-
-        res = self._check_account("cn=testuser3,cn=users," + self.base_dn,
-                                  badPwdCount=1,
-                                  badPasswordTime=badPasswordTime3,
-                                  lastLogon=0,
-                                  lastLogonTimestamp=('absent', None),
-                                  userAccountControl=
-                                    dsdb.UF_NORMAL_ACCOUNT |
-                                    dsdb.UF_ACCOUNTDISABLE |
-                                    dsdb.UF_PASSWD_NOTREQD,
-                                  msDSUserAccountControlComputed=0)
-
-        # Enables the user account
-        self.ldb.enable_account("(sAMAccountName=testuser3)")
-
-        res = self._check_account("cn=testuser3,cn=users," + self.base_dn,
-                                  badPwdCount=1,
-                                  badPasswordTime=badPasswordTime3,
-                                  lastLogon=0,
-                                  lastLogonTimestamp=('absent', None),
-                                  userAccountControl=
-                                    dsdb.UF_NORMAL_ACCOUNT,
-                                  msDSUserAccountControlComputed=0)
-
-        # Open a second LDB connection with the user credentials. Use the
-        # command line credentials for informations like the domain, the realm
-        # and the workstation.
-        creds3 = insta_creds()
-        creds3.set_username("testuser3")
-        creds3.set_password("thatsAcomplPASS1")
-        self.ldb3 = SamDB(url=host_url, credentials=creds3, lp=lp)
+            logoncount_relation = 'equal'
+            lastlogon_relation = 'equal'
+            print "Performs a password cleartext change operation on 'userPassword' using NTLMSSP"
 
-        res = self._check_account("cn=testuser3,cn=users," + self.base_dn,
-                                  badPwdCount=0,
-                                  badPasswordTime=badPasswordTime3,
-                                  lastLogon=('greater', badPasswordTime3),
-                                  lastLogonTimestamp=('greater', badPasswordTime3),
-                                  userAccountControl=
-                                    dsdb.UF_NORMAL_ACCOUNT,
-                                  msDSUserAccountControlComputed=0)
+        if initial_lastlogon_relation is not None:
+            lastlogon_relation = initial_lastlogon_relation
 
-    def _test_userPassword_lockout_with_clear_change(self, method):
-        print "Performs a password cleartext change operation on 'userPassword'"
-        # Notice: This works only against Windows if "dSHeuristics" has been set
-        # properly
-
-        res = self._check_account("cn=testuser,cn=users," + self.base_dn,
+        res = self._check_account(userdn,
                                   badPwdCount=0,
                                   badPasswordTime=("greater", 0),
-                                  lastLogon=('greater', 0),
+                                  logonCount=(logoncount_relation, 0),
+                                  lastLogon=(lastlogon_relation, 0),
                                   lastLogonTimestamp=('greater', 0),
                                   userAccountControl=
                                     dsdb.UF_NORMAL_ACCOUNT,
                                   msDSUserAccountControlComputed=0)
         badPasswordTime = int(res[0]["badPasswordTime"][0])
+        logonCount = int(res[0]["logonCount"][0])
         lastLogon = int(res[0]["lastLogon"][0])
         lastLogonTimestamp = int(res[0]["lastLogonTimestamp"][0])
+        if lastlogon_relation == 'greater':
+            self.assertGreater(lastLogon, badPasswordTime)
+            self.assertGreaterEqual(lastLogon, lastLogonTimestamp)
 
         # Change password on a connection as another user
 
         # Wrong old password
         try:
-            self.ldb3.modify_ldif("""
-dn: cn=testuser,cn=users,""" + self.base_dn + """
+            other_ldb.modify_ldif("""
+dn: """ + userdn + """
 changetype: modify
 delete: userPassword
 userPassword: thatsAcomplPASS1x
@@ -622,12 +156,13 @@ userPassword: thatsAcomplPASS2
 """)
             self.fail()
         except LdbError, (num, msg):
-            self.assertTrue('00000056' in msg)
             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
+            self.assertTrue('00000056' in msg, msg)
 
-        res = self._check_account("cn=testuser,cn=users," + self.base_dn,
+        res = self._check_account(userdn,
                                   badPwdCount=1,
                                   badPasswordTime=("greater", badPasswordTime),
+                                  logonCount=logonCount,
                                   lastLogon=lastLogon,
                                   lastLogonTimestamp=lastLogonTimestamp,
                                   userAccountControl=
@@ -636,18 +171,19 @@ userPassword: thatsAcomplPASS2
         badPasswordTime = int(res[0]["badPasswordTime"][0])
 
         # Correct old password
-        self.ldb3.modify_ldif("""
-dn: cn=testuser,cn=users,""" + self.base_dn + """
+        other_ldb.modify_ldif("""
+dn: """ + userdn + """
 changetype: modify
 delete: userPassword
-userPassword: thatsAcomplPASS1
+userPassword: """ + userpass + """
 add: userPassword
 userPassword: thatsAcomplPASS2
 """)
 
-        res = self._check_account("cn=testuser,cn=users," + self.base_dn,
+        res = self._check_account(userdn,
                                   badPwdCount=1,
                                   badPasswordTime=badPasswordTime,
+                                  logonCount=logonCount,
                                   lastLogon=lastLogon,
                                   lastLogonTimestamp=lastLogonTimestamp,
                                   userAccountControl=
@@ -656,8 +192,8 @@ userPassword: thatsAcomplPASS2
 
         # Wrong old password
         try:
-            self.ldb3.modify_ldif("""
-dn: cn=testuser,cn=users,""" + self.base_dn + """
+            other_ldb.modify_ldif("""
+dn: """ + userdn + """
 changetype: modify
 delete: userPassword
 userPassword: thatsAcomplPASS1x
@@ -666,12 +202,13 @@ userPassword: thatsAcomplPASS2
 """)
             self.fail()
         except LdbError, (num, msg):
-            self.assertTrue('00000056' in msg)
             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
+            self.assertTrue('00000056' in msg, msg)
 
-        res = self._check_account("cn=testuser,cn=users," + self.base_dn,
+        res = self._check_account(userdn,
                                   badPwdCount=2,
                                   badPasswordTime=("greater", badPasswordTime),
+                                  logonCount=logonCount,
                                   lastLogon=lastLogon,
                                   lastLogonTimestamp=lastLogonTimestamp,
                                   userAccountControl=
@@ -683,8 +220,8 @@ userPassword: thatsAcomplPASS2
 
         # Wrong old password
         try:
-            self.ldb3.modify_ldif("""
-dn: cn=testuser,cn=users,""" + self.base_dn + """
+            other_ldb.modify_ldif("""
+dn: """ + userdn + """
 changetype: modify
 delete: userPassword
 userPassword: thatsAcomplPASS1x
@@ -693,12 +230,13 @@ userPassword: thatsAcomplPASS2
 """)
             self.fail()
         except LdbError, (num, msg):
-            self.assertTrue('00000056' in msg)
             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
+            self.assertTrue('00000056' in msg, msg)
 
-        res = self._check_account("cn=testuser,cn=users," + self.base_dn,
+        res = self._check_account(userdn,
                                   badPwdCount=3,
                                   badPasswordTime=("greater", badPasswordTime),
+                                  logonCount=logonCount,
                                   lastLogon=lastLogon,
                                   lastLogonTimestamp=lastLogonTimestamp,
                                   lockoutTime=("greater", badPasswordTime),
@@ -710,8 +248,8 @@ userPassword: thatsAcomplPASS2
 
         # Wrong old password
         try:
-            self.ldb3.modify_ldif("""
-dn: cn=testuser,cn=users,""" + self.base_dn + """
+            other_ldb.modify_ldif("""
+dn: """ + userdn + """
 changetype: modify
 delete: userPassword
 userPassword: thatsAcomplPASS1x
@@ -721,11 +259,12 @@ userPassword: thatsAcomplPASS2
             self.fail()
         except LdbError, (num, msg):
             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
-            self.assertTrue('00000775' in msg)
+            self.assertTrue('00000775' in msg, msg)
 
-        res = self._check_account("cn=testuser,cn=users," + self.base_dn,
+        res = self._check_account(userdn,
                                   badPwdCount=3,
                                   badPasswordTime=badPasswordTime,
+                                  logonCount=logonCount,
                                   lastLogon=lastLogon,
                                   lastLogonTimestamp=lastLogonTimestamp,
                                   lockoutTime=lockoutTime,
@@ -735,8 +274,8 @@ userPassword: thatsAcomplPASS2
 
         # Wrong old password
         try:
-            self.ldb3.modify_ldif("""
-dn: cn=testuser,cn=users,""" + self.base_dn + """
+            other_ldb.modify_ldif("""
+dn: """ + userdn + """
 changetype: modify
 delete: userPassword
 userPassword: thatsAcomplPASS1x
@@ -746,11 +285,12 @@ userPassword: thatsAcomplPASS2
             self.fail()
         except LdbError, (num, msg):
             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
-            self.assertTrue('00000775' in msg)
+            self.assertTrue('00000775' in msg, msg)
 
-        res = self._check_account("cn=testuser,cn=users," + self.base_dn,
+        res = self._check_account(userdn,
                                   badPwdCount=3,
                                   badPasswordTime=badPasswordTime,
+                                  logonCount=logonCount,
                                   lockoutTime=lockoutTime,
                                   lastLogon=lastLogon,
                                   lastLogonTimestamp=lastLogonTimestamp,
@@ -760,8 +300,8 @@ userPassword: thatsAcomplPASS2
 
         try:
             # Correct old password
-            self.ldb3.modify_ldif("""
-dn: cn=testuser,cn=users,""" + self.base_dn + """
+            other_ldb.modify_ldif("""
+dn: """ + userdn + """
 changetype: modify
 delete: userPassword
 userPassword: thatsAcomplPASS2
@@ -771,11 +311,12 @@ userPassword: thatsAcomplPASS2x
             self.fail()
         except LdbError, (num, msg):
             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
-            self.assertTrue('0000775' in msg)
+            self.assertTrue('00000775' in msg, msg)
 
-        res = self._check_account("cn=testuser,cn=users," + self.base_dn,
+        res = self._check_account(userdn,
                                   badPwdCount=3,
                                   badPasswordTime=badPasswordTime,
+                                  logonCount=logonCount,
                                   lastLogon=lastLogon,
                                   lastLogonTimestamp=lastLogonTimestamp,
                                   lockoutTime=lockoutTime,
@@ -785,15 +326,16 @@ userPassword: thatsAcomplPASS2x
 
         # Now reset the password, which does NOT change the lockout!
         self.ldb.modify_ldif("""
-dn: cn=testuser,cn=users,""" + self.base_dn + """
+dn: """ + userdn + """
 changetype: modify
 replace: userPassword
 userPassword: thatsAcomplPASS2
 """)
 
-        res = self._check_account("cn=testuser,cn=users," + self.base_dn,
+        res = self._check_account(userdn,
                                   badPwdCount=3,
                                   badPasswordTime=badPasswordTime,
+                                  logonCount=logonCount,
                                   lastLogon=lastLogon,
                                   lastLogonTimestamp=lastLogonTimestamp,
                                   lockoutTime=lockoutTime,
@@ -803,8 +345,8 @@ userPassword: thatsAcomplPASS2
 
         try:
             # Correct old password
-            self.ldb3.modify_ldif("""
-dn: cn=testuser,cn=users,""" + self.base_dn + """
+            other_ldb.modify_ldif("""
+dn: """ + userdn + """
 changetype: modify
 delete: userPassword
 userPassword: thatsAcomplPASS2
@@ -814,11 +356,12 @@ userPassword: thatsAcomplPASS2x
             self.fail()
         except LdbError, (num, msg):
             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
-            self.assertTrue('0000775' in msg)
+            self.assertTrue('00000775' in msg, msg)
 
-        res = self._check_account("cn=testuser,cn=users," + self.base_dn,
+        res = self._check_account(userdn,
                                   badPwdCount=3,
                                   badPasswordTime=badPasswordTime,
+                                  logonCount=logonCount,
                                   lastLogon=lastLogon,
                                   lastLogonTimestamp=lastLogonTimestamp,
                                   lockoutTime=lockoutTime,
@@ -827,7 +370,7 @@ userPassword: thatsAcomplPASS2x
                                   msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
 
         m = Message()
-        m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn)
+        m.dn = Dn(self.ldb, userdn)
         m["userAccountControl"] = MessageElement(
           str(dsdb.UF_LOCKOUT),
           FLAG_MOD_REPLACE, "userAccountControl")
@@ -835,9 +378,10 @@ userPassword: thatsAcomplPASS2x
         self.ldb.modify(m)
 
         # This shows that setting the UF_LOCKOUT flag alone makes no difference
-        res = self._check_account("cn=testuser,cn=users," + self.base_dn,
+        res = self._check_account(userdn,
                                   badPwdCount=3,
                                   badPasswordTime=badPasswordTime,
+                                  logonCount=logonCount,
                                   lastLogon=lastLogon,
                                   lastLogonTimestamp=lastLogonTimestamp,
                                   lockoutTime=lockoutTime,
@@ -848,8 +392,8 @@ userPassword: thatsAcomplPASS2x
         # This shows that setting the UF_LOCKOUT flag makes no difference
         try:
             # Correct old password
-            self.ldb3.modify_ldif("""
-dn: cn=testuser,cn=users,""" + self.base_dn + """
+            other_ldb.modify_ldif("""
+dn: """ + userdn + """
 changetype: modify
 delete: unicodePwd
 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
@@ -859,11 +403,12 @@ unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2x\"".encode('utf-16-le'))
             self.fail()
         except LdbError, (num, msg):
             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
-            self.assertTrue('0000775' in msg)
+            self.assertTrue('00000775' in msg, msg)
 
-        res = self._check_account("cn=testuser,cn=users," + self.base_dn,
+        res = self._check_account(userdn,
                                   badPwdCount=3,
                                   badPasswordTime=badPasswordTime,
+                                  logonCount=logonCount,
                                   lockoutTime=lockoutTime,
                                   lastLogon=lastLogon,
                                   lastLogonTimestamp=lastLogonTimestamp,
@@ -874,9 +419,10 @@ unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2x\"".encode('utf-16-le'))
         self._reset_by_method(res, method)
 
         # Here bad password counts are reset without logon success.
-        res = self._check_account("cn=testuser,cn=users," + self.base_dn,
+        res = self._check_account(userdn,
                                   badPwdCount=0,
                                   badPasswordTime=badPasswordTime,
+                                  logonCount=logonCount,
                                   lockoutTime=0,
                                   lastLogon=lastLogon,
                                   lastLogonTimestamp=lastLogonTimestamp,
@@ -886,18 +432,21 @@ unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2x\"".encode('utf-16-le'))
 
         # The correct password after doing the unlock
 
-        self.ldb3.modify_ldif("""
-dn: cn=testuser,cn=users,""" + self.base_dn + """
+        other_ldb.modify_ldif("""
+dn: """ + userdn + """
 changetype: modify
 delete: unicodePwd
 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
 add: unicodePwd
 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2x\"".encode('utf-16-le')) + """
 """)
+        userpass = "thatsAcomplPASS2x"
+        creds.set_password(userpass)
 
-        res = self._check_account("cn=testuser,cn=users," + self.base_dn,
+        res = self._check_account(userdn,
                                   badPwdCount=0,
                                   badPasswordTime=badPasswordTime,
+                                  logonCount=logonCount,
                                   lockoutTime=0,
                                   lastLogon=lastLogon,
                                   lastLogonTimestamp=lastLogonTimestamp,
@@ -907,8 +456,8 @@ unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2x\"".encode('utf-16-le'))
 
         # Wrong old password
         try:
-            self.ldb3.modify_ldif("""
-dn: cn=testuser,cn=users,""" + self.base_dn + """
+            other_ldb.modify_ldif("""
+dn: """ + userdn + """
 changetype: modify
 delete: userPassword
 userPassword: thatsAcomplPASS1xyz
@@ -918,11 +467,12 @@ userPassword: thatsAcomplPASS2XYZ
             self.fail()
         except LdbError, (num, msg):
             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
-            self.assertTrue('00000056' in msg)
+            self.assertTrue('00000056' in msg, msg)
 
-        res = self._check_account("cn=testuser,cn=users," + self.base_dn,
+        res = self._check_account(userdn,
                                   badPwdCount=1,
                                   badPasswordTime=("greater", badPasswordTime),
+                                  logonCount=logonCount,
                                   lockoutTime=0,
                                   lastLogon=lastLogon,
                                   lastLogonTimestamp=lastLogonTimestamp,
@@ -933,8 +483,8 @@ userPassword: thatsAcomplPASS2XYZ
 
         # Wrong old password
         try:
-            self.ldb3.modify_ldif("""
-dn: cn=testuser,cn=users,""" + self.base_dn + """
+            other_ldb.modify_ldif("""
+dn: """ + userdn + """
 changetype: modify
 delete: userPassword
 userPassword: thatsAcomplPASS1xyz
@@ -944,11 +494,12 @@ userPassword: thatsAcomplPASS2XYZ
             self.fail()
         except LdbError, (num, msg):
             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
-            self.assertTrue('00000056' in msg)
+            self.assertTrue('00000056' in msg, msg)
 
-        res = self._check_account("cn=testuser,cn=users," + self.base_dn,
+        res = self._check_account(userdn,
                                   badPwdCount=2,
                                   badPasswordTime=("greater", badPasswordTime),
+                                  logonCount=logonCount,
                                   lockoutTime=0,
                                   lastLogon=lastLogon,
                                   lastLogonTimestamp=lastLogonTimestamp,
@@ -959,9 +510,10 @@ userPassword: thatsAcomplPASS2XYZ
 
         self._reset_ldap_lockoutTime(res)
 
-        res = self._check_account("cn=testuser,cn=users," + self.base_dn,
+        res = self._check_account(userdn,
                                   badPwdCount=0,
                                   badPasswordTime=badPasswordTime,
+                                  logonCount=logonCount,
                                   lastLogon=lastLogon,
                                   lastLogonTimestamp=lastLogonTimestamp,
                                   lockoutTime=0,
@@ -969,36 +521,72 @@ userPassword: thatsAcomplPASS2XYZ
                                     dsdb.UF_NORMAL_ACCOUNT,
                                   msDSUserAccountControlComputed=0)
 
-    def test_userPassword_lockout_with_clear_change_ldap_userAccountControl(self):
-        self._test_userPassword_lockout_with_clear_change("ldap_userAccountControl")
+    def test_userPassword_lockout_with_clear_change_krb5_ldap_userAccountControl(self):
+        self._test_userPassword_lockout_with_clear_change(self.lockout1krb5_creds,
+                                                          self.lockout2krb5_ldb,
+                                                          "ldap_userAccountControl")
+
+    def test_userPassword_lockout_with_clear_change_krb5_ldap_lockoutTime(self):
+        self._test_userPassword_lockout_with_clear_change(self.lockout1krb5_creds,
+                                                          self.lockout2krb5_ldb,
+                                                          "ldap_lockoutTime")
+
+    def test_userPassword_lockout_with_clear_change_krb5_samr(self):
+        self._test_userPassword_lockout_with_clear_change(self.lockout1krb5_creds,
+                                                          self.lockout2krb5_ldb,
+                                                          "samr")
 
-    def test_userPassword_lockout_with_clear_change_ldap_lockoutTime(self):
-        self._test_userPassword_lockout_with_clear_change("ldap_lockoutTime")
+    def test_userPassword_lockout_with_clear_change_ntlm_ldap_userAccountControl(self):
+        self._test_userPassword_lockout_with_clear_change(self.lockout1ntlm_creds,
+                                                          self.lockout2ntlm_ldb,
+                                                          "ldap_userAccountControl",
+                                                          initial_lastlogon_relation='greater')
 
-    def test_userPassword_lockout_with_clear_change_samr(self):
-        self._test_userPassword_lockout_with_clear_change("samr")
+    def test_userPassword_lockout_with_clear_change_ntlm_ldap_lockoutTime(self):
+        self._test_userPassword_lockout_with_clear_change(self.lockout1ntlm_creds,
+                                                          self.lockout2ntlm_ldb,
+                                                          "ldap_lockoutTime",
+                                                          initial_lastlogon_relation='greater')
 
+    def test_userPassword_lockout_with_clear_change_ntlm_samr(self):
+        self._test_userPassword_lockout_with_clear_change(self.lockout1ntlm_creds,
+                                                          self.lockout2ntlm_ldb,
+                                                          "samr",
+                                                          initial_lastlogon_relation='greater')
 
-    def test_unicodePwd_lockout_with_clear_change(self):
+    def _test_unicodePwd_lockout_with_clear_change(self, creds, other_ldb,
+                                                   initial_logoncount_relation=None):
         print "Performs a password cleartext change operation on 'unicodePwd'"
+        username = creds.get_username()
+        userpass = creds.get_password()
+        userdn = "cn=%s,cn=users,%s" % (username, self.base_dn)
+        if initial_logoncount_relation is not None:
+            logoncount_relation = initial_logoncount_relation
+        else:
+            logoncount_relation = "greater"
 
-        res = self._check_account("cn=testuser,cn=users," + self.base_dn,
+        res = self._check_account(userdn,
                                   badPwdCount=0,
                                   badPasswordTime=("greater", 0),
+                                  logonCount=(logoncount_relation, 0),
                                   lastLogon=("greater", 0),
                                   lastLogonTimestamp=("greater", 0),
                                   userAccountControl=
                                     dsdb.UF_NORMAL_ACCOUNT,
                                   msDSUserAccountControlComputed=0)
         badPasswordTime = int(res[0]["badPasswordTime"][0])
+        logonCount = int(res[0]["logonCount"][0])
         lastLogon = int(res[0]["lastLogon"][0])
+        lastLogonTimestamp = int(res[0]["lastLogonTimestamp"][0])
+        self.assertGreater(lastLogonTimestamp, badPasswordTime)
+        self.assertGreaterEqual(lastLogon, lastLogonTimestamp)
 
         # Change password on a connection as another user
 
         # Wrong old password
         try:
-            self.ldb3.modify_ldif("""
-dn: cn=testuser,cn=users,""" + self.base_dn + """
+            other_ldb.modify_ldif("""
+dn: """ + userdn + """
 changetype: modify
 delete: unicodePwd
 unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS1x\"".encode('utf-16-le')) + """
@@ -1007,58 +595,67 @@ unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le'))
 """)
             self.fail()
         except LdbError, (num, msg):
-            self.assertTrue('00000056' in msg)
             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
+            self.assertTrue('00000056' in msg, msg)
 
-        res = self._check_account("cn=testuser,cn=users," + self.base_dn,
+        res = self._check_account(userdn,
                                   badPwdCount=1,
                                   badPasswordTime=("greater", badPasswordTime),
+                                  logonCount=logonCount,
                                   lastLogon=lastLogon,
-                                  lastLogonTimestamp=lastLogon,
+                                  lastLogonTimestamp=lastLogonTimestamp,
                                   userAccountControl=
                                     dsdb.UF_NORMAL_ACCOUNT,
                                   msDSUserAccountControlComputed=0)
         badPasswordTime = int(res[0]["badPasswordTime"][0])
 
         # Correct old password
-        self.ldb3.modify_ldif("""
-dn: cn=testuser,cn=users,""" + self.base_dn + """
+        old_utf16 = ("\"%s\"" % userpass).encode('utf-16-le')
+        invalid_utf16 = "\"thatsAcomplPASSX\"".encode('utf-16-le')
+        userpass = "thatsAcomplPASS2"
+        creds.set_password(userpass)
+        new_utf16 = ("\"%s\"" % userpass).encode('utf-16-le')
+
+        other_ldb.modify_ldif("""
+dn: """ + userdn + """
 changetype: modify
 delete: unicodePwd
-unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS1\"".encode('utf-16-le')) + """
+unicodePwd:: """ + base64.b64encode(old_utf16) + """
 add: unicodePwd
-unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
+unicodePwd:: """ + base64.b64encode(new_utf16) + """
 """)
 
-        res = self._check_account("cn=testuser,cn=users," + self.base_dn,
+        res = self._check_account(userdn,
                                   badPwdCount=1,
                                   badPasswordTime=badPasswordTime,
+                                  logonCount=logonCount,
                                   lastLogon=lastLogon,
-                                  lastLogonTimestamp=lastLogon,
+                                  lastLogonTimestamp=lastLogonTimestamp,
                                   userAccountControl=
                                     dsdb.UF_NORMAL_ACCOUNT,
                                   msDSUserAccountControlComputed=0)
 
         # Wrong old password
         try:
-            self.ldb3.modify_ldif("""
-dn: cn=testuser,cn=users,""" + self.base_dn + """
+            other_ldb.modify_ldif("""
+dn: """ + userdn + """
 changetype: modify
 delete: unicodePwd
-unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS1\"".encode('utf-16-le')) + """
+unicodePwd:: """ + base64.b64encode(old_utf16) + """
 add: unicodePwd
-unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
+unicodePwd:: """ + base64.b64encode(new_utf16) + """
 """)
             self.fail()
         except LdbError, (num, msg):
-            self.assertTrue('00000056' in msg)
             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
+            self.assertTrue('00000056' in msg, msg)
 
-        res = self._check_account("cn=testuser,cn=users," + self.base_dn,
+        res = self._check_account(userdn,
                                   badPwdCount=2,
                                   badPasswordTime=("greater", badPasswordTime),
+                                  logonCount=logonCount,
                                   lastLogon=lastLogon,
-                                  lastLogonTimestamp=lastLogon,
+                                  lastLogonTimestamp=lastLogonTimestamp,
                                   userAccountControl=
                                     dsdb.UF_NORMAL_ACCOUNT,
                                   msDSUserAccountControlComputed=0)
@@ -1069,11 +666,12 @@ unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le'))
         # reset "badPwdCount" = 0.
         self._reset_samr(res)
 
-        res = self._check_account("cn=testuser,cn=users," + self.base_dn,
+        res = self._check_account(userdn,
                                   badPwdCount=2,
                                   badPasswordTime=badPasswordTime,
+                                  logonCount=logonCount,
                                   lastLogon=lastLogon,
-                                  lastLogonTimestamp=lastLogon,
+                                  lastLogonTimestamp=lastLogonTimestamp,
                                   userAccountControl=
                                     dsdb.UF_NORMAL_ACCOUNT,
                                   msDSUserAccountControlComputed=0)
@@ -1082,25 +680,26 @@ unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le'))
 
         # Wrong old password
         try:
-            self.ldb3.modify_ldif("""
-dn: cn=testuser,cn=users,""" + self.base_dn + """
+            other_ldb.modify_ldif("""
+dn: """ + userdn + """
 changetype: modify
 delete: unicodePwd
-unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS1x\"".encode('utf-16-le')) + """
+unicodePwd:: """ + base64.b64encode(invalid_utf16) + """
 add: unicodePwd
-unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
+unicodePwd:: """ + base64.b64encode(new_utf16) + """
 """)
             self.fail()
         except LdbError, (num, msg):
-            self.assertTrue('00000056' in msg)
             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
+            self.assertTrue('00000056' in msg, msg)
 
         # this is strange, why do we have lockoutTime=badPasswordTime here?
-        res = self._check_account("cn=testuser,cn=users," + self.base_dn,
+        res = self._check_account(userdn,
                                   badPwdCount=3,
                                   badPasswordTime=("greater", badPasswordTime),
+                                  logonCount=logonCount,
                                   lastLogon=lastLogon,
-                                  lastLogonTimestamp=lastLogon,
+                                  lastLogonTimestamp=lastLogonTimestamp,
                                   lockoutTime=("greater", badPasswordTime),
                                   userAccountControl=
                                     dsdb.UF_NORMAL_ACCOUNT,
@@ -1110,24 +709,25 @@ unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le'))
 
         # Wrong old password
         try:
-            self.ldb3.modify_ldif("""
-dn: cn=testuser,cn=users,""" + self.base_dn + """
+            other_ldb.modify_ldif("""
+dn: """ + userdn + """
 changetype: modify
 delete: unicodePwd
-unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS1x\"".encode('utf-16-le')) + """
+unicodePwd:: """ + base64.b64encode(invalid_utf16) + """
 add: unicodePwd
-unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
+unicodePwd:: """ + base64.b64encode(new_utf16) + """
 """)
             self.fail()
         except LdbError, (num, msg):
             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
-            self.assertTrue('00000775' in msg)
+            self.assertTrue('00000775' in msg, msg)
 
-        res = self._check_account("cn=testuser,cn=users," + self.base_dn,
+        res = self._check_account(userdn,
                                   badPwdCount=3,
                                   badPasswordTime=badPasswordTime,
+                                  logonCount=logonCount,
                                   lastLogon=lastLogon,
-                                  lastLogonTimestamp=lastLogon,
+                                  lastLogonTimestamp=lastLogonTimestamp,
                                   lockoutTime=lockoutTime,
                                   userAccountControl=
                                     dsdb.UF_NORMAL_ACCOUNT,
@@ -1135,24 +735,25 @@ unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le'))
 
         # Wrong old password
         try:
-            self.ldb3.modify_ldif("""
-dn: cn=testuser,cn=users,""" + self.base_dn + """
+            other_ldb.modify_ldif("""
+dn: """ + userdn + """
 changetype: modify
 delete: unicodePwd
-unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS1x\"".encode('utf-16-le')) + """
+unicodePwd:: """ + base64.b64encode(invalid_utf16) + """
 add: unicodePwd
-unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
+unicodePwd:: """ + base64.b64encode(new_utf16) + """
 """)
             self.fail()
         except LdbError, (num, msg):
             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
-            self.assertTrue('00000775' in msg)
+            self.assertTrue('00000775' in msg, msg)
 
-        res = self._check_account("cn=testuser,cn=users," + self.base_dn,
+        res = self._check_account(userdn,
                                   badPwdCount=3,
                                   badPasswordTime=badPasswordTime,
+                                  logonCount=logonCount,
                                   lastLogon=lastLogon,
-                                  lastLogonTimestamp=lastLogon,
+                                  lastLogonTimestamp=lastLogonTimestamp,
                                   lockoutTime=lockoutTime,
                                   userAccountControl=
                                     dsdb.UF_NORMAL_ACCOUNT,
@@ -1160,24 +761,25 @@ unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le'))
 
         try:
             # Correct old password
-            self.ldb3.modify_ldif("""
-dn: cn=testuser,cn=users,""" + self.base_dn + """
+            other_ldb.modify_ldif("""
+dn: """ + userdn + """
 changetype: modify
 delete: unicodePwd
-unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
+unicodePwd:: """ + base64.b64encode(new_utf16) + """
 add: unicodePwd
-unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2x\"".encode('utf-16-le')) + """
+unicodePwd:: """ + base64.b64encode(invalid_utf16) + """
 """)
             self.fail()
         except LdbError, (num, msg):
             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
-            self.assertTrue('0000775' in msg)
+            self.assertTrue('00000775' in msg, msg)
 
-        res = self._check_account("cn=testuser,cn=users," + self.base_dn,
+        res = self._check_account(userdn,
                                   badPwdCount=3,
                                   badPasswordTime=badPasswordTime,
+                                  logonCount=logonCount,
                                   lastLogon=lastLogon,
-                                  lastLogonTimestamp=lastLogon,
+                                  lastLogonTimestamp=lastLogonTimestamp,
                                   lockoutTime=lockoutTime,
                                   userAccountControl=
                                     dsdb.UF_NORMAL_ACCOUNT,
@@ -1186,31 +788,39 @@ unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2x\"".encode('utf-16-le'))
         # Now reset the lockout, by removing ACB_AUTOLOCK (which removes the lock, despite being a generated attribute)
         self._reset_samr(res);
 
-        res = self._check_account("cn=testuser,cn=users," + self.base_dn,
+        res = self._check_account(userdn,
                                   badPwdCount=0,
                                   badPasswordTime=badPasswordTime,
+                                  logonCount=logonCount,
                                   lastLogon=lastLogon,
-                                  lastLogonTimestamp=lastLogon,
+                                  lastLogonTimestamp=lastLogonTimestamp,
                                   lockoutTime=0,
                                   userAccountControl=
                                     dsdb.UF_NORMAL_ACCOUNT,
                                   msDSUserAccountControlComputed=0)
 
         # Correct old password
-        self.ldb3.modify_ldif("""
-dn: cn=testuser,cn=users,""" + self.base_dn + """
+        old_utf16 = ("\"%s\"" % userpass).encode('utf-16-le')
+        invalid_utf16 = "\"thatsAcomplPASSiX\"".encode('utf-16-le')
+        userpass = "thatsAcomplPASS2x"
+        creds.set_password(userpass)
+        new_utf16 = ("\"%s\"" % userpass).encode('utf-16-le')
+
+        other_ldb.modify_ldif("""
+dn: """ + userdn + """
 changetype: modify
 delete: unicodePwd
-unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
+unicodePwd:: """ + base64.b64encode(old_utf16) + """
 add: unicodePwd
-unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2x\"".encode('utf-16-le')) + """
+unicodePwd:: """ + base64.b64encode(new_utf16) + """
 """)
 
-        res = self._check_account("cn=testuser,cn=users," + self.base_dn,
+        res = self._check_account(userdn,
                                   badPwdCount=0,
                                   badPasswordTime=badPasswordTime,
+                                  logonCount=logonCount,
                                   lastLogon=lastLogon,
-                                  lastLogonTimestamp=lastLogon,
+                                  lastLogonTimestamp=lastLogonTimestamp,
                                   lockoutTime=0,
                                   userAccountControl=
                                     dsdb.UF_NORMAL_ACCOUNT,
@@ -1218,24 +828,25 @@ unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2x\"".encode('utf-16-le'))
 
         # Wrong old password
         try:
-            self.ldb3.modify_ldif("""
-dn: cn=testuser,cn=users,""" + self.base_dn + """
+            other_ldb.modify_ldif("""
+dn: """ + userdn + """
 changetype: modify
 delete: unicodePwd
-unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS1x\"".encode('utf-16-le')) + """
+unicodePwd:: """ + base64.b64encode(invalid_utf16) + """
 add: unicodePwd
-unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
+unicodePwd:: """ + base64.b64encode(new_utf16) + """
 """)
             self.fail()
         except LdbError, (num, msg):
-            self.assertTrue('00000056' in msg)
             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
+            self.assertTrue('00000056' in msg, msg)
 
-        res = self._check_account("cn=testuser,cn=users," + self.base_dn,
+        res = self._check_account(userdn,
                                   badPwdCount=1,
                                   badPasswordTime=("greater", badPasswordTime),
+                                  logonCount=logonCount,
                                   lastLogon=lastLogon,
-                                  lastLogonTimestamp=lastLogon,
+                                  lastLogonTimestamp=lastLogonTimestamp,
                                   lockoutTime=0,
                                   userAccountControl=
                                     dsdb.UF_NORMAL_ACCOUNT,
@@ -1244,24 +855,25 @@ unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le'))
 
         # Wrong old password
         try:
-            self.ldb3.modify_ldif("""
-dn: cn=testuser,cn=users,""" + self.base_dn + """
+            other_ldb.modify_ldif("""
+dn: """ + userdn + """
 changetype: modify
 delete: unicodePwd
-unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS1x\"".encode('utf-16-le')) + """
+unicodePwd:: """ + base64.b64encode(invalid_utf16) + """
 add: unicodePwd
-unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
+unicodePwd:: """ + base64.b64encode(new_utf16) + """
 """)
             self.fail()
         except LdbError, (num, msg):
-            self.assertTrue('00000056' in msg)
             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
+            self.assertTrue('00000056' in msg, msg)
 
-        res = self._check_account("cn=testuser,cn=users," + self.base_dn,
+        res = self._check_account(userdn,
                                   badPwdCount=2,
                                   badPasswordTime=("greater", badPasswordTime),
+                                  logonCount=logonCount,
                                   lastLogon=lastLogon,
-                                  lastLogonTimestamp=lastLogon,
+                                  lastLogonTimestamp=lastLogonTimestamp,
                                   lockoutTime=0,
                                   userAccountControl=
                                     dsdb.UF_NORMAL_ACCOUNT,
@@ -1272,11 +884,12 @@ unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le'))
         # It doesn't reset "badPwdCount" = 0.
         self._reset_samr(res)
 
-        res = self._check_account("cn=testuser,cn=users," + self.base_dn,
+        res = self._check_account(userdn,
                                   badPwdCount=2,
                                   badPasswordTime=badPasswordTime,
+                                  logonCount=logonCount,
                                   lastLogon=lastLogon,
-                                  lastLogonTimestamp=lastLogon,
+                                  lastLogonTimestamp=lastLogonTimestamp,
                                   lockoutTime=0,
                                   userAccountControl=
                                     dsdb.UF_NORMAL_ACCOUNT,
@@ -1284,24 +897,25 @@ unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le'))
 
         # Wrong old password
         try:
-            self.ldb3.modify_ldif("""
-dn: cn=testuser,cn=users,""" + self.base_dn + """
+            other_ldb.modify_ldif("""
+dn: """ + userdn + """
 changetype: modify
 delete: unicodePwd
-unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS1x\"".encode('utf-16-le')) + """
+unicodePwd:: """ + base64.b64encode(invalid_utf16) + """
 add: unicodePwd
-unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
+unicodePwd:: """ + base64.b64encode(new_utf16) + """
 """)
             self.fail()
         except LdbError, (num, msg):
-            self.assertTrue('00000056' in msg)
             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
+            self.assertTrue('00000056' in msg, msg)
 
-        res = self._check_account("cn=testuser,cn=users," + self.base_dn,
+        res = self._check_account(userdn,
                                   badPwdCount=3,
                                   badPasswordTime=("greater", badPasswordTime),
+                                  logonCount=logonCount,
                                   lastLogon=lastLogon,
-                                  lastLogonTimestamp=lastLogon,
+                                  lastLogonTimestamp=lastLogonTimestamp,
                                   lockoutTime=("greater", badPasswordTime),
                                   userAccountControl=
                                     dsdb.UF_NORMAL_ACCOUNT,
@@ -1311,11 +925,12 @@ unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le'))
 
         time.sleep(self.account_lockout_duration + 1)
 
-        res = self._check_account("cn=testuser,cn=users," + self.base_dn,
+        res = self._check_account(userdn,
                                   badPwdCount=3, effective_bad_password_count=0,
                                   badPasswordTime=badPasswordTime,
+                                  logonCount=logonCount,
                                   lastLogon=lastLogon,
-                                  lastLogonTimestamp=lastLogon,
+                                  lastLogonTimestamp=lastLogonTimestamp,
                                   lockoutTime=lockoutTime,
                                   userAccountControl=
                                     dsdb.UF_NORMAL_ACCOUNT,
@@ -1326,206 +941,10 @@ unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le'))
         # reset "badPwdCount" = 0.
         self._reset_samr(res)
 
-        res = self._check_account("cn=testuser,cn=users," + self.base_dn,
-                                  badPwdCount=3, effective_bad_password_count=0,
-                                  badPasswordTime=badPasswordTime,
-                                  lockoutTime=lockoutTime,
-                                  lastLogon=lastLogon,
-                                  lastLogonTimestamp=lastLogon,
-                                  userAccountControl=
-                                    dsdb.UF_NORMAL_ACCOUNT,
-                                  msDSUserAccountControlComputed=0)
-
-    def _test_login_lockout(self, use_kerberos):
-        # This unlocks by waiting for account_lockout_duration
-        if use_kerberos == MUST_USE_KERBEROS:
-            lastlogon_relation = 'greater'
-            print "Performs a lockout attempt against LDAP using Kerberos"
-        else:
-            lastlogon_relation = 'equal'
-            print "Performs a lockout attempt against LDAP using NTLM"
-
-        # Change password on a connection as another user
-        res = self._check_account("cn=testuser,cn=users," + self.base_dn,
-                                  badPwdCount=0,
-                                  badPasswordTime=("greater", 0),
-                                  lastLogon=("greater", 0),
-                                  lastLogonTimestamp=("greater", 0),
-                                  userAccountControl=
-                                    dsdb.UF_NORMAL_ACCOUNT,
-                                  msDSUserAccountControlComputed=0)
-        badPasswordTime = int(res[0]["badPasswordTime"][0])
-        lastLogon = int(res[0]["lastLogon"][0])
-        firstLogon = lastLogon
-        lastLogonTimestamp = int(res[0]["lastLogonTimestamp"][0])
-        print firstLogon
-        print lastLogonTimestamp
-
-
-        self.assertGreater(lastLogon, badPasswordTime)
-
-        # Open a second LDB connection with the user credentials. Use the
-        # command line credentials for informations like the domain, the realm
-        # and the workstation.
-        creds_lockout = insta_creds()
-        creds_lockout.set_kerberos_state(use_kerberos)
-
-        # The wrong password
-        creds_lockout.set_password("thatsAcomplPASS1x")
-
-        self.assertLoginFailure(host_url, creds_lockout, lp)
-
-        res = self._check_account("cn=testuser,cn=users," + self.base_dn,
-                                  badPwdCount=1,
-                                  badPasswordTime=("greater", badPasswordTime),
-                                  lastLogon=lastLogon,
-                                  lastLogonTimestamp=lastLogonTimestamp,
-                                  userAccountControl=
-                                    dsdb.UF_NORMAL_ACCOUNT,
-                                  msDSUserAccountControlComputed=0,
-                                  msg='lastlogontimestamp with wrong password')
-        badPasswordTime = int(res[0]["badPasswordTime"][0])
-
-        # Correct old password
-        creds_lockout.set_password("thatsAcomplPASS1")
-
-        ldb_lockout = SamDB(url=host_url, credentials=creds_lockout, lp=lp)
-
-        # lastLogonTimestamp should not change
-        # lastLogon increases if badPwdCount is non-zero (!)
-        res = self._check_account("cn=testuser,cn=users," + self.base_dn,
-                                  badPwdCount=0,
-                                  badPasswordTime=badPasswordTime,
-                                  lastLogon=('greater', lastLogon),
-                                  lastLogonTimestamp=lastLogonTimestamp,
-                                  userAccountControl=
-                                    dsdb.UF_NORMAL_ACCOUNT,
-                                  msDSUserAccountControlComputed=0,
-                                  msg='LLTimestamp is updated to lastlogon')
-
-        lastLogon = int(res[0]["lastLogon"][0])
-        self.assertGreater(lastLogon, badPasswordTime)
-
-        # The wrong password
-        creds_lockout.set_password("thatsAcomplPASS1x")
-
-        self.assertLoginFailure(host_url, creds_lockout, lp)
-
-        res = self._check_account("cn=testuser,cn=users," + self.base_dn,
-                                  badPwdCount=1,
-                                  badPasswordTime=("greater", badPasswordTime),
-                                  lastLogon=lastLogon,
-                                  lastLogonTimestamp=lastLogonTimestamp,
-                                  userAccountControl=
-                                    dsdb.UF_NORMAL_ACCOUNT,
-                                  msDSUserAccountControlComputed=0)
-        badPasswordTime = int(res[0]["badPasswordTime"][0])
-
-        # The wrong password
-        creds_lockout.set_password("thatsAcomplPASS1x")
-
-        try:
-            ldb_lockout = SamDB(url=host_url, credentials=creds_lockout, lp=lp)
-            self.fail()
-
-        except LdbError, (num, msg):
-            self.assertEquals(num, ERR_INVALID_CREDENTIALS)
-
-        res = self._check_account("cn=testuser,cn=users," + self.base_dn,
-                                  badPwdCount=2,
-                                  badPasswordTime=("greater", badPasswordTime),
-                                  lastLogon=lastLogon,
-                                  lastLogonTimestamp=lastLogonTimestamp,
-                                  userAccountControl=
-                                    dsdb.UF_NORMAL_ACCOUNT,
-                                  msDSUserAccountControlComputed=0)
-        badPasswordTime = int(res[0]["badPasswordTime"][0])
-
-        print "two failed password change"
-
-        # The wrong password
-        creds_lockout.set_password("thatsAcomplPASS1x")
-
-        try:
-            ldb_lockout = SamDB(url=host_url, credentials=creds_lockout, lp=lp)
-            self.fail()
-
-        except LdbError, (num, msg):
-            self.assertEquals(num, ERR_INVALID_CREDENTIALS)
-
-        res = self._check_account("cn=testuser,cn=users," + self.base_dn,
-                                  badPwdCount=3,
-                                  badPasswordTime=("greater", badPasswordTime),
-                                  lastLogon=lastLogon,
-                                  lastLogonTimestamp=lastLogonTimestamp,
-                                  lockoutTime=("greater", badPasswordTime),
-                                  userAccountControl=
-                                    dsdb.UF_NORMAL_ACCOUNT,
-                                  msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
-        badPasswordTime = int(res[0]["badPasswordTime"][0])
-        lockoutTime = int(res[0]["lockoutTime"][0])
-
-        # The wrong password
-        creds_lockout.set_password("thatsAcomplPASS1x")
-        try:
-            ldb_lockout = SamDB(url=host_url, credentials=creds_lockout, lp=lp)
-            self.fail()
-        except LdbError, (num, msg):
-            self.assertEquals(num, ERR_INVALID_CREDENTIALS)
-
-        res = self._check_account("cn=testuser,cn=users," + self.base_dn,
-                                  badPwdCount=3,
-                                  badPasswordTime=badPasswordTime,
-                                  lastLogon=lastLogon,
-                                  lastLogonTimestamp=lastLogonTimestamp,
-                                  lockoutTime=lockoutTime,
-                                  userAccountControl=
-                                    dsdb.UF_NORMAL_ACCOUNT,
-                                  msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
-
-        # The wrong password
-        creds_lockout.set_password("thatsAcomplPASS1x")
-        try:
-            ldb_lockout = SamDB(url=host_url, credentials=creds_lockout, lp=lp)
-            self.fail()
-        except LdbError, (num, msg):
-            self.assertEquals(num, ERR_INVALID_CREDENTIALS)
-
-        res = self._check_account("cn=testuser,cn=users," + self.base_dn,
-                                  badPwdCount=3,
-                                  badPasswordTime=badPasswordTime,
-                                  lastLogon=lastLogon,
-                                  lastLogonTimestamp=lastLogonTimestamp,
-                                  lockoutTime=lockoutTime,
-                                  userAccountControl=
-                                    dsdb.UF_NORMAL_ACCOUNT,
-                                  msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
-
-        # The correct password, but we are locked out
-        creds_lockout.set_password("thatsAcomplPASS1")
-        try:
-            ldb_lockout = SamDB(url=host_url, credentials=creds_lockout, lp=lp)
-            self.fail()
-        except LdbError, (num, msg):
-            self.assertEquals(num, ERR_INVALID_CREDENTIALS)
-
-        res = self._check_account("cn=testuser,cn=users," + self.base_dn,
-                                  badPwdCount=3,
-                                  badPasswordTime=badPasswordTime,
-                                  lastLogon=lastLogon,
-                                  lastLogonTimestamp=lastLogonTimestamp,
-                                  lockoutTime=lockoutTime,
-                                  userAccountControl=
-                                    dsdb.UF_NORMAL_ACCOUNT,
-                                  msDSUserAccountControlComputed=dsdb.UF_LOCKOUT)
-
-        # wait for the lockout to end
-        time.sleep(self.account_lockout_duration + 1)
-        print self.account_lockout_duration + 1
-
-        res = self._check_account("cn=testuser,cn=users," + self.base_dn,
+        res = self._check_account(userdn,
                                   badPwdCount=3, effective_bad_password_count=0,
                                   badPasswordTime=badPasswordTime,
+                                  logonCount=logonCount,
                                   lockoutTime=lockoutTime,
                                   lastLogon=lastLogon,
                                   lastLogonTimestamp=lastLogonTimestamp,
@@ -1533,199 +952,52 @@ unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le'))
                                     dsdb.UF_NORMAL_ACCOUNT,
                                   msDSUserAccountControlComputed=0)
 
-        lastLogon = int(res[0]["lastLogon"][0])
-
-        # The correct password after letting the timeout expire
-
-        creds_lockout.set_password("thatsAcomplPASS1")
+    def test_unicodePwd_lockout_with_clear_change_krb5(self):
+        self._test_unicodePwd_lockout_with_clear_change(self.lockout1krb5_creds,
+                                                        self.lockout2krb5_ldb)
 
-        creds_lockout2 = insta_creds(creds_lockout)
+    def test_unicodePwd_lockout_with_clear_change_ntlm(self):
+        self._test_unicodePwd_lockout_with_clear_change(self.lockout1ntlm_creds,
+                                                        self.lockout2ntlm_ldb,
+                                                        initial_logoncount_relation="equal")
 
-        ldb_lockout = SamDB(url=host_url, credentials=creds_lockout2, lp=lp)
-        time.sleep(3)
-
-        res = self._check_account("cn=testuser,cn=users," + self.base_dn,
-                                  badPwdCount=0,
-                                  badPasswordTime=badPasswordTime,
-                                  lastLogon=(lastlogon_relation, lastLogon),
-                                  lastLogonTimestamp=lastLogonTimestamp,
-                                  lockoutTime=0,
-                                  userAccountControl=
-                                    dsdb.UF_NORMAL_ACCOUNT,
-                                  msDSUserAccountControlComputed=0,
-                                  msg="lastLogon is way off")
-
-        lastLogon = int(res[0]["lastLogon"][0])
-
-        # The wrong password
-        creds_lockout.set_password("thatsAcomplPASS1x")
-        try:
-            ldb_lockout = SamDB(url=host_url, credentials=creds_lockout, lp=lp)
-            self.fail()
-        except LdbError, (num, msg):
-            self.assertEquals(num, ERR_INVALID_CREDENTIALS)
-
-        res = self._check_account("cn=testuser,cn=users," + self.base_dn,
-                                  badPwdCount=1,
-                                  badPasswordTime=("greater", badPasswordTime),
-                                  lockoutTime=0,
-                                  lastLogon=lastLogon,
-                                  lastLogonTimestamp=lastLogonTimestamp,
-                                  userAccountControl=
-                                    dsdb.UF_NORMAL_ACCOUNT,
-                                  msDSUserAccountControlComputed=0)
-        badPasswordTime = int(res[0]["badPasswordTime"][0])
-
-        # The wrong password
-        creds_lockout.set_password("thatsAcomplPASS1x")
-        try:
-            ldb_lockout = SamDB(url=host_url, credentials=creds_lockout, lp=lp)
-            self.fail()
-        except LdbError, (num, msg):
-            self.assertEquals(num, ERR_INVALID_CREDENTIALS)
-
-        res = self._check_account("cn=testuser,cn=users," + self.base_dn,
-                                  badPwdCount=2,
-                                  badPasswordTime=("greater", badPasswordTime),
-                                  lockoutTime=0,
-                                  lastLogon=lastLogon,
-                                  lastLogonTimestamp=lastLogonTimestamp,
-                                  userAccountControl=
-                                    dsdb.UF_NORMAL_ACCOUNT,
-                                  msDSUserAccountControlComputed=0)
-        badPasswordTime = int(res[0]["badPasswordTime"][0])
-
-        time.sleep(self.lockout_observation_window + 1)
-
-        res = self._check_account("cn=testuser,cn=users," + self.base_dn,
-                                  badPwdCount=2, effective_bad_password_count=0,
-                                  badPasswordTime=badPasswordTime,
-                                  lockoutTime=0,
-                                  lastLogon=lastLogon,
-                                  lastLogonTimestamp=lastLogonTimestamp,
-                                  userAccountControl=
-                                    dsdb.UF_NORMAL_ACCOUNT,
-                                  msDSUserAccountControlComputed=0)
-
-        # The wrong password
-        creds_lockout.set_password("thatsAcomplPASS1x")
-        try:
-            ldb_lockout = SamDB(url=host_url, credentials=creds_lockout, lp=lp)
-            self.fail()
-        except LdbError, (num, msg):
-            self.assertEquals(num, ERR_INVALID_CREDENTIALS)
-
-        res = self._check_account("cn=testuser,cn=users," + self.base_dn,
-                                  badPwdCount=1,
-                                  badPasswordTime=("greater", badPasswordTime),
-                                  lockoutTime=0,
-                                  lastLogon=lastLogon,
-                                  lastLogonTimestamp=lastLogonTimestamp,
-                                  userAccountControl=
-                                    dsdb.UF_NORMAL_ACCOUNT,
-                                  msDSUserAccountControlComputed=0)
-        badPasswordTime = int(res[0]["badPasswordTime"][0])
-
-        # The correct password without letting the timeout expire
-        creds_lockout.set_password("thatsAcomplPASS1")
-        ldb_lockout = SamDB(url=host_url, credentials=creds_lockout, lp=lp)
-
-        res = self._check_account("cn=testuser,cn=users," + self.base_dn,
-                                  badPwdCount=0,
-                                  badPasswordTime=badPasswordTime,
-                                  lockoutTime=0,
-                                  lastLogon=("greater", lastLogon),
-                                  lastLogonTimestamp=lastLogonTimestamp,
-                                  userAccountControl=
-                                    dsdb.UF_NORMAL_ACCOUNT,
-                                  msDSUserAccountControlComputed=0)
+    def test_login_lockout_krb5(self):
+        self._test_login_lockout(self.lockout1krb5_creds)
 
     def test_login_lockout_ntlm(self):
-        self._test_login_lockout(DONT_USE_KERBEROS)
-
-    def test_login_lockout_kerberos(self):
-        self._test_login_lockout(MUST_USE_KERBEROS)
-
-    def _test_multiple_logon(self, use_kerberos):
-        # Test the happy case in which a user logs on correctly, then
-        # logs on correctly again, so that the bad password and
-        # lockout times are both zero the second time. The lastlogon
-        # time should increase.
-
-        # Open a second LDB connection with the user credentials. Use the
-        # command line credentials for informations like the domain, the realm
-        # and the workstation.
-        creds2 = insta_creds()
-        creds2.set_kerberos_state(use_kerberos)
-        self.assertEqual(creds2.get_kerberos_state(), use_kerberos)
-
-        if use_kerberos == MUST_USE_KERBEROS:
-            print "Testing multiple logon with Kerberos"
-            lastlogon_relation = 'greater'
-        else:
-            print "Testing multiple logon with NTLM"
-            lastlogon_relation = 'equal'
-
-        SamDB(url=host_url, credentials=insta_creds(creds2), lp=lp)
-
-        res = self._check_account("cn=testuser,cn=users," + self.base_dn,
-                                  badPwdCount=0,
-                                  badPasswordTime=("greater", 0),
-                                  lastLogon=("greater", 0),
-                                  lastLogonTimestamp=("greater", 0),
-                                  userAccountControl=
-                                    dsdb.UF_NORMAL_ACCOUNT,
-                                  msDSUserAccountControlComputed=0)
-        badPasswordTime = int(res[0]["badPasswordTime"][0])
-        lastLogon = int(res[0]["lastLogon"][0])
-        lastLogonTimestamp = int(res[0]["lastLogonTimestamp"][0])
-        firstLogon = lastLogon
-        print "last logon is %d" % lastLogon
-        self.assertGreater(lastLogon, badPasswordTime)
-
-        time.sleep(1)
-        SamDB(url=host_url, credentials=insta_creds(creds2), lp=lp)
-
-        res = self._check_account("cn=testuser,cn=users," + self.base_dn,
-                                  badPwdCount=0,
-                                  badPasswordTime=badPasswordTime,
-                                  lastLogon=(lastlogon_relation, lastLogon),
-                                  lastLogonTimestamp=lastLogonTimestamp,
-                                  userAccountControl=
-                                  dsdb.UF_NORMAL_ACCOUNT,
-                                  msDSUserAccountControlComputed=0,
-                                  msg=("second logon, firstlogon was %s" %
-                                       firstLogon))
-
-
-        lastLogon = int(res[0]["lastLogon"][0])
-
-        time.sleep(1)
-
-        SamDB(url=host_url, credentials=insta_creds(creds2), lp=lp)
+        self._test_login_lockout(self.lockout1ntlm_creds)
 
-        res = self._check_account("cn=testuser,cn=users," + self.base_dn,
-                                  badPwdCount=0,
-                                  badPasswordTime=badPasswordTime,
-                                  lastLogon=(lastlogon_relation, lastLogon),
-                                  lastLogonTimestamp=lastLogonTimestamp,
-                                  userAccountControl=
-                                    dsdb.UF_NORMAL_ACCOUNT,
-                                  msDSUserAccountControlComputed=0)
+    def test_multiple_logon_krb5(self):
+        self._test_multiple_logon(self.lockout1krb5_creds)
 
     def test_multiple_logon_ntlm(self):
-        self._test_multiple_logon(DONT_USE_KERBEROS)
-
-    def test_multiple_logon_kerberos(self):
-        self._test_multiple_logon(MUST_USE_KERBEROS)
-
-    def tearDown(self):
-        super(PasswordTests, self).tearDown()
-        delete_force(self.ldb, "cn=testuser,cn=users," + self.base_dn)
-        delete_force(self.ldb, "cn=testuser2,cn=users," + self.base_dn)
-        delete_force(self.ldb, "cn=testuser3,cn=users," + self.base_dn)
-        # Close the second LDB connection (with the user credentials)
-        self.ldb2 = None
+        self._test_multiple_logon(self.lockout1ntlm_creds)
+
+    def test_lockout_observation_window(self):
+        lockout3krb5_creds = self.insta_creds(self.template_creds,
+                                              username="lockout3krb5",
+                                              userpass="thatsAcomplPASS0",
+                                              kerberos_state=MUST_USE_KERBEROS)
+        self._testing_add_user(lockout3krb5_creds)
+
+        lockout4krb5_creds = self.insta_creds(self.template_creds,
+                                              username="lockout4krb5",
+                                              userpass="thatsAcomplPASS0",
+                                              kerberos_state=MUST_USE_KERBEROS)
+        self._testing_add_user(lockout4krb5_creds,
+                               lockOutObservationWindow=self.lockout_observation_window)
+
+        lockout3ntlm_creds = self.insta_creds(self.template_creds,
+                                              username="lockout3ntlm",
+                                              userpass="thatsAcomplPASS0",
+                                              kerberos_state=DONT_USE_KERBEROS)
+        self._testing_add_user(lockout3ntlm_creds)
+        lockout4ntlm_creds = self.insta_creds(self.template_creds,
+                                              username="lockout4ntlm",
+                                              userpass="thatsAcomplPASS0",
+                                              kerberos_state=DONT_USE_KERBEROS)
+        self._testing_add_user(lockout4ntlm_creds,
+                               lockOutObservationWindow=self.lockout_observation_window)
 
 host_url = "ldap://%s" % host