d5cb3ede4c26f46147642ad6641f5b54a5f193b8
[samba.git] / source4 / torture / drs / python / repl_move.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 #
4 # Unix SMB/CIFS implementation.
5 # Copyright (C) Kamen Mazdrashki <kamenim@samba.org> 2010
6 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2016
7 #
8 # This program is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 3 of the License, or
11 # (at your option) any later version.
12 #
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the GNU General Public License
19 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 #
21
22 #
23 # Usage:
24 #  export DC1=dc1_dns_name
25 #  export DC2=dc2_dns_name
26 #  export SUBUNITRUN=$samba4srcdir/scripting/bin/subunitrun
27 #  PYTHONPATH="$PYTHONPATH:$samba4srcdir/torture/drs/python" $SUBUNITRUN repl_move -U"$DOMAIN/$DC_USERNAME"%"$DC_PASSWORD"
28 #
29
30 from __future__ import print_function
31 import time
32 import uuid
33 import samba.tests
34
35 from samba.ndr import ndr_unpack
36 from samba.dcerpc import drsblobs
37 from samba.dcerpc import misc
38 from samba.drs_utils import drs_DsBind
39
40 from ldb import (
41     SCOPE_BASE,
42     SCOPE_SUBTREE,
43 )
44
45 import drs_base
46 import ldb
47 from samba.dcerpc.drsuapi import *
48
49
50 class DrsMoveObjectTestCase(drs_base.DrsBaseTestCase):
51
52     def _ds_bind(self, server_name):
53         binding_str = "ncacn_ip_tcp:%s[print,seal]" % server_name
54
55         drs = drsuapi(binding_str, self.get_loadparm(), self.get_credentials())
56         (drs_handle, supported_extensions) = drs_DsBind(drs)
57         return (drs, drs_handle)
58
59     def setUp(self):
60         super(DrsMoveObjectTestCase, self).setUp()
61         # disable automatic replication temporary
62         self._disable_all_repl(self.dnsname_dc1)
63         self._disable_all_repl(self.dnsname_dc2)
64
65         # make sure DCs are synchronized before the test
66         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True)
67         self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True)
68
69         self.top_ou = samba.tests.create_test_ou(self.ldb_dc1,
70                                                  "replica_move")
71
72         self.ou1_dn = ldb.Dn(self.ldb_dc1, "OU=DrsOU1")
73         self.ou1_dn.add_base(self.top_ou)
74         ou1 = {}
75         ou1["dn"] = self.ou1_dn
76         ou1["objectclass"] = "organizationalUnit"
77         ou1["ou"] = self.ou1_dn.get_component_value(0)
78         self.ldb_dc1.add(ou1)
79
80         self.ou2_dn = ldb.Dn(self.ldb_dc1, "OU=DrsOU2")
81         self.ou2_dn.add_base(self.top_ou)
82         ou2 = {}
83         ou2["dn"] = self.ou2_dn
84         ou2["objectclass"] = "organizationalUnit"
85         ou2["ou"] = self.ou2_dn.get_component_value(0)
86         self.ldb_dc1.add(ou2)
87
88         # trigger replication from DC1 to DC2
89         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True)
90         self.dc1_guid = self.ldb_dc1.get_invocation_id()
91         self.dc2_guid = self.ldb_dc2.get_invocation_id()
92
93         self.drs_dc1 = self._ds_bind(self.dnsname_dc1)
94         self.drs_dc2 = self._ds_bind(self.dnsname_dc2)
95
96     def tearDown(self):
97         try:
98             self.ldb_dc1.delete(self.top_ou, ["tree_delete:1"])
99         except ldb.LdbError as e:
100             (enum, string) = e.args
101             if enum == ldb.ERR_NO_SUCH_OBJECT:
102                 pass
103
104         self._enable_all_repl(self.dnsname_dc1)
105         self._enable_all_repl(self.dnsname_dc2)
106         super(DrsMoveObjectTestCase, self).tearDown()
107
108     def _make_username(self):
109         return "DrsMoveU_" + time.strftime("%s", time.gmtime())
110
111     def _check_metadata(self, user_dn, sam_ldb, drs, metadata, expected):
112         repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob, metadata[0])
113
114         self.assertEqual(len(repl.ctr.array), len(expected))
115
116         i = 0
117         for o in repl.ctr.array:
118             e = expected[i]
119             (attid, orig_dsa, version) = e
120             self.assertEquals(attid, o.attid,
121                               "(LDAP) Wrong attid "
122                               "for expected value %d, wanted 0x%08x got 0x%08x"
123                               % (i, attid, o.attid))
124             self.assertEquals(o.originating_invocation_id,
125                               misc.GUID(orig_dsa),
126                               "(LDAP) Wrong originating_invocation_id "
127                               "for expected value %d, attid 0x%08x, wanted %s got %s"
128                               % (i, o.attid,
129                                  misc.GUID(orig_dsa),
130                                  o.originating_invocation_id))
131             # Allow version to be skipped when it does not matter
132             if version is not None:
133                 self.assertEquals(o.version, version,
134                                   "(LDAP) Wrong version for expected value %d, "
135                                   "attid 0x%08x, "
136                                   "wanted %d got %d"
137                                   % (i, o.attid,
138                                      version, o.version))
139             i = i + 1
140
141         if drs == None:
142             return
143
144         req8 = DsGetNCChangesRequest8()
145
146         req8.source_dsa_invocation_id = misc.GUID(sam_ldb.get_invocation_id())
147         req8.naming_context = DsReplicaObjectIdentifier()
148         req8.naming_context.dn = str(user_dn)
149         req8.highwatermark = DsReplicaHighWaterMark()
150         req8.highwatermark.tmp_highest_usn = 0
151         req8.highwatermark.reserved_usn = 0
152         req8.highwatermark.highest_usn = 0
153         req8.uptodateness_vector = None
154         req8.replica_flags = DRSUAPI_DRS_SYNC_FORCED
155         req8.max_object_count = 1
156         req8.max_ndr_size = 402116
157         req8.extended_op = DRSUAPI_EXOP_REPL_OBJ
158         req8.fsmo_info = 0
159         req8.partial_attribute_set = None
160         req8.partial_attribute_set_ex = None
161         req8.mapping_ctr.num_mappings = 0
162         req8.mapping_ctr.mappings = None
163
164         (drs_conn, drs_handle) = drs
165
166         (level, drs_ctr) = drs_conn.DsGetNCChanges(drs_handle, 8, req8)
167         self.assertEqual(level, 6)
168         self.assertEqual(drs_ctr.object_count, 1)
169
170         self.assertEqual(len(drs_ctr.first_object.meta_data_ctr.meta_data), len(expected) - 1)
171         att_idx = 0
172         for o in drs_ctr.first_object.meta_data_ctr.meta_data:
173             i = 0
174             drs_attid = drs_ctr.first_object.object.attribute_ctr.attributes[att_idx]
175             e = expected[i];
176             (attid, orig_dsa, version) = e
177
178             # Skip the RDN from the expected set, it is not sent over DRS
179             if (user_dn.get_rdn_name().upper() == "CN" \
180                 and attid == DRSUAPI_ATTID_cn) \
181                 or (user_dn.get_rdn_name().upper() == "OU" \
182                     and attid == DRSUAPI_ATTID_ou):
183                 i = i + 1
184                 e = expected[i];
185                 (attid, orig_dsa, version) = e
186
187             self.assertEquals(attid, drs_attid.attid,
188                               "(DRS) Wrong attid "
189                               "for expected value %d, wanted 0x%08x got 0x%08x"
190                               % (i, attid, drs_attid.attid))
191
192             self.assertEquals(o.originating_invocation_id,
193                               misc.GUID(orig_dsa),
194                               "(DRS) Wrong originating_invocation_id "
195                               "for expected value %d, attid 0x%08x, wanted %s got %s"
196                               % (i, attid,
197                                  misc.GUID(orig_dsa),
198                                  o.originating_invocation_id))
199             # Allow version to be skipped when it does not matter
200             if version is not None:
201                 self.assertEquals(o.version, version,
202                                   "(DRS) Wrong version for expected value %d, "
203                                   "attid 0x%08x, "
204                                   "wanted %d got %d"
205                                   % (i, attid, version, o.version))
206                 break
207             i = i + 1
208             att_idx = att_idx + 1
209
210     # now also used to check the group
211     def _check_obj(self, sam_ldb, obj_orig, is_deleted, expected_metadata=None, drs=None):
212         # search the user by guid as it may be deleted
213         guid_str = self._GUID_string(obj_orig["objectGUID"][0])
214         res = sam_ldb.search(base='<GUID=%s>' % guid_str,
215                              controls=["show_deleted:1"],
216                              attrs=["*", "parentGUID",
217                                     "replPropertyMetaData"])
218         self.assertEquals(len(res), 1)
219         user_cur = res[0]
220         rdn_orig = obj_orig[user_cur.dn.get_rdn_name()][0]
221         rdn_cur  = user_cur[user_cur.dn.get_rdn_name()][0]
222         name_orig = obj_orig["name"][0]
223         name_cur  = user_cur["name"][0]
224         dn_orig = obj_orig["dn"]
225         dn_cur  = user_cur["dn"]
226         # now check properties of the user
227         if is_deleted:
228             self.assertTrue("isDeleted" in user_cur)
229             self.assertEquals(rdn_cur.split('\n')[0], rdn_orig)
230             self.assertEquals(name_cur.split('\n')[0], name_orig)
231             self.assertEquals(dn_cur.get_rdn_value().split('\n')[0],
232                               dn_orig.get_rdn_value())
233             self.assertEqual(name_cur, rdn_cur)
234         else:
235             self.assertFalse("isDeleted" in user_cur)
236             self.assertEquals(rdn_cur, rdn_orig)
237             self.assertEquals(name_cur, name_orig)
238             self.assertEquals(dn_cur, dn_orig)
239             self.assertEqual(name_cur, rdn_cur)
240             parent_cur  = user_cur["parentGUID"][0]
241             try:
242                 parent_orig = obj_orig["parentGUID"][0]
243                 self.assertEqual(parent_orig, parent_cur)
244             except KeyError:
245                 pass
246         self.assertEqual(name_cur, user_cur.dn.get_rdn_value())
247
248         if expected_metadata is not None:
249             self._check_metadata(dn_cur, sam_ldb, drs, user_cur["replPropertyMetaData"],
250                                  expected_metadata)
251
252         return user_cur
253
254     def test_ReplicateMoveObject1(self):
255         """Verifies how a moved container with a user inside is replicated between two DCs.
256            This test should verify that:
257             - the OU is replicated properly
258             - the OU is renamed
259             - We verify that after replication,
260               that the user has the correct DN (under OU2)
261             - the OU is deleted
262             - the OU is modified on DC2
263             - We verify that after replication,
264               that the user has the correct DN (deleted) and has not description
265
266            """
267         # work-out unique username to test with
268         username = self._make_username()
269
270         # create user on DC1
271         self.ldb_dc1.newuser(username=username,
272                              userou="ou=%s,ou=%s"
273                              % (self.ou1_dn.get_component_value(0),
274                                 self.top_ou.get_component_value(0)),
275                              password=None, setpassword=False)
276         ldb_res = self.ldb_dc1.search(base=self.ou1_dn,
277                                       scope=SCOPE_SUBTREE,
278                                       expression="(samAccountName=%s)" % username,
279                                       attrs=["*", "parentGUID"])
280         self.assertEquals(len(ldb_res), 1)
281         user_orig = ldb_res[0]
282         user_dn   = ldb_res[0]["dn"]
283
284         # check user info on DC1
285         print("Testing for %s with GUID %s" % (username, self._GUID_string(user_orig["objectGUID"][0])))
286         initial_metadata = [
287             (DRSUAPI_ATTID_objectClass, self.dc1_guid, 1),
288             (DRSUAPI_ATTID_cn, self.dc1_guid, 1),
289             (DRSUAPI_ATTID_instanceType, self.dc1_guid, 1),
290             (DRSUAPI_ATTID_whenCreated, self.dc1_guid, 1),
291             (DRSUAPI_ATTID_ntSecurityDescriptor, self.dc1_guid, 1),
292             (DRSUAPI_ATTID_name, self.dc1_guid, 1),
293             (DRSUAPI_ATTID_userAccountControl, self.dc1_guid, None),
294             (DRSUAPI_ATTID_codePage, self.dc1_guid, 1),
295             (DRSUAPI_ATTID_countryCode, self.dc1_guid, 1),
296             (DRSUAPI_ATTID_dBCSPwd, self.dc1_guid, 1),
297             (DRSUAPI_ATTID_logonHours, self.dc1_guid, 1),
298             (DRSUAPI_ATTID_unicodePwd, self.dc1_guid, 1),
299             (DRSUAPI_ATTID_ntPwdHistory, self.dc1_guid, 1),
300             (DRSUAPI_ATTID_pwdLastSet, self.dc1_guid, 1),
301             (DRSUAPI_ATTID_primaryGroupID, self.dc1_guid, 1),
302             (DRSUAPI_ATTID_objectSid, self.dc1_guid, 1),
303             (DRSUAPI_ATTID_accountExpires, self.dc1_guid, 1),
304             (DRSUAPI_ATTID_lmPwdHistory, self.dc1_guid, 1),
305             (DRSUAPI_ATTID_sAMAccountName, self.dc1_guid, 1),
306             (DRSUAPI_ATTID_sAMAccountType, self.dc1_guid, 1),
307             (DRSUAPI_ATTID_userPrincipalName, self.dc1_guid, 1),
308             (DRSUAPI_ATTID_objectCategory, self.dc1_guid, 1)]
309
310         self._check_obj(sam_ldb=self.ldb_dc1, drs=self.drs_dc1,
311                         obj_orig=user_orig, is_deleted=False,
312                         expected_metadata=initial_metadata)
313
314         new_dn = ldb.Dn(self.ldb_dc1, "CN=%s" % username)
315         new_dn.add_base(self.ou2_dn)
316         self.ldb_dc1.rename(user_dn, new_dn)
317         ldb_res = self.ldb_dc1.search(base=self.ou2_dn,
318                                       scope=SCOPE_SUBTREE,
319                                       expression="(samAccountName=%s)" % username,
320                                       attrs=["*", "parentGUID"])
321         self.assertEquals(len(ldb_res), 1)
322
323         user_moved_orig = ldb_res[0]
324         user_moved_dn   = ldb_res[0]["dn"]
325
326         moved_metadata = [
327             (DRSUAPI_ATTID_objectClass, self.dc1_guid, 1),
328             (DRSUAPI_ATTID_cn, self.dc1_guid, 1),
329             (DRSUAPI_ATTID_instanceType, self.dc1_guid, 1),
330             (DRSUAPI_ATTID_whenCreated, self.dc1_guid, 1),
331             (DRSUAPI_ATTID_ntSecurityDescriptor, self.dc1_guid, 1),
332             (DRSUAPI_ATTID_name, self.dc1_guid, 2),
333             (DRSUAPI_ATTID_userAccountControl, self.dc1_guid, None),
334             (DRSUAPI_ATTID_codePage, self.dc1_guid, 1),
335             (DRSUAPI_ATTID_countryCode, self.dc1_guid, 1),
336             (DRSUAPI_ATTID_dBCSPwd, self.dc1_guid, 1),
337             (DRSUAPI_ATTID_logonHours, self.dc1_guid, 1),
338             (DRSUAPI_ATTID_unicodePwd, self.dc1_guid, 1),
339             (DRSUAPI_ATTID_ntPwdHistory, self.dc1_guid, 1),
340             (DRSUAPI_ATTID_pwdLastSet, self.dc1_guid, 1),
341             (DRSUAPI_ATTID_primaryGroupID, self.dc1_guid, 1),
342             (DRSUAPI_ATTID_objectSid, self.dc1_guid, 1),
343             (DRSUAPI_ATTID_accountExpires, self.dc1_guid, 1),
344             (DRSUAPI_ATTID_lmPwdHistory, self.dc1_guid, 1),
345             (DRSUAPI_ATTID_sAMAccountName, self.dc1_guid, 1),
346             (DRSUAPI_ATTID_sAMAccountType, self.dc1_guid, 1),
347             (DRSUAPI_ATTID_userPrincipalName, self.dc1_guid, 1),
348             (DRSUAPI_ATTID_objectCategory, self.dc1_guid, 1)]
349
350         # check user info on DC1 after rename - should be valid user
351         user_cur = self._check_obj(sam_ldb=self.ldb_dc1, drs=self.drs_dc1,
352                                    obj_orig=user_moved_orig,
353                                    is_deleted=False,
354                                    expected_metadata=moved_metadata)
355
356         # trigger replication from DC1 to DC2
357         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True)
358         moved_metadata_dc2 = [
359             (DRSUAPI_ATTID_objectClass, self.dc1_guid, 1),
360             (DRSUAPI_ATTID_cn, self.dc2_guid, 1),
361             (DRSUAPI_ATTID_instanceType, self.dc1_guid, 1),
362             (DRSUAPI_ATTID_whenCreated, self.dc1_guid, 1),
363             (DRSUAPI_ATTID_ntSecurityDescriptor, self.dc1_guid, 1),
364             (DRSUAPI_ATTID_name, self.dc1_guid, 2),
365             (DRSUAPI_ATTID_userAccountControl, self.dc1_guid, None),
366             (DRSUAPI_ATTID_codePage, self.dc1_guid, 1),
367             (DRSUAPI_ATTID_countryCode, self.dc1_guid, 1),
368             (DRSUAPI_ATTID_dBCSPwd, self.dc1_guid, 1),
369             (DRSUAPI_ATTID_logonHours, self.dc1_guid, 1),
370             (DRSUAPI_ATTID_unicodePwd, self.dc1_guid, 1),
371             (DRSUAPI_ATTID_ntPwdHistory, self.dc1_guid, 1),
372             (DRSUAPI_ATTID_pwdLastSet, self.dc1_guid, 1),
373             (DRSUAPI_ATTID_primaryGroupID, self.dc1_guid, 1),
374             (DRSUAPI_ATTID_objectSid, self.dc1_guid, 1),
375             (DRSUAPI_ATTID_accountExpires, self.dc1_guid, 1),
376             (DRSUAPI_ATTID_lmPwdHistory, self.dc1_guid, 1),
377             (DRSUAPI_ATTID_sAMAccountName, self.dc1_guid, 1),
378             (DRSUAPI_ATTID_sAMAccountType, self.dc1_guid, 1),
379             (DRSUAPI_ATTID_userPrincipalName, self.dc1_guid, 1),
380             (DRSUAPI_ATTID_objectCategory, self.dc1_guid, 1)]
381
382         # check user info on DC2 - should be valid user
383         user_cur = self._check_obj(sam_ldb=self.ldb_dc2, drs=self.drs_dc2,
384                                    obj_orig=user_moved_orig,
385                                    is_deleted=False,
386                                    expected_metadata=moved_metadata_dc2)
387
388         # delete user on DC1
389         self.ldb_dc1.delete('<GUID=%s>' % self._GUID_string(user_orig["objectGUID"][0]))
390         deleted_metadata = [
391             (DRSUAPI_ATTID_objectClass, self.dc1_guid, 1),
392             (DRSUAPI_ATTID_cn, self.dc1_guid, 2),
393             (DRSUAPI_ATTID_instanceType, self.dc1_guid, 1),
394             (DRSUAPI_ATTID_whenCreated, self.dc1_guid, 1),
395             (DRSUAPI_ATTID_isDeleted, self.dc1_guid, 1),
396             (DRSUAPI_ATTID_ntSecurityDescriptor, self.dc1_guid, 1),
397             (DRSUAPI_ATTID_name, self.dc1_guid, 3),
398             (DRSUAPI_ATTID_userAccountControl, self.dc1_guid, None),
399             (DRSUAPI_ATTID_codePage, self.dc1_guid, 2),
400             (DRSUAPI_ATTID_countryCode, self.dc1_guid, 2),
401             (DRSUAPI_ATTID_dBCSPwd, self.dc1_guid, 1),
402             (DRSUAPI_ATTID_logonHours, self.dc1_guid, 1),
403             (DRSUAPI_ATTID_unicodePwd, self.dc1_guid, 1),
404             (DRSUAPI_ATTID_ntPwdHistory, self.dc1_guid, 1),
405             (DRSUAPI_ATTID_pwdLastSet, self.dc1_guid, 2),
406             (DRSUAPI_ATTID_primaryGroupID, self.dc1_guid, 2),
407             (DRSUAPI_ATTID_objectSid, self.dc1_guid, 1),
408             (DRSUAPI_ATTID_accountExpires, self.dc1_guid, 2),
409             (DRSUAPI_ATTID_lmPwdHistory, self.dc1_guid, 1),
410             (DRSUAPI_ATTID_sAMAccountName, self.dc1_guid, 1),
411             (DRSUAPI_ATTID_sAMAccountType, self.dc1_guid, 2),
412             (DRSUAPI_ATTID_userPrincipalName, self.dc1_guid, 2),
413             (DRSUAPI_ATTID_lastKnownParent, self.dc1_guid, 1),
414             (DRSUAPI_ATTID_objectCategory, self.dc1_guid, 2),
415             (DRSUAPI_ATTID_isRecycled, self.dc1_guid, 1)]
416
417         user_cur = self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=user_moved_orig, is_deleted=True, expected_metadata=deleted_metadata)
418
419         # Modify description on DC2.  This triggers a replication, but
420         # not of 'name' and so a bug in Samba regarding the DN.
421         msg = ldb.Message()
422         msg.dn = new_dn
423         msg["description"] = ldb.MessageElement("User Description", ldb.FLAG_MOD_REPLACE, "description")
424         self.ldb_dc2.modify(msg)
425
426         modified_metadata = [
427             (DRSUAPI_ATTID_objectClass, self.dc1_guid, 1),
428             (DRSUAPI_ATTID_cn, self.dc2_guid, 1),
429             (DRSUAPI_ATTID_description, self.dc2_guid, 1),
430             (DRSUAPI_ATTID_instanceType, self.dc1_guid, 1),
431             (DRSUAPI_ATTID_whenCreated, self.dc1_guid, 1),
432             (DRSUAPI_ATTID_ntSecurityDescriptor, self.dc1_guid, 1),
433             (DRSUAPI_ATTID_name, self.dc1_guid, 2),
434             (DRSUAPI_ATTID_userAccountControl, self.dc1_guid, None),
435             (DRSUAPI_ATTID_codePage, self.dc1_guid, 1),
436             (DRSUAPI_ATTID_countryCode, self.dc1_guid, 1),
437             (DRSUAPI_ATTID_dBCSPwd, self.dc1_guid, 1),
438             (DRSUAPI_ATTID_logonHours, self.dc1_guid, 1),
439             (DRSUAPI_ATTID_unicodePwd, self.dc1_guid, 1),
440             (DRSUAPI_ATTID_ntPwdHistory, self.dc1_guid, 1),
441             (DRSUAPI_ATTID_pwdLastSet, self.dc1_guid, 1),
442             (DRSUAPI_ATTID_primaryGroupID, self.dc1_guid, 1),
443             (DRSUAPI_ATTID_objectSid, self.dc1_guid, 1),
444             (DRSUAPI_ATTID_accountExpires, self.dc1_guid, 1),
445             (DRSUAPI_ATTID_lmPwdHistory, self.dc1_guid, 1),
446             (DRSUAPI_ATTID_sAMAccountName, self.dc1_guid, 1),
447             (DRSUAPI_ATTID_sAMAccountType, self.dc1_guid, 1),
448             (DRSUAPI_ATTID_userPrincipalName, self.dc1_guid, 1),
449             (DRSUAPI_ATTID_objectCategory, self.dc1_guid, 1)]
450
451         user_cur = self._check_obj(sam_ldb=self.ldb_dc2, drs=self.drs_dc2,
452                                    obj_orig=user_moved_orig,
453                                    is_deleted=False,
454                                    expected_metadata=modified_metadata)
455
456         # trigger replication from DC1 to DC2, for cleanup
457         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True)
458
459         deleted_modified_metadata_dc2 = [
460             (DRSUAPI_ATTID_objectClass, self.dc1_guid, 1),
461             (DRSUAPI_ATTID_cn, self.dc2_guid, 2),
462             (DRSUAPI_ATTID_description, self.dc2_guid, 2),
463             (DRSUAPI_ATTID_instanceType, self.dc1_guid, 1),
464             (DRSUAPI_ATTID_whenCreated, self.dc1_guid, 1),
465             (DRSUAPI_ATTID_isDeleted, self.dc1_guid, 1),
466             (DRSUAPI_ATTID_ntSecurityDescriptor, self.dc1_guid, 1),
467             (DRSUAPI_ATTID_name, self.dc1_guid, 3),
468             (DRSUAPI_ATTID_userAccountControl, self.dc1_guid, None),
469             (DRSUAPI_ATTID_codePage, self.dc1_guid, 2),
470             (DRSUAPI_ATTID_countryCode, self.dc1_guid, 2),
471             (DRSUAPI_ATTID_dBCSPwd, self.dc1_guid, 1),
472             (DRSUAPI_ATTID_logonHours, self.dc1_guid, 1),
473             (DRSUAPI_ATTID_unicodePwd, self.dc1_guid, 1),
474             (DRSUAPI_ATTID_ntPwdHistory, self.dc1_guid, 1),
475             (DRSUAPI_ATTID_pwdLastSet, self.dc1_guid, 2),
476             (DRSUAPI_ATTID_primaryGroupID, self.dc1_guid, 2),
477             (DRSUAPI_ATTID_objectSid, self.dc1_guid, 1),
478             (DRSUAPI_ATTID_accountExpires, self.dc1_guid, 2),
479             (DRSUAPI_ATTID_lmPwdHistory, self.dc1_guid, 1),
480             (DRSUAPI_ATTID_sAMAccountName, self.dc1_guid, 1),
481             (DRSUAPI_ATTID_sAMAccountType, self.dc1_guid, 2),
482             (DRSUAPI_ATTID_userPrincipalName, self.dc1_guid, 2),
483             (DRSUAPI_ATTID_lastKnownParent, self.dc1_guid, 1),
484             (DRSUAPI_ATTID_objectCategory, self.dc1_guid, 2),
485             (DRSUAPI_ATTID_isRecycled, self.dc1_guid, 1)]
486
487         # check user info on DC2 - should be deleted user
488         user_cur = self._check_obj(sam_ldb=self.ldb_dc2, drs=self.drs_dc2,
489                                    obj_orig=user_moved_orig,
490                                    is_deleted=True,
491                                    expected_metadata=deleted_modified_metadata_dc2)
492         self.assertFalse("description" in user_cur)
493
494         # trigger replication from DC2 to DC1, for cleanup
495         self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True)
496
497         deleted_modified_metadata_dc1 = [
498             (DRSUAPI_ATTID_objectClass, self.dc1_guid, 1),
499             (DRSUAPI_ATTID_cn, self.dc1_guid, 2),
500             (DRSUAPI_ATTID_description, self.dc2_guid, 2),
501             (DRSUAPI_ATTID_instanceType, self.dc1_guid, 1),
502             (DRSUAPI_ATTID_whenCreated, self.dc1_guid, 1),
503             (DRSUAPI_ATTID_isDeleted, self.dc1_guid, 1),
504             (DRSUAPI_ATTID_ntSecurityDescriptor, self.dc1_guid, 1),
505             (DRSUAPI_ATTID_name, self.dc1_guid, 3),
506             (DRSUAPI_ATTID_userAccountControl, self.dc1_guid, None),
507             (DRSUAPI_ATTID_codePage, self.dc1_guid, 2),
508             (DRSUAPI_ATTID_countryCode, self.dc1_guid, 2),
509             (DRSUAPI_ATTID_dBCSPwd, self.dc1_guid, 1),
510             (DRSUAPI_ATTID_logonHours, self.dc1_guid, 1),
511             (DRSUAPI_ATTID_unicodePwd, self.dc1_guid, 1),
512             (DRSUAPI_ATTID_ntPwdHistory, self.dc1_guid, 1),
513             (DRSUAPI_ATTID_pwdLastSet, self.dc1_guid, 2),
514             (DRSUAPI_ATTID_primaryGroupID, self.dc1_guid, 2),
515             (DRSUAPI_ATTID_objectSid, self.dc1_guid, 1),
516             (DRSUAPI_ATTID_accountExpires, self.dc1_guid, 2),
517             (DRSUAPI_ATTID_lmPwdHistory, self.dc1_guid, 1),
518             (DRSUAPI_ATTID_sAMAccountName, self.dc1_guid, 1),
519             (DRSUAPI_ATTID_sAMAccountType, self.dc1_guid, 2),
520             (DRSUAPI_ATTID_userPrincipalName, self.dc1_guid, 2),
521             (DRSUAPI_ATTID_lastKnownParent, self.dc1_guid, 1),
522             (DRSUAPI_ATTID_objectCategory, self.dc1_guid, 2),
523             (DRSUAPI_ATTID_isRecycled, self.dc1_guid, 1)]
524
525         # check user info on DC1 - should be deleted user
526         user_cur = self._check_obj(sam_ldb=self.ldb_dc1, drs=self.drs_dc1,
527                                    obj_orig=user_moved_orig,
528                                    is_deleted=True,
529                                    expected_metadata=deleted_modified_metadata_dc1)
530         self.assertFalse("description" in user_cur)
531
532     def test_ReplicateMoveObject2(self):
533         """Verifies how a moved container with a user inside is not
534            replicated between two DCs as no replication is triggered
535            This test should verify that:
536             - the OU is not replicated
537             - the user is not replicated
538
539            """
540         # work-out unique username to test with
541         username = self._make_username()
542
543         # create user on DC1
544         self.ldb_dc1.newuser(username=username,
545                              userou="ou=%s,ou=%s"
546                              % (self.ou1_dn.get_component_value(0),
547                                 self.top_ou.get_component_value(0)),
548                              password=None, setpassword=False)
549         ldb_res = self.ldb_dc1.search(base=self.ou1_dn,
550                                       scope=SCOPE_SUBTREE,
551                                       expression="(samAccountName=%s)" % username,
552                                       attrs=["*", "parentGUID"])
553         self.assertEquals(len(ldb_res), 1)
554         user_orig = ldb_res[0]
555         user_dn   = ldb_res[0]["dn"]
556
557         # check user info on DC1
558         print("Testing for %s with GUID %s" % (username, self._GUID_string(user_orig["objectGUID"][0])))
559         initial_metadata = [
560             (DRSUAPI_ATTID_objectClass, self.dc1_guid, 1),
561             (DRSUAPI_ATTID_cn, self.dc1_guid, 1),
562             (DRSUAPI_ATTID_instanceType, self.dc1_guid, 1),
563             (DRSUAPI_ATTID_whenCreated, self.dc1_guid, 1),
564             (DRSUAPI_ATTID_ntSecurityDescriptor, self.dc1_guid, 1),
565             (DRSUAPI_ATTID_name, self.dc1_guid, 1),
566             (DRSUAPI_ATTID_userAccountControl, self.dc1_guid, None),
567             (DRSUAPI_ATTID_codePage, self.dc1_guid, 1),
568             (DRSUAPI_ATTID_countryCode, self.dc1_guid, 1),
569             (DRSUAPI_ATTID_dBCSPwd, self.dc1_guid, 1),
570             (DRSUAPI_ATTID_logonHours, self.dc1_guid, 1),
571             (DRSUAPI_ATTID_unicodePwd, self.dc1_guid, 1),
572             (DRSUAPI_ATTID_ntPwdHistory, self.dc1_guid, 1),
573             (DRSUAPI_ATTID_pwdLastSet, self.dc1_guid, 1),
574             (DRSUAPI_ATTID_primaryGroupID, self.dc1_guid, 1),
575             (DRSUAPI_ATTID_objectSid, self.dc1_guid, 1),
576             (DRSUAPI_ATTID_accountExpires, self.dc1_guid, 1),
577             (DRSUAPI_ATTID_lmPwdHistory, self.dc1_guid, 1),
578             (DRSUAPI_ATTID_sAMAccountName, self.dc1_guid, 1),
579             (DRSUAPI_ATTID_sAMAccountType, self.dc1_guid, 1),
580             (DRSUAPI_ATTID_userPrincipalName, self.dc1_guid, 1),
581             (DRSUAPI_ATTID_objectCategory, self.dc1_guid, 1)]
582
583         self._check_obj(sam_ldb=self.ldb_dc1, drs=self.drs_dc1,
584                         obj_orig=user_orig, is_deleted=False,
585                         expected_metadata=initial_metadata)
586
587         new_dn = ldb.Dn(self.ldb_dc1, "CN=%s" % username)
588         new_dn.add_base(self.ou2_dn)
589         self.ldb_dc1.rename(user_dn, new_dn)
590         ldb_res = self.ldb_dc1.search(base=self.ou2_dn,
591                                       scope=SCOPE_SUBTREE,
592                                       expression="(samAccountName=%s)" % username,
593                                       attrs=["*", "parentGUID"])
594         self.assertEquals(len(ldb_res), 1)
595         user_moved_orig = ldb_res[0]
596
597         moved_metadata = [
598             (DRSUAPI_ATTID_objectClass, self.dc1_guid, 1),
599             (DRSUAPI_ATTID_cn, self.dc1_guid, 1),
600             (DRSUAPI_ATTID_instanceType, self.dc1_guid, 1),
601             (DRSUAPI_ATTID_whenCreated, self.dc1_guid, 1),
602             (DRSUAPI_ATTID_ntSecurityDescriptor, self.dc1_guid, 1),
603             (DRSUAPI_ATTID_name, self.dc1_guid, 2),
604             (DRSUAPI_ATTID_userAccountControl, self.dc1_guid, None),
605             (DRSUAPI_ATTID_codePage, self.dc1_guid, 1),
606             (DRSUAPI_ATTID_countryCode, self.dc1_guid, 1),
607             (DRSUAPI_ATTID_dBCSPwd, self.dc1_guid, 1),
608             (DRSUAPI_ATTID_logonHours, self.dc1_guid, 1),
609             (DRSUAPI_ATTID_unicodePwd, self.dc1_guid, 1),
610             (DRSUAPI_ATTID_ntPwdHistory, self.dc1_guid, 1),
611             (DRSUAPI_ATTID_pwdLastSet, self.dc1_guid, 1),
612             (DRSUAPI_ATTID_primaryGroupID, self.dc1_guid, 1),
613             (DRSUAPI_ATTID_objectSid, self.dc1_guid, 1),
614             (DRSUAPI_ATTID_accountExpires, self.dc1_guid, 1),
615             (DRSUAPI_ATTID_lmPwdHistory, self.dc1_guid, 1),
616             (DRSUAPI_ATTID_sAMAccountName, self.dc1_guid, 1),
617             (DRSUAPI_ATTID_sAMAccountType, self.dc1_guid, 1),
618             (DRSUAPI_ATTID_userPrincipalName, self.dc1_guid, 1),
619             (DRSUAPI_ATTID_objectCategory, self.dc1_guid, 1)]
620
621         # check user info on DC1 after rename - should be valid user
622         user_cur = self._check_obj(sam_ldb=self.ldb_dc1, drs=self.drs_dc1,
623                                    obj_orig=user_moved_orig,
624                                    is_deleted=False,
625                                    expected_metadata=moved_metadata)
626
627         # check user info on DC2 - should not be there, we have not done replication
628         ldb_res = self.ldb_dc2.search(base=self.ou2_dn,
629                                       scope=SCOPE_SUBTREE,
630                                       expression="(samAccountName=%s)" % username,
631                                       attrs=["*", "parentGUID"])
632         self.assertEquals(len(ldb_res), 0)
633
634         # delete user on DC1
635         self.ldb_dc1.delete('<GUID=%s>' % self._GUID_string(user_orig["objectGUID"][0]))
636
637         deleted_metadata_dc1 = [
638             (DRSUAPI_ATTID_objectClass, self.dc1_guid, 1),
639             (DRSUAPI_ATTID_cn, self.dc1_guid, 2),
640             (DRSUAPI_ATTID_instanceType, self.dc1_guid, 1),
641             (DRSUAPI_ATTID_whenCreated, self.dc1_guid, 1),
642             (DRSUAPI_ATTID_isDeleted, self.dc1_guid, 1),
643             (DRSUAPI_ATTID_ntSecurityDescriptor, self.dc1_guid, 1),
644             (DRSUAPI_ATTID_name, self.dc1_guid, 3),
645             (DRSUAPI_ATTID_userAccountControl, self.dc1_guid, None),
646             (DRSUAPI_ATTID_codePage, self.dc1_guid, 2),
647             (DRSUAPI_ATTID_countryCode, self.dc1_guid, 2),
648             (DRSUAPI_ATTID_dBCSPwd, self.dc1_guid, 1),
649             (DRSUAPI_ATTID_logonHours, self.dc1_guid, 1),
650             (DRSUAPI_ATTID_unicodePwd, self.dc1_guid, 1),
651             (DRSUAPI_ATTID_ntPwdHistory, self.dc1_guid, 1),
652             (DRSUAPI_ATTID_pwdLastSet, self.dc1_guid, 2),
653             (DRSUAPI_ATTID_primaryGroupID, self.dc1_guid, 2),
654             (DRSUAPI_ATTID_objectSid, self.dc1_guid, 1),
655             (DRSUAPI_ATTID_accountExpires, self.dc1_guid, 2),
656             (DRSUAPI_ATTID_lmPwdHistory, self.dc1_guid, 1),
657             (DRSUAPI_ATTID_sAMAccountName, self.dc1_guid, 1),
658             (DRSUAPI_ATTID_sAMAccountType, self.dc1_guid, 2),
659             (DRSUAPI_ATTID_userPrincipalName, self.dc1_guid, 2),
660             (DRSUAPI_ATTID_lastKnownParent, self.dc1_guid, 1),
661             (DRSUAPI_ATTID_objectCategory, self.dc1_guid, 2),
662             (DRSUAPI_ATTID_isRecycled, self.dc1_guid, 1)]
663
664         # check user info on DC1 - should be deleted user
665         user_cur = self._check_obj(sam_ldb=self.ldb_dc1, drs=self.drs_dc1,
666                                    obj_orig=user_moved_orig,
667                                    is_deleted=True,
668                                    expected_metadata=deleted_metadata_dc1)
669         # trigger replication from DC1 to DC2, for cleanup
670         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True)
671
672         deleted_metadata_dc2 = [
673             (DRSUAPI_ATTID_objectClass, self.dc1_guid, 1),
674             (DRSUAPI_ATTID_cn, self.dc2_guid, 1),
675             (DRSUAPI_ATTID_instanceType, self.dc1_guid, 1),
676             (DRSUAPI_ATTID_whenCreated, self.dc1_guid, 1),
677             (DRSUAPI_ATTID_isDeleted, self.dc1_guid, 1),
678             (DRSUAPI_ATTID_ntSecurityDescriptor, self.dc1_guid, 1),
679             (DRSUAPI_ATTID_name, self.dc1_guid, 3),
680             (DRSUAPI_ATTID_userAccountControl, self.dc1_guid, None),
681             (DRSUAPI_ATTID_codePage, self.dc1_guid, 2),
682             (DRSUAPI_ATTID_countryCode, self.dc1_guid, 2),
683             (DRSUAPI_ATTID_dBCSPwd, self.dc1_guid, 1),
684             (DRSUAPI_ATTID_logonHours, self.dc1_guid, 1),
685             (DRSUAPI_ATTID_unicodePwd, self.dc1_guid, 1),
686             (DRSUAPI_ATTID_ntPwdHistory, self.dc1_guid, 1),
687             (DRSUAPI_ATTID_pwdLastSet, self.dc1_guid, 2),
688             (DRSUAPI_ATTID_primaryGroupID, self.dc1_guid, 2),
689             (DRSUAPI_ATTID_objectSid, self.dc1_guid, 1),
690             (DRSUAPI_ATTID_accountExpires, self.dc1_guid, 2),
691             (DRSUAPI_ATTID_lmPwdHistory, self.dc1_guid, 1),
692             (DRSUAPI_ATTID_sAMAccountName, self.dc1_guid, 1),
693             (DRSUAPI_ATTID_sAMAccountType, self.dc1_guid, 2),
694             (DRSUAPI_ATTID_userPrincipalName, self.dc1_guid, 2),
695             (DRSUAPI_ATTID_lastKnownParent, self.dc1_guid, 1),
696             (DRSUAPI_ATTID_objectCategory, self.dc1_guid, 2),
697             (DRSUAPI_ATTID_isRecycled, self.dc1_guid, 1)]
698
699         # check user info on DC2 - should be deleted user
700         user_cur = self._check_obj(sam_ldb=self.ldb_dc2, drs=self.drs_dc2,
701                                    obj_orig=user_moved_orig,
702                                    is_deleted=True,
703                                    expected_metadata=deleted_metadata_dc2)
704
705         # trigger replication from DC2 to DC1, for cleanup
706         self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True)
707
708         # check user info on DC1 - should be deleted user
709         user_cur = self._check_obj(sam_ldb=self.ldb_dc1, drs=self.drs_dc1,
710                                    obj_orig=user_moved_orig,
711                                    is_deleted=True,
712                                    expected_metadata=deleted_metadata_dc1)
713
714     def test_ReplicateMoveObject3(self):
715         """Verifies how a moved container with a user inside is replicated between two DCs.
716            This test should verify that:
717             - the OU is created on DC1
718             - the OU is renamed on DC1
719             - We verify that after replication,
720               that the user has the correct DN (under OU2).
721
722            """
723         # work-out unique username to test with
724         username = self._make_username()
725
726         # create user on DC1
727         self.ldb_dc1.newuser(username=username,
728                              userou="ou=%s,ou=%s"
729                              % (self.ou1_dn.get_component_value(0),
730                                 self.top_ou.get_component_value(0)),
731                              password=None, setpassword=False)
732         ldb_res = self.ldb_dc1.search(base=self.ou1_dn,
733                                       scope=SCOPE_SUBTREE,
734                                       expression="(samAccountName=%s)" % username,
735                                       attrs=["*", "parentGUID"])
736         self.assertEquals(len(ldb_res), 1)
737         user_orig = ldb_res[0]
738         user_dn   = ldb_res[0]["dn"]
739
740         # check user info on DC1
741         print("Testing for %s with GUID %s" % (username, self._GUID_string(user_orig["objectGUID"][0])))
742         initial_metadata = [
743             (DRSUAPI_ATTID_objectClass, self.dc1_guid, 1),
744             (DRSUAPI_ATTID_cn, self.dc1_guid, 1),
745             (DRSUAPI_ATTID_instanceType, self.dc1_guid, 1),
746             (DRSUAPI_ATTID_whenCreated, self.dc1_guid, 1),
747             (DRSUAPI_ATTID_ntSecurityDescriptor, self.dc1_guid, 1),
748             (DRSUAPI_ATTID_name, self.dc1_guid, 1),
749             (DRSUAPI_ATTID_userAccountControl, self.dc1_guid, None),
750             (DRSUAPI_ATTID_codePage, self.dc1_guid, 1),
751             (DRSUAPI_ATTID_countryCode, self.dc1_guid, 1),
752             (DRSUAPI_ATTID_dBCSPwd, self.dc1_guid, 1),
753             (DRSUAPI_ATTID_logonHours, self.dc1_guid, 1),
754             (DRSUAPI_ATTID_unicodePwd, self.dc1_guid, 1),
755             (DRSUAPI_ATTID_ntPwdHistory, self.dc1_guid, 1),
756             (DRSUAPI_ATTID_pwdLastSet, self.dc1_guid, 1),
757             (DRSUAPI_ATTID_primaryGroupID, self.dc1_guid, 1),
758             (DRSUAPI_ATTID_objectSid, self.dc1_guid, 1),
759             (DRSUAPI_ATTID_accountExpires, self.dc1_guid, 1),
760             (DRSUAPI_ATTID_lmPwdHistory, self.dc1_guid, 1),
761             (DRSUAPI_ATTID_sAMAccountName, self.dc1_guid, 1),
762             (DRSUAPI_ATTID_sAMAccountType, self.dc1_guid, 1),
763             (DRSUAPI_ATTID_userPrincipalName, self.dc1_guid, 1),
764             (DRSUAPI_ATTID_objectCategory, self.dc1_guid, 1)]
765
766         self._check_obj(sam_ldb=self.ldb_dc1, drs=self.drs_dc1,
767                         obj_orig=user_orig, is_deleted=False,
768                         expected_metadata=initial_metadata)
769
770         new_dn = ldb.Dn(self.ldb_dc1, "CN=%s" % username)
771         new_dn.add_base(self.ou2_dn)
772         self.ldb_dc1.rename(user_dn, new_dn)
773         ldb_res = self.ldb_dc1.search(base=self.ou2_dn,
774                                       scope=SCOPE_SUBTREE,
775                                       expression="(samAccountName=%s)" % username,
776                                       attrs=["*", "parentGUID"])
777         self.assertEquals(len(ldb_res), 1)
778
779         user_moved_orig = ldb_res[0]
780         user_moved_dn   = ldb_res[0]["dn"]
781
782         # trigger replication from DC1 to DC2
783         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True)
784         moved_metadata = [
785             (DRSUAPI_ATTID_objectClass, self.dc1_guid, 1),
786             (DRSUAPI_ATTID_cn, self.dc1_guid, 1),
787             (DRSUAPI_ATTID_instanceType, self.dc1_guid, 1),
788             (DRSUAPI_ATTID_whenCreated, self.dc1_guid, 1),
789             (DRSUAPI_ATTID_ntSecurityDescriptor, self.dc1_guid, 1),
790             (DRSUAPI_ATTID_name, self.dc1_guid, 2),
791             (DRSUAPI_ATTID_userAccountControl, self.dc1_guid, None),
792             (DRSUAPI_ATTID_codePage, self.dc1_guid, 1),
793             (DRSUAPI_ATTID_countryCode, self.dc1_guid, 1),
794             (DRSUAPI_ATTID_dBCSPwd, self.dc1_guid, 1),
795             (DRSUAPI_ATTID_logonHours, self.dc1_guid, 1),
796             (DRSUAPI_ATTID_unicodePwd, self.dc1_guid, 1),
797             (DRSUAPI_ATTID_ntPwdHistory, self.dc1_guid, 1),
798             (DRSUAPI_ATTID_pwdLastSet, self.dc1_guid, 1),
799             (DRSUAPI_ATTID_primaryGroupID, self.dc1_guid, 1),
800             (DRSUAPI_ATTID_objectSid, self.dc1_guid, 1),
801             (DRSUAPI_ATTID_accountExpires, self.dc1_guid, 1),
802             (DRSUAPI_ATTID_lmPwdHistory, self.dc1_guid, 1),
803             (DRSUAPI_ATTID_sAMAccountName, self.dc1_guid, 1),
804             (DRSUAPI_ATTID_sAMAccountType, self.dc1_guid, 1),
805             (DRSUAPI_ATTID_userPrincipalName, self.dc1_guid, 1),
806             (DRSUAPI_ATTID_objectCategory, self.dc1_guid, 1)]
807
808         # check user info on DC1 after rename - should be valid user
809         user_cur = self._check_obj(sam_ldb=self.ldb_dc1, drs=self.drs_dc1,
810                                    obj_orig=user_moved_orig,
811                                    is_deleted=False,
812                                    expected_metadata=moved_metadata)
813
814         # delete user on DC1
815         self.ldb_dc1.delete('<GUID=%s>' % self._GUID_string(user_orig["objectGUID"][0]))
816         deleted_metadata_dc1 = [
817             (DRSUAPI_ATTID_objectClass, self.dc1_guid, 1),
818             (DRSUAPI_ATTID_cn, self.dc1_guid, 2),
819             (DRSUAPI_ATTID_instanceType, self.dc1_guid, 1),
820             (DRSUAPI_ATTID_whenCreated, self.dc1_guid, 1),
821             (DRSUAPI_ATTID_isDeleted, self.dc1_guid, 1),
822             (DRSUAPI_ATTID_ntSecurityDescriptor, self.dc1_guid, 1),
823             (DRSUAPI_ATTID_name, self.dc1_guid, 3),
824             (DRSUAPI_ATTID_userAccountControl, self.dc1_guid, None),
825             (DRSUAPI_ATTID_codePage, self.dc1_guid, 2),
826             (DRSUAPI_ATTID_countryCode, self.dc1_guid, 2),
827             (DRSUAPI_ATTID_dBCSPwd, self.dc1_guid, 1),
828             (DRSUAPI_ATTID_logonHours, self.dc1_guid, 1),
829             (DRSUAPI_ATTID_unicodePwd, self.dc1_guid, 1),
830             (DRSUAPI_ATTID_ntPwdHistory, self.dc1_guid, 1),
831             (DRSUAPI_ATTID_pwdLastSet, self.dc1_guid, 2),
832             (DRSUAPI_ATTID_primaryGroupID, self.dc1_guid, 2),
833             (DRSUAPI_ATTID_objectSid, self.dc1_guid, 1),
834             (DRSUAPI_ATTID_accountExpires, self.dc1_guid, 2),
835             (DRSUAPI_ATTID_lmPwdHistory, self.dc1_guid, 1),
836             (DRSUAPI_ATTID_sAMAccountName, self.dc1_guid, 1),
837             (DRSUAPI_ATTID_sAMAccountType, self.dc1_guid, 2),
838             (DRSUAPI_ATTID_userPrincipalName, self.dc1_guid, 2),
839             (DRSUAPI_ATTID_lastKnownParent, self.dc1_guid, 1),
840             (DRSUAPI_ATTID_objectCategory, self.dc1_guid, 2),
841             (DRSUAPI_ATTID_isRecycled, self.dc1_guid, 1)]
842
843         # check user info on DC1 - should be deleted user
844         user_cur = self._check_obj(sam_ldb=self.ldb_dc1, drs=self.drs_dc1,
845                                    obj_orig=user_moved_orig,
846                                    is_deleted=True,
847                                    expected_metadata=deleted_metadata_dc1)
848
849         # trigger replication from DC2 to DC1
850         self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True)
851
852         # check user info on DC1 - should be deleted user
853         user_cur = self._check_obj(sam_ldb=self.ldb_dc1, drs=self.drs_dc1,
854                                    obj_orig=user_moved_orig,
855                                    is_deleted=True,
856                                    expected_metadata=deleted_metadata_dc1)
857
858         # trigger replication from DC1 to DC2, for cleanup
859         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True)
860
861         deleted_metadata_dc2 = [
862             (DRSUAPI_ATTID_objectClass, self.dc1_guid, 1),
863             (DRSUAPI_ATTID_cn, self.dc2_guid, 2),
864             (DRSUAPI_ATTID_instanceType, self.dc1_guid, 1),
865             (DRSUAPI_ATTID_whenCreated, self.dc1_guid, 1),
866             (DRSUAPI_ATTID_isDeleted, self.dc1_guid, 1),
867             (DRSUAPI_ATTID_ntSecurityDescriptor, self.dc1_guid, 1),
868             (DRSUAPI_ATTID_name, self.dc1_guid, 3),
869             (DRSUAPI_ATTID_userAccountControl, self.dc1_guid, None),
870             (DRSUAPI_ATTID_codePage, self.dc1_guid, 2),
871             (DRSUAPI_ATTID_countryCode, self.dc1_guid, 2),
872             (DRSUAPI_ATTID_dBCSPwd, self.dc1_guid, 1),
873             (DRSUAPI_ATTID_logonHours, self.dc1_guid, 1),
874             (DRSUAPI_ATTID_unicodePwd, self.dc1_guid, 1),
875             (DRSUAPI_ATTID_ntPwdHistory, self.dc1_guid, 1),
876             (DRSUAPI_ATTID_pwdLastSet, self.dc1_guid, 2),
877             (DRSUAPI_ATTID_primaryGroupID, self.dc1_guid, 2),
878             (DRSUAPI_ATTID_objectSid, self.dc1_guid, 1),
879             (DRSUAPI_ATTID_accountExpires, self.dc1_guid, 2),
880             (DRSUAPI_ATTID_lmPwdHistory, self.dc1_guid, 1),
881             (DRSUAPI_ATTID_sAMAccountName, self.dc1_guid, 1),
882             (DRSUAPI_ATTID_sAMAccountType, self.dc1_guid, 2),
883             (DRSUAPI_ATTID_userPrincipalName, self.dc1_guid, 2),
884             (DRSUAPI_ATTID_lastKnownParent, self.dc1_guid, 1),
885             (DRSUAPI_ATTID_objectCategory, self.dc1_guid, 2),
886             (DRSUAPI_ATTID_isRecycled, self.dc1_guid, 1)]
887
888         # check user info on DC2 - should be deleted user
889         user_cur = self._check_obj(sam_ldb=self.ldb_dc2, drs=self.drs_dc2,
890                                    obj_orig=user_moved_orig,
891                                    is_deleted=True,
892                                    expected_metadata=deleted_metadata_dc2)
893
894     def test_ReplicateMoveObject3b(self):
895         """Verifies how a moved container with a user inside is replicated between two DCs.
896            This test should verify that:
897             - the OU is created on DC1
898             - the OU is renamed on DC1
899             - We verify that after replication,
900               that the user has the correct DN (under OU2).
901
902            """
903         # work-out unique username to test with
904         username = self._make_username()
905
906         # create user on DC1
907         self.ldb_dc1.newuser(username=username,
908                              userou="ou=%s,ou=%s"
909                              % (self.ou1_dn.get_component_value(0),
910                                 self.top_ou.get_component_value(0)),
911                              password=None, setpassword=False)
912         ldb_res = self.ldb_dc1.search(base=self.ou1_dn,
913                                       scope=SCOPE_SUBTREE,
914                                       expression="(samAccountName=%s)" % username,
915                                       attrs=["*", "parentGUID"])
916         self.assertEquals(len(ldb_res), 1)
917         user_orig = ldb_res[0]
918         user_dn   = ldb_res[0]["dn"]
919
920         # check user info on DC1
921         print("Testing for %s with GUID %s" % (username, self._GUID_string(user_orig["objectGUID"][0])))
922         initial_metadata = [
923             (DRSUAPI_ATTID_objectClass, self.dc1_guid, 1),
924             (DRSUAPI_ATTID_cn, self.dc1_guid, 1),
925             (DRSUAPI_ATTID_instanceType, self.dc1_guid, 1),
926             (DRSUAPI_ATTID_whenCreated, self.dc1_guid, 1),
927             (DRSUAPI_ATTID_ntSecurityDescriptor, self.dc1_guid, 1),
928             (DRSUAPI_ATTID_name, self.dc1_guid, 1),
929             (DRSUAPI_ATTID_userAccountControl, self.dc1_guid, None),
930             (DRSUAPI_ATTID_codePage, self.dc1_guid, 1),
931             (DRSUAPI_ATTID_countryCode, self.dc1_guid, 1),
932             (DRSUAPI_ATTID_dBCSPwd, self.dc1_guid, 1),
933             (DRSUAPI_ATTID_logonHours, self.dc1_guid, 1),
934             (DRSUAPI_ATTID_unicodePwd, self.dc1_guid, 1),
935             (DRSUAPI_ATTID_ntPwdHistory, self.dc1_guid, 1),
936             (DRSUAPI_ATTID_pwdLastSet, self.dc1_guid, 1),
937             (DRSUAPI_ATTID_primaryGroupID, self.dc1_guid, 1),
938             (DRSUAPI_ATTID_objectSid, self.dc1_guid, 1),
939             (DRSUAPI_ATTID_accountExpires, self.dc1_guid, 1),
940             (DRSUAPI_ATTID_lmPwdHistory, self.dc1_guid, 1),
941             (DRSUAPI_ATTID_sAMAccountName, self.dc1_guid, 1),
942             (DRSUAPI_ATTID_sAMAccountType, self.dc1_guid, 1),
943             (DRSUAPI_ATTID_userPrincipalName, self.dc1_guid, 1),
944             (DRSUAPI_ATTID_objectCategory, self.dc1_guid, 1)]
945
946         self._check_obj(sam_ldb=self.ldb_dc1, drs=self.drs_dc1,
947                         obj_orig=user_orig, is_deleted=False,
948                         expected_metadata=initial_metadata)
949
950         new_dn = ldb.Dn(self.ldb_dc1, "CN=%s" % username)
951         new_dn.add_base(self.ou2_dn)
952         self.ldb_dc1.rename(user_dn, new_dn)
953         ldb_res = self.ldb_dc1.search(base=self.ou2_dn,
954                                       scope=SCOPE_SUBTREE,
955                                       expression="(samAccountName=%s)" % username,
956                                       attrs=["*", "parentGUID"])
957         self.assertEquals(len(ldb_res), 1)
958
959         user_moved_orig = ldb_res[0]
960         user_moved_dn   = ldb_res[0]["dn"]
961
962         # trigger replication from DC2 (Which has never seen the object) to DC1
963         self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True)
964         moved_metadata = [
965             (DRSUAPI_ATTID_objectClass, self.dc1_guid, 1),
966             (DRSUAPI_ATTID_cn, self.dc1_guid, 1),
967             (DRSUAPI_ATTID_instanceType, self.dc1_guid, 1),
968             (DRSUAPI_ATTID_whenCreated, self.dc1_guid, 1),
969             (DRSUAPI_ATTID_ntSecurityDescriptor, self.dc1_guid, 1),
970             (DRSUAPI_ATTID_name, self.dc1_guid, 2),
971             (DRSUAPI_ATTID_userAccountControl, self.dc1_guid, None),
972             (DRSUAPI_ATTID_codePage, self.dc1_guid, 1),
973             (DRSUAPI_ATTID_countryCode, self.dc1_guid, 1),
974             (DRSUAPI_ATTID_dBCSPwd, self.dc1_guid, 1),
975             (DRSUAPI_ATTID_logonHours, self.dc1_guid, 1),
976             (DRSUAPI_ATTID_unicodePwd, self.dc1_guid, 1),
977             (DRSUAPI_ATTID_ntPwdHistory, self.dc1_guid, 1),
978             (DRSUAPI_ATTID_pwdLastSet, self.dc1_guid, 1),
979             (DRSUAPI_ATTID_primaryGroupID, self.dc1_guid, 1),
980             (DRSUAPI_ATTID_objectSid, self.dc1_guid, 1),
981             (DRSUAPI_ATTID_accountExpires, self.dc1_guid, 1),
982             (DRSUAPI_ATTID_lmPwdHistory, self.dc1_guid, 1),
983             (DRSUAPI_ATTID_sAMAccountName, self.dc1_guid, 1),
984             (DRSUAPI_ATTID_sAMAccountType, self.dc1_guid, 1),
985             (DRSUAPI_ATTID_userPrincipalName, self.dc1_guid, 1),
986             (DRSUAPI_ATTID_objectCategory, self.dc1_guid, 1)]
987
988         # check user info on DC1 after rename - should be valid user
989         user_cur = self._check_obj(sam_ldb=self.ldb_dc1, drs=self.drs_dc1,
990                                    obj_orig=user_moved_orig,
991                                    is_deleted=False,
992                                    expected_metadata=moved_metadata)
993
994         # delete user on DC1
995         self.ldb_dc1.delete('<GUID=%s>' % self._GUID_string(user_orig["objectGUID"][0]))
996         deleted_metadata_dc1 = [
997             (DRSUAPI_ATTID_objectClass, self.dc1_guid, 1),
998             (DRSUAPI_ATTID_cn, self.dc1_guid, 2),
999             (DRSUAPI_ATTID_instanceType, self.dc1_guid, 1),
1000             (DRSUAPI_ATTID_whenCreated, self.dc1_guid, 1),
1001             (DRSUAPI_ATTID_isDeleted, self.dc1_guid, 1),
1002             (DRSUAPI_ATTID_ntSecurityDescriptor, self.dc1_guid, 1),
1003             (DRSUAPI_ATTID_name, self.dc1_guid, 3),
1004             (DRSUAPI_ATTID_userAccountControl, self.dc1_guid, None),
1005             (DRSUAPI_ATTID_codePage, self.dc1_guid, 2),
1006             (DRSUAPI_ATTID_countryCode, self.dc1_guid, 2),
1007             (DRSUAPI_ATTID_dBCSPwd, self.dc1_guid, 1),
1008             (DRSUAPI_ATTID_logonHours, self.dc1_guid, 1),
1009             (DRSUAPI_ATTID_unicodePwd, self.dc1_guid, 1),
1010             (DRSUAPI_ATTID_ntPwdHistory, self.dc1_guid, 1),
1011             (DRSUAPI_ATTID_pwdLastSet, self.dc1_guid, 2),
1012             (DRSUAPI_ATTID_primaryGroupID, self.dc1_guid, 2),
1013             (DRSUAPI_ATTID_objectSid, self.dc1_guid, 1),
1014             (DRSUAPI_ATTID_accountExpires, self.dc1_guid, 2),
1015             (DRSUAPI_ATTID_lmPwdHistory, self.dc1_guid, 1),
1016             (DRSUAPI_ATTID_sAMAccountName, self.dc1_guid, 1),
1017             (DRSUAPI_ATTID_sAMAccountType, self.dc1_guid, 2),
1018             (DRSUAPI_ATTID_userPrincipalName, self.dc1_guid, 2),
1019             (DRSUAPI_ATTID_lastKnownParent, self.dc1_guid, 1),
1020             (DRSUAPI_ATTID_objectCategory, self.dc1_guid, 2),
1021             (DRSUAPI_ATTID_isRecycled, self.dc1_guid, 1)]
1022
1023         # check user info on DC1 - should be deleted user
1024         user_cur = self._check_obj(sam_ldb=self.ldb_dc1, drs=self.drs_dc1,
1025                                    obj_orig=user_moved_orig,
1026                                    is_deleted=True,
1027                                    expected_metadata=deleted_metadata_dc1)
1028
1029         # trigger replication from DC2 to DC1
1030         self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True)
1031
1032         # check user info on DC1 - should be deleted user
1033         user_cur = self._check_obj(sam_ldb=self.ldb_dc1, drs=self.drs_dc1,
1034                                    obj_orig=user_moved_orig,
1035                                    is_deleted=True,
1036                                    expected_metadata=deleted_metadata_dc1)
1037
1038         # trigger replication from DC1 to DC2, for cleanup
1039         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True)
1040
1041         deleted_metadata_dc2 = [
1042             (DRSUAPI_ATTID_objectClass, self.dc1_guid, 1),
1043             (DRSUAPI_ATTID_cn, self.dc2_guid, 1),
1044             (DRSUAPI_ATTID_instanceType, self.dc1_guid, 1),
1045             (DRSUAPI_ATTID_whenCreated, self.dc1_guid, 1),
1046             (DRSUAPI_ATTID_isDeleted, self.dc1_guid, 1),
1047             (DRSUAPI_ATTID_ntSecurityDescriptor, self.dc1_guid, 1),
1048             (DRSUAPI_ATTID_name, self.dc1_guid, 3),
1049             (DRSUAPI_ATTID_userAccountControl, self.dc1_guid, None),
1050             (DRSUAPI_ATTID_codePage, self.dc1_guid, 2),
1051             (DRSUAPI_ATTID_countryCode, self.dc1_guid, 2),
1052             (DRSUAPI_ATTID_dBCSPwd, self.dc1_guid, 1),
1053             (DRSUAPI_ATTID_logonHours, self.dc1_guid, 1),
1054             (DRSUAPI_ATTID_unicodePwd, self.dc1_guid, 1),
1055             (DRSUAPI_ATTID_ntPwdHistory, self.dc1_guid, 1),
1056             (DRSUAPI_ATTID_pwdLastSet, self.dc1_guid, 2),
1057             (DRSUAPI_ATTID_primaryGroupID, self.dc1_guid, 2),
1058             (DRSUAPI_ATTID_objectSid, self.dc1_guid, 1),
1059             (DRSUAPI_ATTID_accountExpires, self.dc1_guid, 2),
1060             (DRSUAPI_ATTID_lmPwdHistory, self.dc1_guid, 1),
1061             (DRSUAPI_ATTID_sAMAccountName, self.dc1_guid, 1),
1062             (DRSUAPI_ATTID_sAMAccountType, self.dc1_guid, 2),
1063             (DRSUAPI_ATTID_userPrincipalName, self.dc1_guid, 2),
1064             (DRSUAPI_ATTID_lastKnownParent, self.dc1_guid, 1),
1065             (DRSUAPI_ATTID_objectCategory, self.dc1_guid, 2),
1066             (DRSUAPI_ATTID_isRecycled, self.dc1_guid, 1)]
1067
1068         # check user info on DC2 - should be deleted user
1069         user_cur = self._check_obj(sam_ldb=self.ldb_dc2, drs=self.drs_dc2,
1070                                    obj_orig=user_moved_orig,
1071                                    is_deleted=True,
1072                                    expected_metadata=deleted_metadata_dc2)
1073
1074     def test_ReplicateMoveObject4(self):
1075         """Verifies how a moved container with a user inside is replicated between two DCs.
1076            This test should verify that:
1077             - the OU is replicated properly
1078             - the user is modified on DC2
1079             - the OU is renamed on DC1
1080             - We verify that after replication DC1 -> DC2,
1081               that the user has the correct DN (under OU2), and the description
1082
1083            """
1084         # work-out unique username to test with
1085         username = self._make_username()
1086
1087         # create user on DC1
1088         self.ldb_dc1.newuser(username=username,
1089                              userou="ou=%s,ou=%s"
1090                              % (self.ou1_dn.get_component_value(0),
1091                                 self.top_ou.get_component_value(0)),
1092                              password=None, setpassword=False)
1093         ldb_res = self.ldb_dc1.search(base=self.ou1_dn,
1094                                       scope=SCOPE_SUBTREE,
1095                                       expression="(samAccountName=%s)" % username,
1096                                       attrs=["*", "parentGUID"])
1097         self.assertEquals(len(ldb_res), 1)
1098         user_orig = ldb_res[0]
1099         user_dn   = ldb_res[0]["dn"]
1100
1101         # check user info on DC1
1102         print("Testing for %s with GUID %s" % (username, self._GUID_string(user_orig["objectGUID"][0])))
1103         initial_metadata = [
1104             (DRSUAPI_ATTID_objectClass, self.dc1_guid, 1),
1105             (DRSUAPI_ATTID_cn, self.dc1_guid, 1),
1106             (DRSUAPI_ATTID_instanceType, self.dc1_guid, 1),
1107             (DRSUAPI_ATTID_whenCreated, self.dc1_guid, 1),
1108             (DRSUAPI_ATTID_ntSecurityDescriptor, self.dc1_guid, 1),
1109             (DRSUAPI_ATTID_name, self.dc1_guid, 1),
1110             (DRSUAPI_ATTID_userAccountControl, self.dc1_guid, None),
1111             (DRSUAPI_ATTID_codePage, self.dc1_guid, 1),
1112             (DRSUAPI_ATTID_countryCode, self.dc1_guid, 1),
1113             (DRSUAPI_ATTID_dBCSPwd, self.dc1_guid, 1),
1114             (DRSUAPI_ATTID_logonHours, self.dc1_guid, 1),
1115             (DRSUAPI_ATTID_unicodePwd, self.dc1_guid, 1),
1116             (DRSUAPI_ATTID_ntPwdHistory, self.dc1_guid, 1),
1117             (DRSUAPI_ATTID_pwdLastSet, self.dc1_guid, 1),
1118             (DRSUAPI_ATTID_primaryGroupID, self.dc1_guid, 1),
1119             (DRSUAPI_ATTID_objectSid, self.dc1_guid, 1),
1120             (DRSUAPI_ATTID_accountExpires, self.dc1_guid, 1),
1121             (DRSUAPI_ATTID_lmPwdHistory, self.dc1_guid, 1),
1122             (DRSUAPI_ATTID_sAMAccountName, self.dc1_guid, 1),
1123             (DRSUAPI_ATTID_sAMAccountType, self.dc1_guid, 1),
1124             (DRSUAPI_ATTID_userPrincipalName, self.dc1_guid, 1),
1125             (DRSUAPI_ATTID_objectCategory, self.dc1_guid, 1)]
1126
1127         self._check_obj(sam_ldb=self.ldb_dc1, drs=self.drs_dc1,
1128                         obj_orig=user_orig, is_deleted=False,
1129                         expected_metadata=initial_metadata)
1130
1131         # trigger replication from DC1 to DC2
1132         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True)
1133
1134         initial_metadata_dc2 = [
1135             (DRSUAPI_ATTID_objectClass, self.dc1_guid, 1),
1136             (DRSUAPI_ATTID_cn, self.dc2_guid, 1),
1137             (DRSUAPI_ATTID_instanceType, self.dc1_guid, 1),
1138             (DRSUAPI_ATTID_whenCreated, self.dc1_guid, 1),
1139             (DRSUAPI_ATTID_ntSecurityDescriptor, self.dc1_guid, 1),
1140             (DRSUAPI_ATTID_name, self.dc1_guid, 1),
1141             (DRSUAPI_ATTID_userAccountControl, self.dc1_guid, None),
1142             (DRSUAPI_ATTID_codePage, self.dc1_guid, 1),
1143             (DRSUAPI_ATTID_countryCode, self.dc1_guid, 1),
1144             (DRSUAPI_ATTID_dBCSPwd, self.dc1_guid, 1),
1145             (DRSUAPI_ATTID_logonHours, self.dc1_guid, 1),
1146             (DRSUAPI_ATTID_unicodePwd, self.dc1_guid, 1),
1147             (DRSUAPI_ATTID_ntPwdHistory, self.dc1_guid, 1),
1148             (DRSUAPI_ATTID_pwdLastSet, self.dc1_guid, 1),
1149             (DRSUAPI_ATTID_primaryGroupID, self.dc1_guid, 1),
1150             (DRSUAPI_ATTID_objectSid, self.dc1_guid, 1),
1151             (DRSUAPI_ATTID_accountExpires, self.dc1_guid, 1),
1152             (DRSUAPI_ATTID_lmPwdHistory, self.dc1_guid, 1),
1153             (DRSUAPI_ATTID_sAMAccountName, self.dc1_guid, 1),
1154             (DRSUAPI_ATTID_sAMAccountType, self.dc1_guid, 1),
1155             (DRSUAPI_ATTID_userPrincipalName, self.dc1_guid, 1),
1156             (DRSUAPI_ATTID_objectCategory, self.dc1_guid, 1)]
1157
1158         # check user info on DC2 - should still be valid user
1159         user_cur = self._check_obj(sam_ldb=self.ldb_dc2, drs=self.drs_dc2,
1160                                    obj_orig=user_orig, is_deleted=False,
1161                                    expected_metadata=initial_metadata_dc2)
1162
1163         new_dn = ldb.Dn(self.ldb_dc1, "CN=%s" % username)
1164         new_dn.add_base(self.ou2_dn)
1165         self.ldb_dc1.rename(user_dn, new_dn)
1166         ldb_res = self.ldb_dc1.search(base=self.ou2_dn,
1167                                       scope=SCOPE_SUBTREE,
1168                                       expression="(samAccountName=%s)" % username,
1169                                       attrs=["*", "parentGUID"])
1170         self.assertEquals(len(ldb_res), 1)
1171
1172         user_moved_orig = ldb_res[0]
1173         user_moved_dn   = ldb_res[0]["dn"]
1174
1175         moved_metadata = [
1176             (DRSUAPI_ATTID_objectClass, self.dc1_guid, 1),
1177             (DRSUAPI_ATTID_cn, self.dc1_guid, 1),
1178             (DRSUAPI_ATTID_instanceType, self.dc1_guid, 1),
1179             (DRSUAPI_ATTID_whenCreated, self.dc1_guid, 1),
1180             (DRSUAPI_ATTID_ntSecurityDescriptor, self.dc1_guid, 1),
1181             (DRSUAPI_ATTID_name, self.dc1_guid, 2),
1182             (DRSUAPI_ATTID_userAccountControl, self.dc1_guid, None),
1183             (DRSUAPI_ATTID_codePage, self.dc1_guid, 1),
1184             (DRSUAPI_ATTID_countryCode, self.dc1_guid, 1),
1185             (DRSUAPI_ATTID_dBCSPwd, self.dc1_guid, 1),
1186             (DRSUAPI_ATTID_logonHours, self.dc1_guid, 1),
1187             (DRSUAPI_ATTID_unicodePwd, self.dc1_guid, 1),
1188             (DRSUAPI_ATTID_ntPwdHistory, self.dc1_guid, 1),
1189             (DRSUAPI_ATTID_pwdLastSet, self.dc1_guid, 1),
1190             (DRSUAPI_ATTID_primaryGroupID, self.dc1_guid, 1),
1191             (DRSUAPI_ATTID_objectSid, self.dc1_guid, 1),
1192             (DRSUAPI_ATTID_accountExpires, self.dc1_guid, 1),
1193             (DRSUAPI_ATTID_lmPwdHistory, self.dc1_guid, 1),
1194             (DRSUAPI_ATTID_sAMAccountName, self.dc1_guid, 1),
1195             (DRSUAPI_ATTID_sAMAccountType, self.dc1_guid, 1),
1196             (DRSUAPI_ATTID_userPrincipalName, self.dc1_guid, 1),
1197             (DRSUAPI_ATTID_objectCategory, self.dc1_guid, 1)]
1198
1199         # check user info on DC1 after rename - should be valid user
1200         user_cur = self._check_obj(sam_ldb=self.ldb_dc1, drs=self.drs_dc1,
1201                                    obj_orig=user_moved_orig,
1202                                    is_deleted=False,
1203                                    expected_metadata=moved_metadata)
1204
1205         # Modify description on DC2.  This triggers a replication, but
1206         # not of 'name' and so a bug in Samba regarding the DN.
1207         msg = ldb.Message()
1208         msg.dn = user_dn
1209         msg["description"] = ldb.MessageElement("User Description", ldb.FLAG_MOD_REPLACE, "description")
1210         self.ldb_dc2.modify(msg)
1211
1212         modified_metadata = [
1213             (DRSUAPI_ATTID_objectClass, self.dc1_guid, 1),
1214             (DRSUAPI_ATTID_cn, self.dc2_guid, 1),
1215             (DRSUAPI_ATTID_description, self.dc2_guid, 1),
1216             (DRSUAPI_ATTID_instanceType, self.dc1_guid, 1),
1217             (DRSUAPI_ATTID_whenCreated, self.dc1_guid, 1),
1218             (DRSUAPI_ATTID_ntSecurityDescriptor, self.dc1_guid, 1),
1219             (DRSUAPI_ATTID_name, self.dc1_guid, 1),
1220             (DRSUAPI_ATTID_userAccountControl, self.dc1_guid, None),
1221             (DRSUAPI_ATTID_codePage, self.dc1_guid, 1),
1222             (DRSUAPI_ATTID_countryCode, self.dc1_guid, 1),
1223             (DRSUAPI_ATTID_dBCSPwd, self.dc1_guid, 1),
1224             (DRSUAPI_ATTID_logonHours, self.dc1_guid, 1),
1225             (DRSUAPI_ATTID_unicodePwd, self.dc1_guid, 1),
1226             (DRSUAPI_ATTID_ntPwdHistory, self.dc1_guid, 1),
1227             (DRSUAPI_ATTID_pwdLastSet, self.dc1_guid, 1),
1228             (DRSUAPI_ATTID_primaryGroupID, self.dc1_guid, 1),
1229             (DRSUAPI_ATTID_objectSid, self.dc1_guid, 1),
1230             (DRSUAPI_ATTID_accountExpires, self.dc1_guid, 1),
1231             (DRSUAPI_ATTID_lmPwdHistory, self.dc1_guid, 1),
1232             (DRSUAPI_ATTID_sAMAccountName, self.dc1_guid, 1),
1233             (DRSUAPI_ATTID_sAMAccountType, self.dc1_guid, 1),
1234             (DRSUAPI_ATTID_userPrincipalName, self.dc1_guid, 1),
1235             (DRSUAPI_ATTID_objectCategory, self.dc1_guid, 1)]
1236
1237         user_cur = self._check_obj(sam_ldb=self.ldb_dc2, drs=self.drs_dc2,
1238                                    obj_orig=user_orig,
1239                                    is_deleted=False,
1240                                    expected_metadata=modified_metadata)
1241
1242         # trigger replication from DC1 to DC2
1243         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True)
1244
1245         modified_renamed_metadata = [
1246             (DRSUAPI_ATTID_objectClass, self.dc1_guid, 1),
1247             (DRSUAPI_ATTID_cn, self.dc2_guid, 2),
1248             (DRSUAPI_ATTID_description, self.dc2_guid, 1),
1249             (DRSUAPI_ATTID_instanceType, self.dc1_guid, 1),
1250             (DRSUAPI_ATTID_whenCreated, self.dc1_guid, 1),
1251             (DRSUAPI_ATTID_ntSecurityDescriptor, self.dc1_guid, 1),
1252             (DRSUAPI_ATTID_name, self.dc1_guid, 2),
1253             (DRSUAPI_ATTID_userAccountControl, self.dc1_guid, None),
1254             (DRSUAPI_ATTID_codePage, self.dc1_guid, 1),
1255             (DRSUAPI_ATTID_countryCode, self.dc1_guid, 1),
1256             (DRSUAPI_ATTID_dBCSPwd, self.dc1_guid, 1),
1257             (DRSUAPI_ATTID_logonHours, self.dc1_guid, 1),
1258             (DRSUAPI_ATTID_unicodePwd, self.dc1_guid, 1),
1259             (DRSUAPI_ATTID_ntPwdHistory, self.dc1_guid, 1),
1260             (DRSUAPI_ATTID_pwdLastSet, self.dc1_guid, 1),
1261             (DRSUAPI_ATTID_primaryGroupID, self.dc1_guid, 1),
1262             (DRSUAPI_ATTID_objectSid, self.dc1_guid, 1),
1263             (DRSUAPI_ATTID_accountExpires, self.dc1_guid, 1),
1264             (DRSUAPI_ATTID_lmPwdHistory, self.dc1_guid, 1),
1265             (DRSUAPI_ATTID_sAMAccountName, self.dc1_guid, 1),
1266             (DRSUAPI_ATTID_sAMAccountType, self.dc1_guid, 1),
1267             (DRSUAPI_ATTID_userPrincipalName, self.dc1_guid, 1),
1268             (DRSUAPI_ATTID_objectCategory, self.dc1_guid, 1)]
1269
1270         # check user info on DC2 - should still be valid user
1271         user_cur = self._check_obj(sam_ldb=self.ldb_dc2, drs=self.drs_dc2,
1272                                    obj_orig=user_moved_orig,
1273                                    is_deleted=False,
1274                                    expected_metadata=modified_renamed_metadata)
1275
1276         self.assertTrue("description" in user_cur)
1277
1278         # delete user on DC1
1279         self.ldb_dc1.delete('<GUID=%s>' % self._GUID_string(user_orig["objectGUID"][0]))
1280         deleted_metadata_dc1 = [
1281             (DRSUAPI_ATTID_objectClass, self.dc1_guid, 1),
1282             (DRSUAPI_ATTID_cn, self.dc1_guid, 2),
1283             (DRSUAPI_ATTID_instanceType, self.dc1_guid, 1),
1284             (DRSUAPI_ATTID_whenCreated, self.dc1_guid, 1),
1285             (DRSUAPI_ATTID_isDeleted, self.dc1_guid, 1),
1286             (DRSUAPI_ATTID_ntSecurityDescriptor, self.dc1_guid, 1),
1287             (DRSUAPI_ATTID_name, self.dc1_guid, 3),
1288             (DRSUAPI_ATTID_userAccountControl, self.dc1_guid, None),
1289             (DRSUAPI_ATTID_codePage, self.dc1_guid, 2),
1290             (DRSUAPI_ATTID_countryCode, self.dc1_guid, 2),
1291             (DRSUAPI_ATTID_dBCSPwd, self.dc1_guid, 1),
1292             (DRSUAPI_ATTID_logonHours, self.dc1_guid, 1),
1293             (DRSUAPI_ATTID_unicodePwd, self.dc1_guid, 1),
1294             (DRSUAPI_ATTID_ntPwdHistory, self.dc1_guid, 1),
1295             (DRSUAPI_ATTID_pwdLastSet, self.dc1_guid, 2),
1296             (DRSUAPI_ATTID_primaryGroupID, self.dc1_guid, 2),
1297             (DRSUAPI_ATTID_objectSid, self.dc1_guid, 1),
1298             (DRSUAPI_ATTID_accountExpires, self.dc1_guid, 2),
1299             (DRSUAPI_ATTID_lmPwdHistory, self.dc1_guid, 1),
1300             (DRSUAPI_ATTID_sAMAccountName, self.dc1_guid, 1),
1301             (DRSUAPI_ATTID_sAMAccountType, self.dc1_guid, 2),
1302             (DRSUAPI_ATTID_userPrincipalName, self.dc1_guid, 2),
1303             (DRSUAPI_ATTID_lastKnownParent, self.dc1_guid, 1),
1304             (DRSUAPI_ATTID_objectCategory, self.dc1_guid, 2),
1305             (DRSUAPI_ATTID_isRecycled, self.dc1_guid, 1)]
1306
1307         # check user info on DC1 - should be deleted user
1308         user_cur = self._check_obj(sam_ldb=self.ldb_dc1, drs=self.drs_dc1,
1309                                    obj_orig=user_moved_orig,
1310                                    is_deleted=True,
1311                                    expected_metadata=deleted_metadata_dc1)
1312
1313         # trigger replication from DC2 to DC1
1314         self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True)
1315         # check user info on DC2 - should still be valid user
1316         user_cur = self._check_obj(sam_ldb=self.ldb_dc2, drs=self.drs_dc2,
1317                                    obj_orig=user_moved_orig,
1318                                    is_deleted=False,
1319                                    expected_metadata=modified_renamed_metadata)
1320
1321         self.assertTrue("description" in user_cur)
1322
1323         deleted_metadata_dc1 = [
1324             (DRSUAPI_ATTID_objectClass, self.dc1_guid, 1),
1325             (DRSUAPI_ATTID_cn, self.dc1_guid, 2),
1326             (DRSUAPI_ATTID_description, self.dc1_guid, 2),
1327             (DRSUAPI_ATTID_instanceType, self.dc1_guid, 1),
1328             (DRSUAPI_ATTID_whenCreated, self.dc1_guid, 1),
1329             (DRSUAPI_ATTID_isDeleted, self.dc1_guid, 1),
1330             (DRSUAPI_ATTID_ntSecurityDescriptor, self.dc1_guid, 1),
1331             (DRSUAPI_ATTID_name, self.dc1_guid, 3),
1332             (DRSUAPI_ATTID_userAccountControl, self.dc1_guid, None),
1333             (DRSUAPI_ATTID_codePage, self.dc1_guid, 2),
1334             (DRSUAPI_ATTID_countryCode, self.dc1_guid, 2),
1335             (DRSUAPI_ATTID_dBCSPwd, self.dc1_guid, 1),
1336             (DRSUAPI_ATTID_logonHours, self.dc1_guid, 1),
1337             (DRSUAPI_ATTID_unicodePwd, self.dc1_guid, 1),
1338             (DRSUAPI_ATTID_ntPwdHistory, self.dc1_guid, 1),
1339             (DRSUAPI_ATTID_pwdLastSet, self.dc1_guid, 2),
1340             (DRSUAPI_ATTID_primaryGroupID, self.dc1_guid, 2),
1341             (DRSUAPI_ATTID_objectSid, self.dc1_guid, 1),
1342             (DRSUAPI_ATTID_accountExpires, self.dc1_guid, 2),
1343             (DRSUAPI_ATTID_lmPwdHistory, self.dc1_guid, 1),
1344             (DRSUAPI_ATTID_sAMAccountName, self.dc1_guid, 1),
1345             (DRSUAPI_ATTID_sAMAccountType, self.dc1_guid, 2),
1346             (DRSUAPI_ATTID_userPrincipalName, self.dc1_guid, 2),
1347             (DRSUAPI_ATTID_lastKnownParent, self.dc1_guid, 1),
1348             (DRSUAPI_ATTID_objectCategory, self.dc1_guid, 2),
1349             (DRSUAPI_ATTID_isRecycled, self.dc1_guid, 1)]
1350
1351         # check user info on DC1 - should be deleted user
1352         user_cur = self._check_obj(sam_ldb=self.ldb_dc1, drs=self.drs_dc1,
1353                                    obj_orig=user_moved_orig,
1354                                    is_deleted=True,
1355                                    expected_metadata=deleted_metadata_dc1)
1356
1357         self.assertFalse("description" in user_cur)
1358
1359         # trigger replication from DC1 to DC2, for cleanup
1360         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True)
1361
1362         deleted_metadata_dc2 = [
1363             (DRSUAPI_ATTID_objectClass, self.dc1_guid, 1),
1364             (DRSUAPI_ATTID_cn, self.dc2_guid, 3),
1365             (DRSUAPI_ATTID_description, self.dc1_guid, 2),
1366             (DRSUAPI_ATTID_instanceType, self.dc1_guid, 1),
1367             (DRSUAPI_ATTID_whenCreated, self.dc1_guid, 1),
1368             (DRSUAPI_ATTID_isDeleted, self.dc1_guid, 1),
1369             (DRSUAPI_ATTID_ntSecurityDescriptor, self.dc1_guid, 1),
1370             (DRSUAPI_ATTID_name, self.dc1_guid, 3),
1371             (DRSUAPI_ATTID_userAccountControl, self.dc1_guid, None),
1372             (DRSUAPI_ATTID_codePage, self.dc1_guid, 2),
1373             (DRSUAPI_ATTID_countryCode, self.dc1_guid, 2),
1374             (DRSUAPI_ATTID_dBCSPwd, self.dc1_guid, 1),
1375             (DRSUAPI_ATTID_logonHours, self.dc1_guid, 1),
1376             (DRSUAPI_ATTID_unicodePwd, self.dc1_guid, 1),
1377             (DRSUAPI_ATTID_ntPwdHistory, self.dc1_guid, 1),
1378             (DRSUAPI_ATTID_pwdLastSet, self.dc1_guid, 2),
1379             (DRSUAPI_ATTID_primaryGroupID, self.dc1_guid, 2),
1380             (DRSUAPI_ATTID_objectSid, self.dc1_guid, 1),
1381             (DRSUAPI_ATTID_accountExpires, self.dc1_guid, 2),
1382             (DRSUAPI_ATTID_lmPwdHistory, self.dc1_guid, 1),
1383             (DRSUAPI_ATTID_sAMAccountName, self.dc1_guid, 1),
1384             (DRSUAPI_ATTID_sAMAccountType, self.dc1_guid, 2),
1385             (DRSUAPI_ATTID_userPrincipalName, self.dc1_guid, 2),
1386             (DRSUAPI_ATTID_lastKnownParent, self.dc1_guid, 1),
1387             (DRSUAPI_ATTID_objectCategory, self.dc1_guid, 2),
1388             (DRSUAPI_ATTID_isRecycled, self.dc1_guid, 1)]
1389
1390         # check user info on DC2 - should be deleted user
1391         user_cur = self._check_obj(sam_ldb=self.ldb_dc2, drs=self.drs_dc2,
1392                                    obj_orig=user_moved_orig,
1393                                    is_deleted=True,
1394                                    expected_metadata=deleted_metadata_dc2)
1395
1396         self.assertFalse("description" in user_cur)
1397
1398     def test_ReplicateMoveObject5(self):
1399         """Verifies how a moved container with a user inside is replicated between two DCs.
1400            This test should verify that:
1401             - the OU is replicated properly
1402             - the user is modified on DC2
1403             - the OU is renamed on DC1
1404             - We verify that after replication DC2 -> DC1,
1405               that the user has the correct DN (under OU2), and the description
1406
1407            """
1408         # work-out unique username to test with
1409         username = self._make_username()
1410
1411         # create user on DC1
1412         self.ldb_dc1.newuser(username=username,
1413                              userou="ou=%s,ou=%s"
1414                              % (self.ou1_dn.get_component_value(0),
1415                                 self.top_ou.get_component_value(0)),
1416                              password=None, setpassword=False)
1417         ldb_res = self.ldb_dc1.search(base=self.ou1_dn,
1418                                       scope=SCOPE_SUBTREE,
1419                                       expression="(samAccountName=%s)" % username,
1420                                       attrs=["*", "parentGUID"])
1421         self.assertEquals(len(ldb_res), 1)
1422         user_orig = ldb_res[0]
1423         user_dn   = ldb_res[0]["dn"]
1424
1425         # trigger replication from DC1 to DC2
1426         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True)
1427         # check user info on DC2 - should still be valid user
1428         user_cur = self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=user_orig, is_deleted=False)
1429
1430         # check user info on DC1
1431         print("Testing for %s with GUID %s" % (username, self._GUID_string(user_orig["objectGUID"][0])))
1432         self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=user_orig, is_deleted=False)
1433
1434         new_dn = ldb.Dn(self.ldb_dc1, "CN=%s" % username)
1435         new_dn.add_base(self.ou2_dn)
1436         self.ldb_dc1.rename(user_dn, new_dn)
1437         ldb_res = self.ldb_dc1.search(base=self.ou2_dn,
1438                                       scope=SCOPE_SUBTREE,
1439                                       expression="(samAccountName=%s)" % username,
1440                                       attrs=["*", "parentGUID"])
1441         self.assertEquals(len(ldb_res), 1)
1442
1443         user_moved_orig = ldb_res[0]
1444         user_moved_dn   = ldb_res[0]["dn"]
1445
1446         # Modify description on DC2.  This triggers a replication, but
1447         # not of 'name' and so a bug in Samba regarding the DN.
1448         msg = ldb.Message()
1449         msg.dn = user_dn
1450         msg["description"] = ldb.MessageElement("User Description", ldb.FLAG_MOD_REPLACE, "description")
1451         self.ldb_dc2.modify(msg)
1452
1453         # trigger replication from DC2 to DC1
1454         self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True)
1455         # check user info on DC1 - should still be valid user
1456         user_cur = self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=user_moved_orig, is_deleted=False)
1457         self.assertTrue("description" in user_cur)
1458
1459         # trigger replication from DC1 to DC2
1460         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True)
1461         user_cur = self._check_obj(sam_ldb=self.ldb_dc2, obj_orig=user_moved_orig, is_deleted=False)
1462         self.assertTrue("description" in user_cur)
1463
1464         # delete user on DC2
1465         self.ldb_dc2.delete('<GUID=%s>' % self._GUID_string(user_orig["objectGUID"][0]))
1466         # trigger replication from DC2 to DC1 for cleanup
1467         self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True)
1468
1469         # check user info on DC1 - should be deleted user
1470         user_cur = self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=user_moved_orig, is_deleted=True)
1471         self.assertFalse("description" in user_cur)
1472
1473     def test_ReplicateMoveObject6(self):
1474         """Verifies how a moved container is replicated between two DCs.
1475            This test should verify that:
1476             - the OU1 is replicated properly
1477             - the OU1 is modified on DC2
1478             - the OU1 is renamed on DC1
1479             - We verify that after replication DC1 -> DC2,
1480               that the OU1 has the correct DN (under OU2), and the description
1481
1482            """
1483         ldb_res = self.ldb_dc1.search(base=self.ou1_dn,
1484                                       scope=SCOPE_BASE,
1485                                       attrs=["*", "parentGUID"])
1486         self.assertEquals(len(ldb_res), 1)
1487         ou_orig = ldb_res[0]
1488         ou_dn   = ldb_res[0]["dn"]
1489
1490         # check user info on DC1
1491         print("Testing for %s with GUID %s" % (self.ou1_dn, self._GUID_string(ou_orig["objectGUID"][0])))
1492         self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=ou_orig, is_deleted=False)
1493
1494         # trigger replication from DC1 to DC2
1495         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True)
1496         # check user info on DC2 - should still be valid user
1497         ou_cur = self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=ou_orig, is_deleted=False)
1498
1499         new_dn = ldb.Dn(self.ldb_dc1, "OU=%s" % self.ou1_dn.get_component_value(0))
1500         new_dn.add_base(self.ou2_dn)
1501         self.ldb_dc1.rename(ou_dn, new_dn)
1502         ldb_res = self.ldb_dc1.search(base=new_dn,
1503                                       scope=SCOPE_BASE,
1504                                       attrs=["*", "parentGUID"])
1505         self.assertEquals(len(ldb_res), 1)
1506
1507         ou_moved_orig = ldb_res[0]
1508         ou_moved_dn   = ldb_res[0]["dn"]
1509
1510         # Modify description on DC2.  This triggers a replication, but
1511         # not of 'name' and so a bug in Samba regarding the DN.
1512         msg = ldb.Message()
1513         msg.dn = ou_dn
1514         msg["description"] = ldb.MessageElement("OU Description", ldb.FLAG_MOD_REPLACE, "description")
1515         self.ldb_dc2.modify(msg)
1516
1517         # trigger replication from DC1 to DC2
1518         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True)
1519         # check user info on DC2 - should still be valid user
1520         ou_cur = self._check_obj(sam_ldb=self.ldb_dc2, obj_orig=ou_moved_orig, is_deleted=False)
1521         self.assertTrue("description" in ou_cur)
1522
1523         # delete OU on DC1
1524         self.ldb_dc1.delete('<GUID=%s>' % self._GUID_string(ou_orig["objectGUID"][0]))
1525         # trigger replication from DC2 to DC1
1526         self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True)
1527         # check user info on DC2 - should be deleted user
1528         ou_cur = self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=ou_moved_orig, is_deleted=True)
1529         self.assertFalse("description" in ou_cur)
1530
1531         # trigger replication from DC1 to DC2, for cleanup
1532         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True)
1533
1534         # check user info on DC2 - should be deleted user
1535         ou_cur = self._check_obj(sam_ldb=self.ldb_dc2, obj_orig=ou_moved_orig, is_deleted=True)
1536         self.assertFalse("description" in ou_cur)
1537
1538     def test_ReplicateMoveObject7(self):
1539         """Verifies how a moved container is replicated between two DCs.
1540            This test should verify that:
1541             - the OU1 is replicated properly
1542             - the OU1 is modified on DC2
1543             - the OU1 is renamed on DC1 to be under OU2
1544             - We verify that after replication DC2 -> DC1,
1545               that the OU1 has the correct DN (under OU2), and the description
1546
1547            """
1548         ldb_res = self.ldb_dc1.search(base=self.ou1_dn,
1549                                       scope=SCOPE_BASE,
1550                                       attrs=["*", "parentGUID"])
1551         self.assertEquals(len(ldb_res), 1)
1552         ou_orig = ldb_res[0]
1553         ou_dn   = ldb_res[0]["dn"]
1554
1555         # check user info on DC1
1556         print("Testing for %s with GUID %s" % (self.ou1_dn, self._GUID_string(ou_orig["objectGUID"][0])))
1557         self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=ou_orig, is_deleted=False)
1558
1559         # trigger replication from DC1 to DC2
1560         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True)
1561         # check user info on DC2 - should still be valid user
1562         ou_cur = self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=ou_orig, is_deleted=False)
1563
1564         new_dn = ldb.Dn(self.ldb_dc1, "OU=%s" % self.ou1_dn.get_component_value(0))
1565         new_dn.add_base(self.ou2_dn)
1566         self.ldb_dc1.rename(ou_dn, new_dn)
1567         ldb_res = self.ldb_dc1.search(base=new_dn,
1568                                       scope=SCOPE_BASE,
1569                                       attrs=["*", "parentGUID"])
1570         self.assertEquals(len(ldb_res), 1)
1571
1572         ou_moved_orig = ldb_res[0]
1573         ou_moved_dn   = ldb_res[0]["dn"]
1574
1575         # Modify description on DC2.  This triggers a replication, but
1576         # not of 'name' and so a bug in Samba regarding the DN.
1577         msg = ldb.Message()
1578         msg.dn = ou_dn
1579         msg["description"] = ldb.MessageElement("OU Description", ldb.FLAG_MOD_REPLACE, "description")
1580         self.ldb_dc2.modify(msg)
1581
1582         # trigger replication from DC2 to DC1
1583         self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True)
1584         # check user info on DC1 - should still be valid user
1585         ou_cur = self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=ou_moved_orig, is_deleted=False)
1586         self.assertTrue("description" in ou_cur)
1587
1588         # delete OU on DC1
1589         self.ldb_dc1.delete('<GUID=%s>' % self._GUID_string(ou_orig["objectGUID"][0]))
1590         # trigger replication from DC2 to DC1
1591         self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True)
1592         # check user info on DC2 - should be deleted user
1593         ou_cur = self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=ou_moved_orig, is_deleted=True)
1594         self.assertFalse("description" in ou_cur)
1595
1596         # trigger replication from DC1 to DC2, for cleanup
1597         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True)
1598
1599         # check user info on DC2 - should be deleted user
1600         ou_cur = self._check_obj(sam_ldb=self.ldb_dc2, obj_orig=ou_moved_orig, is_deleted=True)
1601         self.assertFalse("description" in ou_cur)
1602
1603     def test_ReplicateMoveObject8(self):
1604         """Verifies how a moved container is replicated between two DCs.
1605            This test should verify that:
1606             - the OU1 is replicated properly
1607             - the OU1 is modified on DC2
1608             - the OU1 is renamed on DC1 to OU1-renamed
1609             - We verify that after replication DC1 -> DC2,
1610               that the OU1 has the correct DN (OU1-renamed), and the description
1611
1612            """
1613         ldb_res = self.ldb_dc1.search(base=self.ou1_dn,
1614                                       scope=SCOPE_BASE,
1615                                       attrs=["*", "parentGUID"])
1616         self.assertEquals(len(ldb_res), 1)
1617         ou_orig = ldb_res[0]
1618         ou_dn   = ldb_res[0]["dn"]
1619
1620         # check user info on DC1
1621         print("Testing for %s with GUID %s" % (self.ou1_dn, self._GUID_string(ou_orig["objectGUID"][0])))
1622         self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=ou_orig, is_deleted=False)
1623
1624         # trigger replication from DC1 to DC2
1625         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True)
1626         # check user info on DC2 - should still be valid user
1627         ou_cur = self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=ou_orig, is_deleted=False)
1628
1629         new_dn = ldb.Dn(self.ldb_dc1, "OU=%s-renamed" % self.ou1_dn.get_component_value(0))
1630         new_dn.add_base(self.ou1_dn.parent())
1631         self.ldb_dc1.rename(ou_dn, new_dn)
1632         ldb_res = self.ldb_dc1.search(base=new_dn,
1633                                       scope=SCOPE_BASE,
1634                                       attrs=["*", "parentGUID"])
1635         self.assertEquals(len(ldb_res), 1)
1636
1637         ou_moved_orig = ldb_res[0]
1638         ou_moved_dn   = ldb_res[0]["dn"]
1639
1640         # Modify description on DC2.  This triggers a replication, but
1641         # not of 'name' and so a bug in Samba regarding the DN.
1642         msg = ldb.Message()
1643         msg.dn = ou_dn
1644         msg["description"] = ldb.MessageElement("OU Description", ldb.FLAG_MOD_REPLACE, "description")
1645         self.ldb_dc2.modify(msg)
1646
1647         # trigger replication from DC1 to DC2
1648         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True)
1649         # check user info on DC2 - should still be valid user
1650         ou_cur = self._check_obj(sam_ldb=self.ldb_dc2, obj_orig=ou_moved_orig, is_deleted=False)
1651         self.assertTrue("description" in ou_cur)
1652
1653         # delete OU on DC1
1654         self.ldb_dc1.delete('<GUID=%s>' % self._GUID_string(ou_orig["objectGUID"][0]))
1655         # trigger replication from DC2 to DC1
1656         self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True)
1657         # check user info on DC2 - should be deleted user
1658         ou_cur = self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=ou_moved_orig, is_deleted=True)
1659         self.assertFalse("description" in ou_cur)
1660
1661         # trigger replication from DC1 to DC2, for cleanup
1662         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True)
1663
1664         # check user info on DC2 - should be deleted user
1665         ou_cur = self._check_obj(sam_ldb=self.ldb_dc2, obj_orig=ou_moved_orig, is_deleted=True)
1666         self.assertFalse("description" in ou_cur)
1667
1668     def test_ReplicateMoveObject9(self):
1669         """Verifies how a moved container is replicated between two DCs.
1670            This test should verify that:
1671             - the OU1 is replicated properly
1672             - the OU1 is modified on DC2
1673             - the OU1 is renamed on DC1 to be under OU2
1674             - the OU1 is renamed on DC1 to OU1-renamed
1675             - We verify that after replication DC1 -> DC2,
1676               that the OU1 has the correct DN (OU1-renamed), and the description
1677
1678            """
1679         ldb_res = self.ldb_dc1.search(base=self.ou1_dn,
1680                                       scope=SCOPE_BASE,
1681                                       attrs=["*", "parentGUID"])
1682         self.assertEquals(len(ldb_res), 1)
1683         ou_orig = ldb_res[0]
1684         ou_dn   = ldb_res[0]["dn"]
1685
1686         # check user info on DC1
1687         print("Testing for %s with GUID %s" % (self.ou1_dn, self._GUID_string(ou_orig["objectGUID"][0])))
1688         self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=ou_orig, is_deleted=False)
1689
1690         # trigger replication from DC1 to DC2
1691         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True)
1692         # check user info on DC2 - should still be valid user
1693         ou_cur = self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=ou_orig, is_deleted=False)
1694
1695         new_dn = ldb.Dn(self.ldb_dc1, "OU=%s-renamed" % self.ou1_dn.get_component_value(0))
1696         new_dn.add_base(self.ou1_dn.parent())
1697         self.ldb_dc1.rename(ou_dn, new_dn)
1698         ldb_res = self.ldb_dc1.search(base=new_dn,
1699                                       scope=SCOPE_BASE,
1700                                       attrs=["*", "parentGUID"])
1701         self.assertEquals(len(ldb_res), 1)
1702
1703         ou_moved_orig = ldb_res[0]
1704         ou_moved_dn   = ldb_res[0]["dn"]
1705
1706         # Modify description on DC2.  This triggers a replication, but
1707         # not of 'name' and so a bug in Samba regarding the DN.
1708         msg = ldb.Message()
1709         msg.dn = ou_dn
1710         msg["description"] = ldb.MessageElement("OU Description", ldb.FLAG_MOD_REPLACE, "description")
1711         self.ldb_dc2.modify(msg)
1712
1713         # trigger replication from DC2 to DC1
1714         self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True)
1715         # check user info on DC1 - should still be valid user
1716         ou_cur = self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=ou_moved_orig, is_deleted=False)
1717         self.assertTrue("description" in ou_cur)
1718
1719         # delete OU on DC1
1720         self.ldb_dc1.delete('<GUID=%s>' % self._GUID_string(ou_orig["objectGUID"][0]))
1721         # trigger replication from DC2 to DC1
1722         self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True)
1723         # check user info on DC2 - should be deleted user
1724         ou_cur = self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=ou_moved_orig, is_deleted=True)
1725         self.assertFalse("description" in ou_cur)
1726
1727         # trigger replication from DC1 to DC2, for cleanup
1728         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True)
1729
1730         # check user info on DC2 - should be deleted user
1731         ou_cur = self._check_obj(sam_ldb=self.ldb_dc2, obj_orig=ou_moved_orig, is_deleted=True)
1732         self.assertFalse("description" in ou_cur)
1733
1734     def test_ReplicateMoveObject10(self):
1735         """Verifies how a moved container is replicated between two DCs.
1736            This test should verify that:
1737             - the OU1 is replicated properly
1738             - the OU1 is modified on DC2
1739             - the OU1 is deleted on DC1
1740             - We verify that after replication DC1 -> DC2,
1741               that the OU1 is deleted, and the description has gone away
1742
1743            """
1744         ldb_res = self.ldb_dc1.search(base=self.ou1_dn,
1745                                       scope=SCOPE_BASE,
1746                                       attrs=["*", "parentGUID"])
1747         self.assertEquals(len(ldb_res), 1)
1748         ou_orig = ldb_res[0]
1749         ou_dn   = ldb_res[0]["dn"]
1750
1751         # check user info on DC1
1752         print("Testing for %s with GUID %s" % (self.ou1_dn, self._GUID_string(ou_orig["objectGUID"][0])))
1753         self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=ou_orig, is_deleted=False)
1754
1755         # trigger replication from DC1 to DC2
1756         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True)
1757         # check user info on DC2 - should still be valid user
1758         ou_cur = self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=ou_orig, is_deleted=False)
1759
1760         # Modify description on DC2.  This triggers a replication, but
1761         # not of 'name' and so a bug in Samba regarding the DN.
1762         msg = ldb.Message()
1763         msg.dn = ou_dn
1764         msg["description"] = ldb.MessageElement("OU Description", ldb.FLAG_MOD_REPLACE, "description")
1765         self.ldb_dc2.modify(msg)
1766
1767         # delete OU on DC1
1768         self.ldb_dc1.delete('<GUID=%s>' % self._GUID_string(ou_orig["objectGUID"][0]))
1769         # trigger replication from DC1 to DC2
1770         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True)
1771         # check user info on DC2 - should be deleted OU
1772         ou_cur = self._check_obj(sam_ldb=self.ldb_dc2, obj_orig=ou_orig, is_deleted=True)
1773         self.assertFalse("description" in ou_cur)
1774
1775         # trigger replication from DC1 to DC2, for cleanup
1776         self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True)
1777
1778         # check user info on DC2 - should be deleted OU
1779         ou_cur = self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=ou_orig, is_deleted=True)
1780         self.assertFalse("description" in ou_cur)
1781
1782     def test_ReplicateMoveObject11(self):
1783         """Verifies how a moved container is replicated between two DCs.
1784            This test should verify that:
1785             - the OU1 is replicated properly
1786             - the OU1 is modified on DC2
1787             - the OU1 is deleted on DC1
1788             - We verify that after replication DC2 -> DC1,
1789               that the OU1 is deleted, and the description has gone away
1790
1791            """
1792         ldb_res = self.ldb_dc1.search(base=self.ou1_dn,
1793                                       scope=SCOPE_BASE,
1794                                       attrs=["*", "parentGUID"])
1795         self.assertEquals(len(ldb_res), 1)
1796         ou_orig = ldb_res[0]
1797         ou_dn   = ldb_res[0]["dn"]
1798
1799         # check user info on DC1
1800         print("Testing for %s with GUID %s" % (self.ou1_dn, self._GUID_string(ou_orig["objectGUID"][0])))
1801         self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=ou_orig, is_deleted=False)
1802
1803         # trigger replication from DC1 to DC2
1804         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True)
1805         # check user info on DC2 - should still be valid user
1806         ou_cur = self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=ou_orig, is_deleted=False)
1807
1808         # Modify description on DC2.  This triggers a replication, but
1809         # not of 'name' and so a bug in Samba regarding the DN.
1810         msg = ldb.Message()
1811         msg.dn = ou_dn
1812         msg["description"] = ldb.MessageElement("OU Description", ldb.FLAG_MOD_REPLACE, "description")
1813         self.ldb_dc2.modify(msg)
1814
1815         # delete OU on DC1
1816         self.ldb_dc1.delete('<GUID=%s>' % self._GUID_string(ou_orig["objectGUID"][0]))
1817         # trigger replication from DC2 to DC1
1818         self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True)
1819         # check user info on DC2 - should be deleted OU
1820         ou_cur = self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=ou_orig, is_deleted=True)
1821         self.assertFalse("description" in ou_cur)
1822
1823         # trigger replication from DC1 to DC2, for cleanup
1824         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True)
1825
1826         # check user info on DC2 - should be deleted OU
1827         ou_cur = self._check_obj(sam_ldb=self.ldb_dc2, obj_orig=ou_orig, is_deleted=True)
1828         self.assertFalse("description" in ou_cur)
1829
1830
1831 class DrsMoveBetweenTreeOfObjectTestCase(drs_base.DrsBaseTestCase):
1832
1833     def setUp(self):
1834         super(DrsMoveBetweenTreeOfObjectTestCase, self).setUp()
1835         # disable automatic replication temporary
1836         self._disable_all_repl(self.dnsname_dc1)
1837         self._disable_all_repl(self.dnsname_dc2)
1838
1839         self.top_ou = samba.tests.create_test_ou(self.ldb_dc1,
1840                                                  "replica_move")
1841
1842         # make sure DCs are synchronized before the test
1843         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True)
1844         self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True)
1845
1846         self.ou1_dn = ldb.Dn(self.ldb_dc1, "OU=DrsOU1")
1847         self.ou1_dn.add_base(self.top_ou)
1848         self.ou1 = {}
1849         self.ou1["dn"] = self.ou1_dn
1850         self.ou1["objectclass"] = "organizationalUnit"
1851         self.ou1["ou"] = self.ou1_dn.get_component_value(0)
1852
1853         self.ou2_dn = ldb.Dn(self.ldb_dc1, "OU=DrsOU2,OU=DrsOU1")
1854         self.ou2_dn.add_base(self.top_ou)
1855         self.ou2 = {}
1856         self.ou2["dn"] = self.ou2_dn
1857         self.ou2["objectclass"] = "organizationalUnit"
1858         self.ou2["ou"] = self.ou2_dn.get_component_value(0)
1859
1860         self.ou2b_dn = ldb.Dn(self.ldb_dc1, "OU=DrsOU2B,OU=DrsOU1")
1861         self.ou2b_dn.add_base(self.top_ou)
1862         self.ou2b = {}
1863         self.ou2b["dn"] = self.ou2b_dn
1864         self.ou2b["objectclass"] = "organizationalUnit"
1865         self.ou2b["ou"] = self.ou2b_dn.get_component_value(0)
1866
1867         self.ou2c_dn = ldb.Dn(self.ldb_dc1, "OU=DrsOU2C,OU=DrsOU1")
1868         self.ou2c_dn.add_base(self.top_ou)
1869
1870         self.ou3_dn = ldb.Dn(self.ldb_dc1, "OU=DrsOU3,OU=DrsOU2,OU=DrsOU1")
1871         self.ou3_dn.add_base(self.top_ou)
1872         self.ou3 = {}
1873         self.ou3["dn"] = self.ou3_dn
1874         self.ou3["objectclass"] = "organizationalUnit"
1875         self.ou3["ou"] = self.ou3_dn.get_component_value(0)
1876
1877         self.ou4_dn = ldb.Dn(self.ldb_dc1, "OU=DrsOU4,OU=DrsOU3,OU=DrsOU2,OU=DrsOU1")
1878         self.ou4_dn.add_base(self.top_ou)
1879         self.ou4 = {}
1880         self.ou4["dn"] = self.ou4_dn
1881         self.ou4["objectclass"] = "organizationalUnit"
1882         self.ou4["ou"] = self.ou4_dn.get_component_value(0)
1883
1884         self.ou5_dn = ldb.Dn(self.ldb_dc1, "OU=DrsOU5,OU=DrsOU4,OU=DrsOU3,OU=DrsOU2,OU=DrsOU1")
1885         self.ou5_dn.add_base(self.top_ou)
1886         self.ou5 = {}
1887         self.ou5["dn"] = self.ou5_dn
1888         self.ou5["objectclass"] = "organizationalUnit"
1889         self.ou5["ou"] = self.ou5_dn.get_component_value(0)
1890
1891         self.ou6_dn = ldb.Dn(self.ldb_dc1, "OU=DrsOU6,OU=DrsOU5,OU=DrsOU4,OU=DrsOU3,OU=DrsOU2,OU=DrsOU1")
1892         self.ou6_dn.add_base(self.top_ou)
1893         self.ou6 = {}
1894         self.ou6["dn"] = self.ou6_dn
1895         self.ou6["objectclass"] = "organizationalUnit"
1896         self.ou6["ou"] = self.ou6_dn.get_component_value(0)
1897
1898     def tearDown(self):
1899         self.ldb_dc1.delete(self.top_ou, ["tree_delete:1"])
1900         self._enable_all_repl(self.dnsname_dc1)
1901         self._enable_all_repl(self.dnsname_dc2)
1902         super(DrsMoveBetweenTreeOfObjectTestCase, self).tearDown()
1903
1904     def _make_username(self):
1905         return "DrsTreeU_" + time.strftime("%s", time.gmtime())
1906
1907     # now also used to check the group
1908     def _check_obj(self, sam_ldb, obj_orig, is_deleted):
1909         # search the user by guid as it may be deleted
1910         guid_str = self._GUID_string(obj_orig["objectGUID"][0])
1911         res = sam_ldb.search(base='<GUID=%s>' % guid_str,
1912                              controls=["show_deleted:1"],
1913                              attrs=["*", "parentGUID"])
1914         self.assertEquals(len(res), 1)
1915         user_cur = res[0]
1916         cn_orig = obj_orig["cn"][0]
1917         cn_cur  = user_cur["cn"][0]
1918         name_orig = obj_orig["name"][0]
1919         name_cur  = user_cur["name"][0]
1920         dn_orig = obj_orig["dn"]
1921         dn_cur  = user_cur["dn"]
1922         # now check properties of the user
1923         if is_deleted:
1924             self.assertTrue("isDeleted" in user_cur)
1925             self.assertEquals(cn_cur.split('\n')[0], cn_orig)
1926             self.assertEquals(name_cur.split('\n')[0], name_orig)
1927             self.assertEquals(dn_cur.get_rdn_value().split('\n')[0],
1928                               dn_orig.get_rdn_value())
1929             self.assertEqual(name_cur, cn_cur)
1930         else:
1931             self.assertFalse("isDeleted" in user_cur)
1932             self.assertEquals(cn_cur, cn_orig)
1933             self.assertEquals(name_cur, name_orig)
1934             self.assertEquals(dn_cur, dn_orig)
1935             self.assertEqual(name_cur, cn_cur)
1936         self.assertEqual(name_cur, user_cur.dn.get_rdn_value())
1937
1938         return user_cur
1939
1940     def test_ReplicateMoveInTree1(self):
1941         """Verifies how an object is replicated between two DCs.
1942            This test should verify that:
1943             - a complex OU tree can be replicated correctly
1944             - the user is in the correct spot (renamed into) within the tree
1945               on both DCs
1946            """
1947         # work-out unique username to test with
1948         username = self._make_username()
1949
1950         self.ldb_dc1.add(self.ou1)
1951
1952         # create user on DC1
1953         self.ldb_dc1.newuser(username=username,
1954                              userou="ou=%s,ou=%s"
1955                              % (self.ou1_dn.get_component_value(0),
1956                                 self.top_ou.get_component_value(0)),
1957                              password=None, setpassword=False)
1958         ldb_res = self.ldb_dc1.search(base=self.ou1_dn,
1959                                       scope=SCOPE_SUBTREE,
1960                                       expression="(samAccountName=%s)" % username)
1961         self.assertEquals(len(ldb_res), 1)
1962         user_orig = ldb_res[0]
1963         user_dn   = ldb_res[0]["dn"]
1964
1965         # check user info on DC1
1966         print("Testing for %s with GUID %s" % (username, self._GUID_string(user_orig["objectGUID"][0])))
1967         self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=user_orig, is_deleted=False)
1968
1969         self.ldb_dc1.add(self.ou2)
1970         self.ldb_dc1.add(self.ou3)
1971         self.ldb_dc1.add(self.ou4)
1972         self.ldb_dc1.add(self.ou5)
1973
1974         new_dn = ldb.Dn(self.ldb_dc1, "CN=%s" % username)
1975         new_dn.add_base(self.ou5_dn)
1976         self.ldb_dc1.rename(user_dn, new_dn)
1977         ldb_res = self.ldb_dc1.search(base=self.ou2_dn,
1978                                       scope=SCOPE_SUBTREE,
1979                                       expression="(samAccountName=%s)" % username)
1980         self.assertEquals(len(ldb_res), 1)
1981
1982         user_moved_orig = ldb_res[0]
1983         user_moved_dn   = ldb_res[0]["dn"]
1984
1985         # trigger replication from DC1 to DC2
1986         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True)
1987         # check user info on DC2 - should be valid user
1988         user_cur = self._check_obj(sam_ldb=self.ldb_dc2, obj_orig=user_moved_orig, is_deleted=False)
1989
1990         # delete user on DC1
1991         self.ldb_dc1.delete('<GUID=%s>' % self._GUID_string(user_orig["objectGUID"][0]))
1992
1993         # trigger replication from DC1 to DC2, for cleanup
1994         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True)
1995
1996     def test_ReplicateMoveInTree2(self):
1997         """Verifies how an object is replicated between two DCs.
1998            This test should verify that:
1999             - a complex OU tree can be replicated correctly
2000             - the user is in the correct spot (renamed into) within the tree
2001               on both DCs
2002             - that a rename back works correctly, and is replicated
2003            """
2004         # work-out unique username to test with
2005         username = self._make_username()
2006
2007         self.ldb_dc1.add(self.ou1)
2008
2009         # create user on DC1
2010         self.ldb_dc1.newuser(username=username,
2011                              userou="ou=%s,ou=%s"
2012                              % (self.ou1_dn.get_component_value(0),
2013                                 self.top_ou.get_component_value(0)),
2014                              password=None, setpassword=False)
2015         ldb_res = self.ldb_dc1.search(base=self.ou1_dn,
2016                                       scope=SCOPE_SUBTREE,
2017                                       expression="(samAccountName=%s)" % username)
2018         self.assertEquals(len(ldb_res), 1)
2019         user_orig = ldb_res[0]
2020         user_dn   = ldb_res[0]["dn"]
2021
2022         # check user info on DC1
2023         print("Testing for %s with GUID %s" % (username, self._GUID_string(user_orig["objectGUID"][0])))
2024         self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=user_orig, is_deleted=False)
2025
2026         self.ldb_dc1.add(self.ou2)
2027         self.ldb_dc1.add(self.ou2b)
2028         self.ldb_dc1.add(self.ou3)
2029
2030         new_dn = ldb.Dn(self.ldb_dc1, "CN=%s" % username)
2031         new_dn.add_base(self.ou3_dn)
2032         self.ldb_dc1.rename(user_dn, new_dn)
2033
2034         new_dn3 = ldb.Dn(self.ldb_dc1, "OU=%s" % self.ou3_dn.get_component_value(0))
2035         new_dn3.add_base(self.ou2b_dn)
2036         self.ldb_dc1.rename(self.ou3_dn, new_dn3)
2037
2038         ldb_res = self.ldb_dc1.search(base=new_dn3,
2039                                       scope=SCOPE_SUBTREE,
2040                                       expression="(samAccountName=%s)" % username)
2041         self.assertEquals(len(ldb_res), 1)
2042
2043         user_moved_orig = ldb_res[0]
2044         user_moved_dn   = ldb_res[0]["dn"]
2045
2046         # trigger replication from DC1 to DC2
2047         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True)
2048         # check user info on DC2 - should be valid user
2049         user_cur = self._check_obj(sam_ldb=self.ldb_dc2, obj_orig=user_moved_orig, is_deleted=False)
2050
2051         # Rename on DC1
2052         new_dn = ldb.Dn(self.ldb_dc1, "CN=%s" % username)
2053         new_dn.add_base(self.ou1_dn)
2054         self.ldb_dc1.rename(user_moved_dn, new_dn)
2055
2056         # Modify description on DC2
2057         msg = ldb.Message()
2058         msg.dn = user_moved_dn
2059         msg["description"] = ldb.MessageElement("User Description", ldb.FLAG_MOD_REPLACE, "description")
2060         self.ldb_dc2.modify(msg)
2061
2062         ldb_res = self.ldb_dc1.search(base=self.ou1_dn,
2063                                       scope=SCOPE_SUBTREE,
2064                                       expression="(samAccountName=%s)" % username)
2065         self.assertEquals(len(ldb_res), 1)
2066
2067         user_moved_orig = ldb_res[0]
2068         user_moved_dn   = ldb_res[0]["dn"]
2069
2070         # trigger replication from DC1 to DC2
2071         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True)
2072         # check user info on DC2 - should be valid user
2073         user_cur = self._check_obj(sam_ldb=self.ldb_dc2, obj_orig=user_moved_orig, is_deleted=False)
2074         self.assertTrue("description" in user_cur)
2075
2076         # delete user on DC1
2077         self.ldb_dc1.delete('<GUID=%s>' % self._GUID_string(user_orig["objectGUID"][0]))
2078
2079         # trigger replication from DC2 to DC1
2080         self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True)
2081         # check user info on DC1 - should be deleted user
2082         user_cur = self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=user_moved_orig, is_deleted=True)
2083         self.assertFalse("description" in user_cur)
2084
2085         # trigger replication from DC1 to DC2, for cleanup
2086         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True)
2087         # check user info on DC2 - should be deleted user
2088         user_cur = self._check_obj(sam_ldb=self.ldb_dc2, obj_orig=user_moved_orig, is_deleted=True)
2089         self.assertFalse("description" in user_cur)
2090
2091     def test_ReplicateMoveInTree3(self):
2092         """Verifies how an object is replicated between two DCs.
2093            This test should verify that:
2094             - a complex OU tree can be replicated correctly
2095             - the user is in the correct spot (renamed into) within the tree
2096               on both DCs
2097             - that a rename back works correctly, and is replicated
2098            """
2099         # work-out unique username to test with
2100         username = self._make_username()
2101
2102         self.ldb_dc1.add(self.ou1)
2103
2104         # create user on DC1
2105         self.ldb_dc1.newuser(username=username,
2106                              userou="ou=%s,ou=%s"
2107                              % (self.ou1_dn.get_component_value(0),
2108                                 self.top_ou.get_component_value(0)),
2109                              password=None, setpassword=False)
2110         ldb_res = self.ldb_dc1.search(base=self.ou1_dn,
2111                                       scope=SCOPE_SUBTREE,
2112                                       expression="(samAccountName=%s)" % username)
2113         self.assertEquals(len(ldb_res), 1)
2114         user_orig = ldb_res[0]
2115         user_dn   = ldb_res[0]["dn"]
2116
2117         # check user info on DC1
2118         print("Testing for %s with GUID %s" % (username, self._GUID_string(user_orig["objectGUID"][0])))
2119         self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=user_orig, is_deleted=False)
2120
2121         self.ldb_dc1.add(self.ou2)
2122         self.ldb_dc1.add(self.ou2b)
2123         self.ldb_dc1.add(self.ou3)
2124
2125         new_dn = ldb.Dn(self.ldb_dc1, "CN=%s" % username)
2126         new_dn.add_base(self.ou3_dn)
2127         self.ldb_dc1.rename(user_dn, new_dn)
2128
2129         new_dn3 = ldb.Dn(self.ldb_dc1, "OU=%s" % self.ou3_dn.get_component_value(0))
2130         new_dn3.add_base(self.ou2b_dn)
2131         self.ldb_dc1.rename(self.ou3_dn, new_dn3)
2132
2133         ldb_res = self.ldb_dc1.search(base=new_dn3,
2134                                       scope=SCOPE_SUBTREE,
2135                                       expression="(samAccountName=%s)" % username)
2136         self.assertEquals(len(ldb_res), 1)
2137
2138         user_moved_orig = ldb_res[0]
2139         user_moved_dn   = ldb_res[0]["dn"]
2140
2141         # trigger replication from DC1 to DC2
2142         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True)
2143         # check user info on DC2 - should be valid user
2144         user_cur = self._check_obj(sam_ldb=self.ldb_dc2, obj_orig=user_moved_orig, is_deleted=False)
2145
2146         new_dn = ldb.Dn(self.ldb_dc1, "CN=%s" % username)
2147         new_dn.add_base(self.ou2_dn)
2148         self.ldb_dc1.rename(user_moved_dn, new_dn)
2149
2150         self.ldb_dc1.rename(self.ou2_dn, self.ou2c_dn)
2151         self.ldb_dc1.rename(self.ou2b_dn, self.ou2_dn)
2152         self.ldb_dc1.rename(self.ou2c_dn, self.ou2b_dn)
2153
2154         ldb_res = self.ldb_dc1.search(base=self.ou1_dn,
2155                                       scope=SCOPE_SUBTREE,
2156                                       expression="(samAccountName=%s)" % username,
2157                                       attrs=["*", "parentGUID"])
2158         self.assertEquals(len(ldb_res), 1)
2159
2160         user_moved_orig = ldb_res[0]
2161         user_moved_dn   = ldb_res[0]["dn"]
2162
2163         # trigger replication from DC1 to DC2
2164         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True)
2165         # check user info on DC2 - should be valid user
2166         user_cur = self._check_obj(sam_ldb=self.ldb_dc2, obj_orig=user_moved_orig, is_deleted=False)
2167
2168         self.assertEquals(user_cur["parentGUID"], user_moved_orig["parentGUID"])
2169
2170         # delete user on DC1
2171         self.ldb_dc1.delete('<GUID=%s>' % self._GUID_string(user_orig["objectGUID"][0]))
2172
2173         # trigger replication from DC1 to DC2, for cleanup
2174         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True)
2175
2176     def test_ReplicateMoveInTree3b(self):
2177         """Verifies how an object is replicated between two DCs.
2178            This test should verify that:
2179             - a complex OU tree can be replicated correctly
2180             - the user is in the correct spot (renamed into) within the tree
2181               on both DCs
2182             - that a rename back works correctly, and is replicated
2183             - that a complex rename suffle, combined with unrelated changes to the object,
2184               is replicated correctly.  The aim here is the send the objects out-of-order
2185               when sorted by usnChanged.
2186             - confirm that the OU tree and (in particular the user DN) is identical between
2187               the DCs once this has been replicated.
2188         """
2189         # work-out unique username to test with
2190         username = self._make_username()
2191
2192         self.ldb_dc1.add(self.ou1)
2193
2194         # create user on DC1
2195         self.ldb_dc1.newuser(username=username,
2196                              userou="ou=%s,ou=%s"
2197                              % (self.ou1_dn.get_component_value(0),
2198                                 self.top_ou.get_component_value(0)),
2199                              password=None, setpassword=False)
2200         ldb_res = self.ldb_dc1.search(base=self.ou1_dn,
2201                                       scope=SCOPE_SUBTREE,
2202                                       expression="(samAccountName=%s)" % username)
2203         self.assertEquals(len(ldb_res), 1)
2204         user_orig = ldb_res[0]
2205         user_dn   = ldb_res[0]["dn"]
2206
2207         # check user info on DC1
2208         print("Testing for %s with GUID %s" % (username, self._GUID_string(user_orig["objectGUID"][0])))
2209         self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=user_orig, is_deleted=False)
2210
2211         self.ldb_dc1.add(self.ou2)
2212         self.ldb_dc1.add(self.ou2b)
2213         self.ldb_dc1.add(self.ou3)
2214
2215         new_dn = ldb.Dn(self.ldb_dc1, "CN=%s" % username)
2216         new_dn.add_base(self.ou2_dn)
2217         self.ldb_dc1.rename(user_dn, new_dn)
2218
2219         ldb_res = self.ldb_dc1.search(base=self.ou2_dn,
2220                                       scope=SCOPE_SUBTREE,
2221                                       expression="(samAccountName=%s)" % username)
2222         self.assertEquals(len(ldb_res), 1)
2223
2224         user_moved_orig = ldb_res[0]
2225         user_moved_dn   = ldb_res[0]["dn"]
2226
2227         # trigger replication from DC1 to DC2
2228         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True)
2229         # check user info on DC2 - should be valid user
2230         user_cur = self._check_obj(sam_ldb=self.ldb_dc2, obj_orig=user_moved_orig, is_deleted=False)
2231
2232         msg = ldb.Message()
2233         msg.dn = new_dn
2234         msg["description"] = ldb.MessageElement("User Description", ldb.FLAG_MOD_REPLACE, "description")
2235         self.ldb_dc1.modify(msg)
2236
2237         # The sleep(1) calls here ensure that the name objects get a
2238         # new 1-sec based timestamp, and so we select how the conflict
2239         # resolution resolves.
2240         self.ldb_dc1.rename(self.ou2_dn, self.ou2c_dn)
2241         time.sleep(1)
2242         self.ldb_dc1.rename(self.ou2b_dn, self.ou2_dn)
2243         time.sleep(1)
2244         self.ldb_dc1.rename(self.ou2c_dn, self.ou2b_dn)
2245
2246         new_dn = ldb.Dn(self.ldb_dc1, "CN=%s" % username)
2247         new_dn.add_base(self.ou2_dn)
2248         self.ldb_dc1.rename('<GUID=%s>' % self._GUID_string(user_orig["objectGUID"][0]), new_dn)
2249
2250         msg = ldb.Message()
2251         msg.dn = self.ou2_dn
2252         msg["description"] = ldb.MessageElement("OU2 Description", ldb.FLAG_MOD_REPLACE, "description")
2253         self.ldb_dc1.modify(msg)
2254
2255         msg = ldb.Message()
2256         msg.dn = self.ou2b_dn
2257         msg["description"] = ldb.MessageElement("OU2b Description", ldb.FLAG_MOD_REPLACE, "description")
2258         self.ldb_dc1.modify(msg)
2259
2260         ldb_res = self.ldb_dc1.search(base=self.ou2_dn,
2261                                       scope=SCOPE_SUBTREE,
2262                                       expression="(samAccountName=%s)" % username,
2263                                       attrs=["*", "parentGUID"])
2264         self.assertEquals(len(ldb_res), 1)
2265
2266         user_moved_orig = ldb_res[0]
2267         user_moved_dn   = ldb_res[0]["dn"]
2268
2269         # trigger replication from DC1 to DC2
2270         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True)
2271         # check user info on DC2 - should be valid user
2272         user_cur = self._check_obj(sam_ldb=self.ldb_dc2, obj_orig=user_moved_orig, is_deleted=False)
2273         self.assertEquals(user_cur["parentGUID"][0], user_moved_orig["parentGUID"][0])
2274
2275         # delete user on DC1
2276         self.ldb_dc1.delete('<GUID=%s>' % self._GUID_string(user_orig["objectGUID"][0]))
2277
2278         # trigger replication from DC1 to DC2, for cleanup
2279         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True)
2280
2281     def test_ReplicateMoveInTree4(self):
2282         """Verifies how an object is replicated between two DCs.
2283            This test should verify that:
2284             - an OU and user can be replicated correctly, even after a rename
2285             - The creation and rename of the OU has been combined with unrelated changes to the object,
2286               The aim here is the send the objects out-of-order when sorted by usnChanged.
2287             - That is, the OU will be sorted by usnChanged after the user that is within that OU.
2288             - That will cause the client to need to get the OU first, by use of the GET_ANC flag
2289         """
2290         # work-out unique username to test with
2291         username = self._make_username()
2292
2293         self.ldb_dc1.add(self.ou1)
2294
2295         # create user on DC1
2296         self.ldb_dc1.newuser(username=username,
2297                              userou="ou=%s,ou=%s"
2298                              % (self.ou1_dn.get_component_value(0),
2299                                 self.top_ou.get_component_value(0)),
2300                              password=None, setpassword=False)
2301         ldb_res = self.ldb_dc1.search(base=self.ou1_dn,
2302                                       scope=SCOPE_SUBTREE,
2303                                       expression="(samAccountName=%s)" % username)
2304         self.assertEquals(len(ldb_res), 1)
2305         user_orig = ldb_res[0]
2306         user_dn   = ldb_res[0]["dn"]
2307
2308         # check user info on DC1
2309         print("Testing for %s with GUID %s" % (username, self._GUID_string(user_orig["objectGUID"][0])))
2310         self._check_obj(sam_ldb=self.ldb_dc1, obj_orig=user_orig, is_deleted=False)
2311
2312         self.ldb_dc1.add(self.ou2)
2313
2314         new_dn = ldb.Dn(self.ldb_dc1, "CN=%s" % username)
2315         new_dn.add_base(self.ou2_dn)
2316         self.ldb_dc1.rename(user_dn, new_dn)
2317
2318         msg = ldb.Message()
2319         msg.dn = self.ou2_dn
2320         msg["description"] = ldb.MessageElement("OU2 Description", ldb.FLAG_MOD_REPLACE, "description")
2321         self.ldb_dc1.modify(msg)
2322
2323         ldb_res = self.ldb_dc1.search(base=self.ou2_dn,
2324                                       scope=SCOPE_SUBTREE,
2325                                       expression="(samAccountName=%s)" % username)
2326         self.assertEquals(len(ldb_res), 1)
2327
2328         user_moved_orig = ldb_res[0]
2329         user_moved_dn   = ldb_res[0]["dn"]
2330
2331         # trigger replication from DC1 to DC2
2332         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True)
2333         # check user info on DC2 - should be valid user
2334         user_cur = self._check_obj(sam_ldb=self.ldb_dc2, obj_orig=user_moved_orig, is_deleted=False)
2335
2336         # delete user on DC1
2337         self.ldb_dc1.delete('<GUID=%s>' % self._GUID_string(user_orig["objectGUID"][0]))
2338
2339         # trigger replication from DC1 to DC2, for cleanup
2340         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True)
2341
2342     def test_ReplicateAddInOU(self):
2343         """Verifies how an object is replicated between two DCs.
2344            This test should verify that:
2345             - an OU and user can be replicated correctly
2346             - The creation of the OU has been combined with unrelated changes to the object,
2347               The aim here is the send the objects out-of-order when sorted by usnChanged.
2348             - That is, the OU will be sorted by usnChanged after the user that is within that OU.
2349             - That will cause the client to need to get the OU first, by use of the GET_ANC flag
2350         """
2351         # work-out unique username to test with
2352         username = self._make_username()
2353
2354         self.ldb_dc1.add(self.ou1)
2355
2356         # create user on DC1
2357         self.ldb_dc1.newuser(username=username,
2358                              userou="ou=%s,ou=%s"
2359                              % (self.ou1_dn.get_component_value(0),
2360                                 self.top_ou.get_component_value(0)),
2361                              password=None, setpassword=False)
2362         ldb_res = self.ldb_dc1.search(base=self.ou1_dn,
2363                                       scope=SCOPE_SUBTREE,
2364                                       expression="(samAccountName=%s)" % username,
2365                                       attrs=["*", "parentGUID"])
2366         self.assertEquals(len(ldb_res), 1)
2367         user_orig = ldb_res[0]
2368         user_dn   = ldb_res[0]["dn"]
2369
2370         msg = ldb.Message()
2371         msg.dn = self.ou1_dn
2372         msg["description"] = ldb.MessageElement("OU1 Description", ldb.FLAG_MOD_REPLACE, "description")
2373         self.ldb_dc1.modify(msg)
2374
2375         # trigger replication from DC1 to DC2
2376         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True)
2377         # check user info on DC2 - should be valid user
2378         user_cur = self._check_obj(sam_ldb=self.ldb_dc2, obj_orig=user_orig, is_deleted=False)
2379
2380         self.assertEquals(user_cur["parentGUID"], user_orig["parentGUID"])
2381
2382         # delete user on DC1
2383         self.ldb_dc1.delete('<GUID=%s>' % self._GUID_string(user_orig["objectGUID"][0]))
2384
2385         # trigger replication from DC1 to DC2, for cleanup
2386         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True)
2387
2388     def test_ReplicateAddInMovedOU(self):
2389         """Verifies how an object is replicated between two DCs.
2390            This test should verify that:
2391             - an OU and user can be replicated correctly
2392             - The creation of the OU has been combined with unrelated changes to the object,
2393               The aim here is the send the objects out-of-order when sorted by usnChanged.
2394             - That is, the OU will be sorted by usnChanged after the user that is within that OU.
2395             - That will cause the client to need to get the OU first, by use of the GET_ANC flag
2396         """
2397         # work-out unique username to test with
2398         username = self._make_username()
2399
2400         self.ldb_dc1.add(self.ou1)
2401         self.ldb_dc1.add(self.ou2)
2402
2403         # create user on DC1
2404         self.ldb_dc1.newuser(username=username,
2405                              userou="ou=%s,ou=%s"
2406                              % (self.ou1_dn.get_component_value(0),
2407                                 self.top_ou.get_component_value(0)),
2408                              password=None, setpassword=False)
2409         ldb_res = self.ldb_dc1.search(base=self.ou1_dn,
2410                                       scope=SCOPE_SUBTREE,
2411                                       expression="(samAccountName=%s)" % username,
2412                                       attrs=["*", "parentGUID"])
2413         self.assertEquals(len(ldb_res), 1)
2414         user_orig = ldb_res[0]
2415         user_dn   = ldb_res[0]["dn"]
2416
2417         new_dn = ldb.Dn(self.ldb_dc1, "CN=%s" % username)
2418         new_dn.add_base(self.ou2_dn)
2419         self.ldb_dc1.rename(user_dn, new_dn)
2420
2421         self.ldb_dc1.rename(self.ou2_dn, self.ou2b_dn)
2422
2423         ldb_res = self.ldb_dc1.search(base=self.ou1_dn,
2424                                       scope=SCOPE_SUBTREE,
2425                                       expression="(samAccountName=%s)" % username,
2426                                       attrs=["*", "parentGUID"])
2427         self.assertEquals(len(ldb_res), 1)
2428         user_moved = ldb_res[0]
2429         user_moved_dn = ldb_res[0]["dn"]
2430
2431         # trigger replication from DC1 to DC2
2432         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True)
2433         # check user info on DC2 - should be valid user
2434         user_cur = self._check_obj(sam_ldb=self.ldb_dc2, obj_orig=user_moved, is_deleted=False)
2435
2436         self.assertEquals(user_cur["parentGUID"], user_moved["parentGUID"])
2437
2438         # delete user on DC1
2439         self.ldb_dc1.delete('<GUID=%s>' % self._GUID_string(user_orig["objectGUID"][0]))
2440
2441         # trigger replication from DC1 to DC2, for cleanup
2442         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True)
2443
2444     def test_ReplicateAddInConflictOU_time(self):
2445         """Verifies how an object is replicated between two DCs, when created in an ambigious location
2446            This test should verify that:
2447             - Without replication, two conflicting objects can be created
2448             - force the conflict resolution algorithm so we know which copy will win
2449               (by sleeping while creating the objects, therefore increasing that timestamp on 'name')
2450             - confirm that the user object, created on DC1, ends up in the right place on DC2
2451             - therefore confirm that the conflict algorithm worked correctly, and that parentGUID was used.
2452
2453         """
2454         # work-out unique username to test with
2455         username = self._make_username()
2456
2457         self.ldb_dc1.add(self.ou1)
2458
2459         # create user on DC1
2460         self.ldb_dc1.newuser(username=username,
2461                              userou="ou=%s,ou=%s"
2462                              % (self.ou1_dn.get_component_value(0),
2463                                 self.top_ou.get_component_value(0)),
2464                              password=None, setpassword=False)
2465         ldb_res = self.ldb_dc1.search(base=self.ou1_dn,
2466                                       scope=SCOPE_SUBTREE,
2467                                       expression="(samAccountName=%s)" % username,
2468                                       attrs=["*", "parentGUID"])
2469         self.assertEquals(len(ldb_res), 1)
2470         user_orig = ldb_res[0]
2471         user_dn   = ldb_res[0]["dn"]
2472
2473         # trigger replication from DC1 to DC2
2474         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True)
2475
2476         # Now create two, conflicting objects.  This gives the user
2477         # object something to be under on both DCs.
2478
2479         # We sleep between the two adds so that DC1 adds second, and
2480         # so wins the conflict resoution due to a later creation time
2481         # (modification timestamp on the name attribute).
2482         self.ldb_dc2.add(self.ou2)
2483         time.sleep(1)
2484         self.ldb_dc1.add(self.ou2)
2485
2486         new_dn = ldb.Dn(self.ldb_dc1, "CN=%s" % username)
2487         new_dn.add_base(self.ou2_dn)
2488         self.ldb_dc1.rename(user_dn, new_dn)
2489
2490         # Now that we have renamed the user (and so bumpted the
2491         # usnChanged), bump the value on the OUs.
2492         msg = ldb.Message()
2493         msg.dn = self.ou2_dn
2494         msg["description"] = ldb.MessageElement("OU2 Description", ldb.FLAG_MOD_REPLACE, "description")
2495         self.ldb_dc1.modify(msg)
2496
2497         msg = ldb.Message()
2498         msg.dn = self.ou2_dn
2499         msg["description"] = ldb.MessageElement("OU2 Description", ldb.FLAG_MOD_REPLACE, "description")
2500         self.ldb_dc2.modify(msg)
2501
2502         # trigger replication from DC1 to DC2
2503         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True)
2504         ldb_res = self.ldb_dc1.search(base=self.ou1_dn,
2505                                       scope=SCOPE_SUBTREE,
2506                                       expression="(samAccountName=%s)" % username,
2507                                       attrs=["*", "parentGUID"])
2508         self.assertEquals(len(ldb_res), 1)
2509         user_moved = ldb_res[0]
2510         user_moved_dn = ldb_res[0]["dn"]
2511
2512         # trigger replication from DC1 to DC2
2513         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True)
2514         # check user info on DC2 - should be under the OU2 from DC1
2515         user_cur = self._check_obj(sam_ldb=self.ldb_dc2, obj_orig=user_moved, is_deleted=False)
2516
2517         self.assertEquals(user_cur["parentGUID"], user_moved["parentGUID"])
2518
2519         # delete user on DC1
2520         self.ldb_dc1.delete('<GUID=%s>' % self._GUID_string(user_orig["objectGUID"][0]))
2521
2522         # trigger replication from DC1 to DC2, for cleanup
2523         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True)
2524
2525     def test_ReplicateAddInConflictOU2(self):
2526         """Verifies how an object is replicated between two DCs, when created in an ambigious location
2527            This test should verify that:
2528             - Without replication, two conflicting objects can be created
2529             - force the conflict resolution algorithm so we know which copy will win
2530               (by changing the description twice, therefore increasing that version count)
2531             - confirm that the user object, created on DC1, ends up in the right place on DC2
2532             - therefore confirm that the conflict algorithm worked correctly, and that parentGUID was used.
2533         """
2534         # work-out unique username to test with
2535         username = self._make_username()
2536
2537         self.ldb_dc1.add(self.ou1)
2538
2539         # create user on DC1
2540         self.ldb_dc1.newuser(username=username,
2541                              userou="ou=%s,ou=%s"
2542                              % (self.ou1_dn.get_component_value(0),
2543                                 self.top_ou.get_component_value(0)),
2544                              password=None, setpassword=False)
2545         ldb_res = self.ldb_dc1.search(base=self.ou1_dn,
2546                                       scope=SCOPE_SUBTREE,
2547                                       expression="(samAccountName=%s)" % username,
2548                                       attrs=["*", "parentGUID"])
2549         self.assertEquals(len(ldb_res), 1)
2550         user_orig = ldb_res[0]
2551         user_dn   = ldb_res[0]["dn"]
2552
2553         # trigger replication from DC1 to DC2
2554         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True)
2555
2556         # Now create two, conflicting objects.  This gives the user
2557         # object something to be under on both DCs.  We create it on
2558         # DC1 1sec later so that it will win the conflict resolution.
2559
2560         self.ldb_dc2.add(self.ou2)
2561         time.sleep(1)
2562         self.ldb_dc1.add(self.ou2)
2563
2564         new_dn = ldb.Dn(self.ldb_dc1, "CN=%s" % username)
2565         new_dn.add_base(self.ou2_dn)
2566         self.ldb_dc1.rename(user_dn, new_dn)
2567
2568         # Now that we have renamed the user (and so bumpted the
2569         # usnChanged), bump the value on the OUs.
2570         msg = ldb.Message()
2571         msg.dn = self.ou2_dn
2572         msg["description"] = ldb.MessageElement("OU2 Description", ldb.FLAG_MOD_REPLACE, "description")
2573         self.ldb_dc1.modify(msg)
2574
2575         msg = ldb.Message()
2576         msg.dn = self.ou2_dn
2577         msg["description"] = ldb.MessageElement("OU2 Description", ldb.FLAG_MOD_REPLACE, "description")
2578         self.ldb_dc2.modify(msg)
2579
2580         # trigger replication from DC1 to DC2
2581         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True)
2582         ldb_res = self.ldb_dc1.search(base=self.ou1_dn,
2583                                       scope=SCOPE_SUBTREE,
2584                                       expression="(samAccountName=%s)" % username,
2585                                       attrs=["*", "parentGUID"])
2586         self.assertEquals(len(ldb_res), 1)
2587         user_moved = ldb_res[0]
2588         user_moved_dn = ldb_res[0]["dn"]
2589
2590         # trigger replication from DC1 to DC2
2591         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True)
2592         # check user info on DC2 - should be under the OU2 from DC1
2593         user_cur = self._check_obj(sam_ldb=self.ldb_dc2, obj_orig=user_moved, is_deleted=False)
2594
2595         self.assertEquals(user_cur["parentGUID"], user_moved["parentGUID"])
2596
2597         # delete user on DC1
2598         self.ldb_dc1.delete('<GUID=%s>' % self._GUID_string(user_orig["objectGUID"][0]))
2599
2600         # trigger replication from DC1 to DC2, for cleanup
2601         self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True)