2 # -*- coding: utf-8 -*-
4 # Tests various schema replication scenarios
6 # Copyright (C) Kamen Mazdrashki <kamenim@samba.org> 2011
7 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2016
9 # This program is free software; you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation; either version 3 of the License, or
12 # (at your option) any later version.
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
19 # You should have received a copy of the GNU General Public License
20 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # export DC1=dc1_dns_name
26 # export DC2=dc2_dns_name
27 # export SUBUNITRUN=$samba4srcdir/scripting/bin/subunitrun
28 # PYTHONPATH="$PYTHONPATH:$samba4srcdir/torture/drs/python" $SUBUNITRUN getnc_exop -U"$DOMAIN/$DC_USERNAME"%"$DC_PASSWORD"
34 from drs_base
import AbstractLink
37 from samba
import werror
, WERRORError
40 from ldb
import SCOPE_BASE
42 from samba
.dcerpc
import drsuapi
, misc
, drsblobs
43 from samba
.drs_utils
import drs_DsBind
44 from samba
.ndr
import ndr_unpack
, ndr_pack
45 from functools
import cmp_to_key
46 from samba
.common
import cmp
49 def _linked_attribute_compare(la1
, la2
):
50 """See CompareLinks() in MS-DRSR section 4.1.10.5.17"""
54 # Ascending host object GUID
55 c
= cmp(ndr_pack(la1
.identifier
.guid
), ndr_pack(la2
.identifier
.guid
))
59 # Ascending attribute ID
60 if la1
.attid
!= la2
.attid
:
61 return -1 if la1
.attid
< la2
.attid
else 1
63 la1_active
= la1
.flags
& drsuapi
.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
64 la2_active
= la2
.flags
& drsuapi
.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
66 # Ascending 'is present'
67 if la1_active
!= la2_active
:
68 return 1 if la1_active
else -1
70 # Ascending target object GUID
71 return cmp(ndr_pack(la1_target
), ndr_pack(la2_target
))
74 class DrsReplicaSyncTestCase(drs_base
.DrsBaseTestCase
):
75 """Intended as a semi-black box test case for DsGetNCChanges
76 implementation for extended operations. It should be testing
77 how DsGetNCChanges handles different input params (mostly invalid).
78 Final goal is to make DsGetNCChanges as binary compatible to
79 Windows implementation as possible"""
82 super(DrsReplicaSyncTestCase
, self
).setUp()
83 self
.base_dn
= self
.ldb_dc1
.get_default_basedn()
84 self
.ou
= "OU=test_getncchanges%d,%s" % (random
.randint(0, 4294967295),
88 "objectclass": "organizationalUnit"})
89 (self
.drs
, self
.drs_handle
) = self
._ds
_bind
(self
.dnsname_dc1
, ip
=self
.url_dc1
)
90 (self
.default_hwm
, self
.default_utdv
) = self
._get
_highest
_hwm
_utdv
(self
.ldb_dc1
)
94 self
.ldb_dc1
.delete(self
.ou
, ["tree_delete:1"])
95 except ldb
.LdbError
as e
:
96 (enum
, string
) = e
.args
97 if enum
== ldb
.ERR_NO_SUCH_OBJECT
:
99 super(DrsReplicaSyncTestCase
, self
).tearDown()
101 def _determine_fSMORoleOwner(self
, fsmo_obj_dn
):
102 """Returns (owner, not_owner) pair where:
103 owner: dns name for FSMO owner
104 not_owner: dns name for DC not owning the FSMO"""
105 # collect info to return later
106 fsmo_info_1
= {"dns_name": self
.dnsname_dc1
,
107 "invocation_id": self
.ldb_dc1
.get_invocation_id(),
108 "ntds_guid": self
.ldb_dc1
.get_ntds_GUID(),
109 "server_dn": self
.ldb_dc1
.get_serverName()}
110 fsmo_info_2
= {"dns_name": self
.dnsname_dc2
,
111 "invocation_id": self
.ldb_dc2
.get_invocation_id(),
112 "ntds_guid": self
.ldb_dc2
.get_ntds_GUID(),
113 "server_dn": self
.ldb_dc2
.get_serverName()}
115 msgs
= self
.ldb_dc1
.search(scope
=ldb
.SCOPE_BASE
, base
=fsmo_info_1
["server_dn"], attrs
=["serverReference"])
116 fsmo_info_1
["server_acct_dn"] = ldb
.Dn(self
.ldb_dc1
, msgs
[0]["serverReference"][0].decode('utf8'))
117 fsmo_info_1
["rid_set_dn"] = ldb
.Dn(self
.ldb_dc1
, "CN=RID Set") + fsmo_info_1
["server_acct_dn"]
119 msgs
= self
.ldb_dc2
.search(scope
=ldb
.SCOPE_BASE
, base
=fsmo_info_2
["server_dn"], attrs
=["serverReference"])
120 fsmo_info_2
["server_acct_dn"] = ldb
.Dn(self
.ldb_dc2
, msgs
[0]["serverReference"][0].decode('utf8'))
121 fsmo_info_2
["rid_set_dn"] = ldb
.Dn(self
.ldb_dc2
, "CN=RID Set") + fsmo_info_2
["server_acct_dn"]
123 # determine the owner dc
124 res
= self
.ldb_dc1
.search(fsmo_obj_dn
,
125 scope
=SCOPE_BASE
, attrs
=["fSMORoleOwner"])
126 assert len(res
) == 1, "Only one fSMORoleOwner value expected for %s!" % fsmo_obj_dn
127 fsmo_owner
= res
[0]["fSMORoleOwner"][0]
128 if fsmo_owner
== self
.info_dc1
["dsServiceName"][0]:
129 return (fsmo_info_1
, fsmo_info_2
)
130 return (fsmo_info_2
, fsmo_info_1
)
132 def _check_exop_failed(self
, ctr6
, expected_failure
):
133 self
.assertEqual(ctr6
.extended_ret
, expected_failure
)
134 #self.assertEqual(ctr6.object_count, 0)
135 #self.assertEqual(ctr6.first_object, None)
136 self
.assertEqual(ctr6
.more_data
, False)
137 self
.assertEqual(ctr6
.nc_object_count
, 0)
138 self
.assertEqual(ctr6
.nc_linked_attributes_count
, 0)
139 self
.assertEqual(ctr6
.linked_attributes_count
, 0)
140 self
.assertEqual(ctr6
.linked_attributes
, [])
141 self
.assertEqual(ctr6
.drs_error
[0], 0)
143 def test_do_single_repl(self
):
145 Make sure that DRSUAPI_EXOP_REPL_OBJ never replicates more than
146 one object, even when we use DRS_GET_ANC/GET_TGT.
149 ou1
= "OU=get_anc1,%s" % self
.ou
152 "objectclass": "organizationalUnit"
154 ou1_id
= self
._get
_identifier
(self
.ldb_dc1
, ou1
)
155 ou2
= "OU=get_anc2,%s" % ou1
158 "objectclass": "organizationalUnit"
160 ou2_id
= self
._get
_identifier
(self
.ldb_dc1
, ou2
)
161 dc3
= "CN=test_anc_dc_%u,%s" % (random
.randint(0, 4294967295), ou2
)
164 "objectclass": "computer",
165 "userAccountControl": "%d" % (samba
.dsdb
.UF_ACCOUNTDISABLE | samba
.dsdb
.UF_SERVER_TRUST_ACCOUNT
)
167 dc3_id
= self
._get
_identifier
(self
.ldb_dc1
, dc3
)
169 # Add some linked attributes (for checking GET_TGT behaviour)
171 m
.dn
= ldb
.Dn(self
.ldb_dc2
, ou1
)
172 m
["managedBy"] = ldb
.MessageElement(ou2
, ldb
.FLAG_MOD_ADD
, "managedBy")
173 self
.ldb_dc1
.modify(m
)
174 ou1_link
= AbstractLink(drsuapi
.DRSUAPI_ATTID_managedBy
,
175 drsuapi
.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
,
176 ou1_id
.guid
, ou2_id
.guid
)
178 m
.dn
= ldb
.Dn(self
.ldb_dc2
, dc3
)
179 m
["managedBy"] = ldb
.MessageElement(ou2
, ldb
.FLAG_MOD_ADD
, "managedBy")
180 self
.ldb_dc1
.modify(m
)
181 dc3_link
= AbstractLink(drsuapi
.DRSUAPI_ATTID_managedBy
,
182 drsuapi
.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
,
183 dc3_id
.guid
, ou2_id
.guid
)
185 req
= self
._getnc
_req
10(dest_dsa
=None,
186 invocation_id
=self
.ldb_dc1
.get_invocation_id(),
188 exop
=drsuapi
.DRSUAPI_EXOP_REPL_OBJ
,
189 replica_flags
=drsuapi
.DRSUAPI_DRS_WRIT_REP
,
190 more_flags
=drsuapi
.DRSUAPI_DRS_GET_TGT
)
191 (level
, ctr
) = self
.drs
.DsGetNCChanges(self
.drs_handle
, 10, req
)
192 self
._check
_ctr
6(ctr
, [ou1
], expected_links
=[ou1_link
])
194 # DRSUAPI_DRS_WRIT_REP means that we should only replicate the dn we give (dc3).
195 # DRSUAPI_DRS_GET_ANC means that we should also replicate its ancestors, but
196 # Windows doesn't do this if we use both.
197 req
= self
._getnc
_req
10(dest_dsa
=None,
198 invocation_id
=self
.ldb_dc1
.get_invocation_id(),
200 exop
=drsuapi
.DRSUAPI_EXOP_REPL_OBJ
,
201 replica_flags
=drsuapi
.DRSUAPI_DRS_WRIT_REP |
202 drsuapi
.DRSUAPI_DRS_GET_ANC
,
203 more_flags
=drsuapi
.DRSUAPI_DRS_GET_TGT
)
204 (level
, ctr
) = self
.drs
.DsGetNCChanges(self
.drs_handle
, 10, req
)
205 self
._check
_ctr
6(ctr
, [dc3
], expected_links
=[dc3_link
])
207 # Even though the ancestor of ou2 (ou1) has changed since last hwm, and we're
208 # sending DRSUAPI_DRS_GET_ANC, the expected response is that it will only try
209 # and replicate the single object still.
210 req
= self
._getnc
_req
10(dest_dsa
=None,
211 invocation_id
=self
.ldb_dc1
.get_invocation_id(),
213 exop
=drsuapi
.DRSUAPI_EXOP_REPL_OBJ
,
214 replica_flags
=drsuapi
.DRSUAPI_DRS_CRITICAL_ONLY |
215 drsuapi
.DRSUAPI_DRS_GET_ANC
,
216 more_flags
=drsuapi
.DRSUAPI_DRS_GET_TGT
)
217 (level
, ctr
) = self
.drs
.DsGetNCChanges(self
.drs_handle
, 10, req
)
218 self
._check
_ctr
6(ctr
, [ou2
])
220 def test_do_full_repl_on_ou(self
):
222 Make sure that a full replication on a not-an-nc fails with
226 non_nc_ou
= "OU=not-an-NC,%s" % self
.ou
229 "objectclass": "organizationalUnit"
231 req8
= self
._exop
_req
8(dest_dsa
=None,
232 invocation_id
=self
.ldb_dc1
.get_invocation_id(),
234 exop
=drsuapi
.DRSUAPI_EXOP_NONE
,
235 replica_flags
=drsuapi
.DRSUAPI_DRS_WRIT_REP
)
238 (level
, ctr
) = self
.drs
.DsGetNCChanges(self
.drs_handle
, 8, req8
)
239 self
.fail("Expected DsGetNCChanges to fail with WERR_DS_CANT_FIND_EXPECTED_NC")
240 except WERRORError
as e1
:
241 (enum
, estr
) = e1
.args
242 self
.assertEqual(enum
, werror
.WERR_DS_CANT_FIND_EXPECTED_NC
)
244 def test_InvalidNC_DummyDN_InvalidGUID_REPL_OBJ(self
):
245 """Test single object replication on a totally invalid GUID fails with the right error code"""
246 fsmo_dn
= self
.ldb_dc1
.get_schema_basedn()
247 (fsmo_owner
, fsmo_not_owner
) = self
._determine
_fSMORoleOwner
(fsmo_dn
)
249 req8
= self
._exop
_req
8(dest_dsa
="9c637462-5b8c-4467-aef2-bdb1f57bc4ef",
250 invocation_id
=fsmo_owner
["invocation_id"],
252 nc_guid
=misc
.GUID("c2d2f745-1610-4e93-964b-d4ba73eb32f8"),
253 exop
=drsuapi
.DRSUAPI_EXOP_REPL_OBJ
)
255 (drs
, drs_handle
) = self
._ds
_bind
(self
.dnsname_dc1
, ip
=self
.url_dc1
)
257 (level
, ctr
) = drs
.DsGetNCChanges(drs_handle
, 8, req8
)
258 except WERRORError
as e1
:
259 (enum
, estr
) = e1
.args
260 self
.assertEqual(enum
, werror
.WERR_DS_DRA_BAD_DN
)
262 def test_InvalidNC_DummyDN_InvalidGUID_REPL_SECRET(self
):
263 """Test single object replication on a totally invalid GUID fails with the right error code"""
264 fsmo_dn
= self
.ldb_dc1
.get_schema_basedn()
265 (fsmo_owner
, fsmo_not_owner
) = self
._determine
_fSMORoleOwner
(fsmo_dn
)
267 req8
= self
._exop
_req
8(dest_dsa
="9c637462-5b8c-4467-aef2-bdb1f57bc4ef",
268 invocation_id
=fsmo_owner
["invocation_id"],
270 nc_guid
=misc
.GUID("c2d2f745-1610-4e93-964b-d4ba73eb32f8"),
271 exop
=drsuapi
.DRSUAPI_EXOP_REPL_OBJ
)
273 (drs
, drs_handle
) = self
._ds
_bind
(self
.dnsname_dc1
, ip
=self
.url_dc1
)
275 (level
, ctr
) = drs
.DsGetNCChanges(drs_handle
, 8, req8
)
276 except WERRORError
as e1
:
277 (enum
, estr
) = e1
.args
278 self
.assertEqual(enum
, werror
.WERR_DS_DRA_BAD_DN
)
280 def test_InvalidNC_DummyDN_InvalidGUID_RID_ALLOC(self
):
281 """Test RID Allocation on a totally invalid GUID fails with the right error code"""
282 fsmo_dn
= self
.ldb_dc1
.get_schema_basedn()
283 (fsmo_owner
, fsmo_not_owner
) = self
._determine
_fSMORoleOwner
(fsmo_dn
)
285 req8
= self
._exop
_req
8(dest_dsa
="9c637462-5b8c-4467-aef2-bdb1f57bc4ef",
286 invocation_id
=fsmo_owner
["invocation_id"],
288 nc_guid
=misc
.GUID("c2d2f745-1610-4e93-964b-d4ba73eb32f8"),
289 exop
=drsuapi
.DRSUAPI_EXOP_FSMO_RID_ALLOC
)
291 (drs
, drs_handle
) = self
._ds
_bind
(self
.dnsname_dc1
, ip
=self
.url_dc1
)
293 (level
, ctr
) = drs
.DsGetNCChanges(drs_handle
, 8, req8
)
294 except WERRORError
as e1
:
295 (enum
, estr
) = e1
.args
296 self
.assertEqual(enum
, werror
.WERR_DS_DRA_BAD_NC
)
298 def test_valid_GUID_only_REPL_OBJ(self
):
299 dc_guid_1
= self
.ldb_dc1
.get_invocation_id()
300 drs
, drs_handle
= self
._ds
_bind
(self
.dnsname_dc1
, ip
=self
.url_dc1
)
302 res
= self
.ldb_dc1
.search(base
=self
.ou
, scope
=SCOPE_BASE
,
303 attrs
=["objectGUID"])
305 guid
= misc
.GUID(res
[0]["objectGUID"][0])
307 req8
= self
._exop
_req
8(dest_dsa
=None,
308 invocation_id
=dc_guid_1
,
311 exop
=drsuapi
.DRSUAPI_EXOP_REPL_OBJ
)
314 (level
, ctr
) = drs
.DsGetNCChanges(drs_handle
, 8, req8
)
315 except WERRORError
as e1
:
316 (enum
, estr
) = e1
.args
317 self
.fail(f
"Failed to call GetNCChanges with EXOP_REPL_OBJ and a GUID: {estr}")
319 self
.assertEqual(ctr
.first_object
.object.identifier
.guid
, guid
)
321 def test_DummyDN_valid_GUID_REPL_OBJ(self
):
322 dc_guid_1
= self
.ldb_dc1
.get_invocation_id()
323 drs
, drs_handle
= self
._ds
_bind
(self
.dnsname_dc1
, ip
=self
.url_dc1
)
325 res
= self
.ldb_dc1
.search(base
=self
.ou
, scope
=SCOPE_BASE
,
326 attrs
=["objectGUID"])
328 guid
= misc
.GUID(res
[0]["objectGUID"][0])
330 req8
= self
._exop
_req
8(dest_dsa
=None,
331 invocation_id
=dc_guid_1
,
334 exop
=drsuapi
.DRSUAPI_EXOP_REPL_OBJ
)
337 (level
, ctr
) = drs
.DsGetNCChanges(drs_handle
, 8, req8
)
338 except WERRORError
as e1
:
339 (enum
, estr
) = e1
.args
340 self
.fail(f
"Failed to call GetNCChanges with EXOP_REPL_OBJ, DummyDN and a GUID: {estr}")
342 self
.assertEqual(ctr
.first_object
.object.identifier
.guid
, guid
)
344 def test_DummyDN_valid_GUID_REPL_SECRET(self
):
345 dc_guid_1
= self
.ldb_dc1
.get_invocation_id()
346 drs
, drs_handle
= self
._ds
_bind
(self
.dnsname_dc1
, ip
=self
.url_dc1
)
348 res
= self
.ldb_dc1
.search(base
=self
.ou
, scope
=SCOPE_BASE
,
349 attrs
=["objectGUID"])
351 guid
= misc
.GUID(res
[0]["objectGUID"][0])
353 req8
= self
._exop
_req
8(dest_dsa
=None,
354 invocation_id
=dc_guid_1
,
357 exop
=drsuapi
.DRSUAPI_EXOP_REPL_SECRET
)
360 (level
, ctr
) = drs
.DsGetNCChanges(drs_handle
, 8, req8
)
361 except WERRORError
as e1
:
362 (enum
, estr
) = e1
.args
364 # We expect to get as far as failing on the missing dest_dsa
365 self
.assertEqual(enum
, werror
.WERR_DS_DRA_DB_ERROR
)
367 def test_link_utdv_hwm(self
):
368 """Test verify the DRS_GET_ANC behavior."""
370 ou1
= "OU=get_anc1,%s" % self
.ou
373 "objectclass": "organizationalUnit"
375 ou1_id
= self
._get
_identifier
(self
.ldb_dc1
, ou1
)
376 ou2
= "OU=get_anc2,%s" % ou1
379 "objectclass": "organizationalUnit"
381 ou2_id
= self
._get
_identifier
(self
.ldb_dc1
, ou2
)
382 dc3
= "CN=test_anc_dc_%u,%s" % (random
.randint(0, 4294967295), ou2
)
385 "objectclass": "computer",
386 "userAccountControl": "%d" % (samba
.dsdb
.UF_ACCOUNTDISABLE | samba
.dsdb
.UF_SERVER_TRUST_ACCOUNT
)
388 dc3_id
= self
._get
_identifier
(self
.ldb_dc1
, dc3
)
390 (hwm1
, utdv1
) = self
._check
_replication
([ou1
, ou2
, dc3
],
391 drsuapi
.DRSUAPI_DRS_WRIT_REP
)
393 self
._check
_replication
([ou1
, ou2
, dc3
],
394 drsuapi
.DRSUAPI_DRS_WRIT_REP |
395 drsuapi
.DRSUAPI_DRS_GET_ANC
)
397 self
._check
_replication
([dc3
],
398 drsuapi
.DRSUAPI_DRS_CRITICAL_ONLY
)
400 self
._check
_replication
([ou1
, ou2
, dc3
],
401 drsuapi
.DRSUAPI_DRS_CRITICAL_ONLY |
402 drsuapi
.DRSUAPI_DRS_GET_ANC
)
405 m
.dn
= ldb
.Dn(self
.ldb_dc1
, ou1
)
406 m
["displayName"] = ldb
.MessageElement("OU1", ldb
.FLAG_MOD_ADD
, "displayName")
407 self
.ldb_dc1
.modify(m
)
409 (hwm2
, utdv2
) = self
._check
_replication
([ou2
, dc3
, ou1
],
410 drsuapi
.DRSUAPI_DRS_WRIT_REP
)
412 self
._check
_replication
([ou1
, ou2
, dc3
],
413 drsuapi
.DRSUAPI_DRS_WRIT_REP |
414 drsuapi
.DRSUAPI_DRS_GET_ANC
)
416 self
._check
_replication
([dc3
],
417 drsuapi
.DRSUAPI_DRS_CRITICAL_ONLY
)
419 self
._check
_replication
([ou1
, ou2
, dc3
],
420 drsuapi
.DRSUAPI_DRS_CRITICAL_ONLY |
421 drsuapi
.DRSUAPI_DRS_GET_ANC
)
423 self
._check
_replication
([ou1
],
424 drsuapi
.DRSUAPI_DRS_WRIT_REP
,
427 self
._check
_replication
([ou1
],
428 drsuapi
.DRSUAPI_DRS_WRIT_REP |
429 drsuapi
.DRSUAPI_DRS_GET_ANC
,
432 self
._check
_replication
([ou1
],
433 drsuapi
.DRSUAPI_DRS_WRIT_REP |
434 drsuapi
.DRSUAPI_DRS_GET_ANC
,
435 uptodateness_vector
=utdv1
)
438 m
.dn
= ldb
.Dn(self
.ldb_dc1
, ou2
)
439 m
["displayName"] = ldb
.MessageElement("OU2", ldb
.FLAG_MOD_ADD
, "displayName")
440 self
.ldb_dc1
.modify(m
)
442 (hwm3
, utdv3
) = self
._check
_replication
([dc3
, ou1
, ou2
],
443 drsuapi
.DRSUAPI_DRS_WRIT_REP
)
445 self
._check
_replication
([ou1
, ou2
, dc3
],
446 drsuapi
.DRSUAPI_DRS_WRIT_REP |
447 drsuapi
.DRSUAPI_DRS_GET_ANC
)
449 self
._check
_replication
([dc3
],
450 drsuapi
.DRSUAPI_DRS_CRITICAL_ONLY
)
452 self
._check
_replication
([ou1
, ou2
, dc3
],
453 drsuapi
.DRSUAPI_DRS_CRITICAL_ONLY |
454 drsuapi
.DRSUAPI_DRS_GET_ANC
)
456 self
._check
_replication
([ou1
, ou2
],
457 drsuapi
.DRSUAPI_DRS_WRIT_REP
,
460 self
._check
_replication
([ou1
, ou2
],
461 drsuapi
.DRSUAPI_DRS_WRIT_REP |
462 drsuapi
.DRSUAPI_DRS_GET_ANC
,
465 self
._check
_replication
([ou1
, ou2
],
466 drsuapi
.DRSUAPI_DRS_WRIT_REP |
467 drsuapi
.DRSUAPI_DRS_GET_ANC
,
468 uptodateness_vector
=utdv1
)
471 m
.dn
= ldb
.Dn(self
.ldb_dc1
, self
.ou
)
472 m
["displayName"] = ldb
.MessageElement("OU", ldb
.FLAG_MOD_ADD
, "displayName")
473 self
.ldb_dc1
.modify(m
)
475 (hwm4
, utdv4
) = self
._check
_replication
([dc3
, ou1
, ou2
, self
.ou
],
476 drsuapi
.DRSUAPI_DRS_WRIT_REP
)
478 self
._check
_replication
([self
.ou
, ou1
, ou2
, dc3
],
479 drsuapi
.DRSUAPI_DRS_WRIT_REP |
480 drsuapi
.DRSUAPI_DRS_GET_ANC
)
482 self
._check
_replication
([dc3
],
483 drsuapi
.DRSUAPI_DRS_CRITICAL_ONLY
)
485 self
._check
_replication
([self
.ou
, ou1
, ou2
, dc3
],
486 drsuapi
.DRSUAPI_DRS_CRITICAL_ONLY |
487 drsuapi
.DRSUAPI_DRS_GET_ANC
)
489 self
._check
_replication
([self
.ou
, ou2
],
490 drsuapi
.DRSUAPI_DRS_WRIT_REP |
491 drsuapi
.DRSUAPI_DRS_GET_ANC
,
492 uptodateness_vector
=utdv2
)
494 cn3
= "CN=get_anc3,%s" % ou2
497 "objectclass": "container",
500 (hwm5
, utdv5
) = self
._check
_replication
([dc3
, ou1
, ou2
, self
.ou
, cn3
],
501 drsuapi
.DRSUAPI_DRS_WRIT_REP
)
503 self
._check
_replication
([self
.ou
, ou1
, ou2
, dc3
, cn3
],
504 drsuapi
.DRSUAPI_DRS_WRIT_REP |
505 drsuapi
.DRSUAPI_DRS_GET_ANC
)
507 self
._check
_replication
([dc3
],
508 drsuapi
.DRSUAPI_DRS_CRITICAL_ONLY
)
510 self
._check
_replication
([self
.ou
, ou1
, ou2
, dc3
],
511 drsuapi
.DRSUAPI_DRS_CRITICAL_ONLY |
512 drsuapi
.DRSUAPI_DRS_GET_ANC
)
515 m
.dn
= ldb
.Dn(self
.ldb_dc1
, ou2
)
516 m
["managedBy"] = ldb
.MessageElement(dc3
, ldb
.FLAG_MOD_ADD
, "managedBy")
517 self
.ldb_dc1
.modify(m
)
518 ou2_managedBy_dc3
= AbstractLink(drsuapi
.DRSUAPI_ATTID_managedBy
,
519 drsuapi
.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
,
520 ou2_id
.guid
, dc3_id
.guid
)
522 (hwm6
, utdv6
) = self
._check
_replication
([dc3
, ou1
, self
.ou
, cn3
, ou2
],
523 drsuapi
.DRSUAPI_DRS_WRIT_REP
,
524 expected_links
=[ou2_managedBy_dc3
])
526 # Can fail against Windows due to equal precedence of dc3, cn3
527 self
._check
_replication
([self
.ou
, ou1
, ou2
, dc3
, cn3
],
528 drsuapi
.DRSUAPI_DRS_WRIT_REP |
529 drsuapi
.DRSUAPI_DRS_GET_ANC
,
530 expected_links
=[ou2_managedBy_dc3
])
532 self
._check
_replication
([dc3
],
533 drsuapi
.DRSUAPI_DRS_CRITICAL_ONLY
)
535 self
._check
_replication
([self
.ou
, ou1
, ou2
, dc3
],
536 drsuapi
.DRSUAPI_DRS_CRITICAL_ONLY |
537 drsuapi
.DRSUAPI_DRS_GET_ANC
)
539 self
._check
_replication
([],
540 drsuapi
.DRSUAPI_DRS_WRIT_REP
,
541 uptodateness_vector
=utdv5
,
542 expected_links
=[ou2_managedBy_dc3
])
544 self
._check
_replication
([],
545 drsuapi
.DRSUAPI_DRS_CRITICAL_ONLY
,
546 uptodateness_vector
=utdv5
)
548 self
._check
_replication
([],
549 drsuapi
.DRSUAPI_DRS_CRITICAL_ONLY
,
550 uptodateness_vector
=utdv5
)
553 m
.dn
= ldb
.Dn(self
.ldb_dc1
, dc3
)
554 m
["managedBy"] = ldb
.MessageElement(ou1
, ldb
.FLAG_MOD_ADD
, "managedBy")
555 self
.ldb_dc1
.modify(m
)
556 dc3_managedBy_ou1
= AbstractLink(drsuapi
.DRSUAPI_ATTID_managedBy
,
557 drsuapi
.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
,
558 dc3_id
.guid
, ou1_id
.guid
)
560 (hwm7
, utdv7
) = self
._check
_replication
([ou1
, self
.ou
, cn3
, ou2
, dc3
],
561 drsuapi
.DRSUAPI_DRS_WRIT_REP
,
562 expected_links
=[ou2_managedBy_dc3
, dc3_managedBy_ou1
])
564 # Can fail against Windows due to equal precedence of dc3, cn3
565 # self._check_replication([self.ou,ou1,ou2,dc3,cn3],
566 # drsuapi.DRSUAPI_DRS_WRIT_REP|
567 # drsuapi.DRSUAPI_DRS_GET_ANC,
568 # expected_links=[ou2_managedBy_dc3,dc3_managedBy_ou1])
570 self
._check
_replication
([dc3
],
571 drsuapi
.DRSUAPI_DRS_CRITICAL_ONLY
,
572 expected_links
=[dc3_managedBy_ou1
])
574 self
._check
_replication
([dc3
],
575 drsuapi
.DRSUAPI_DRS_CRITICAL_ONLY
,
576 expected_links
=[dc3_managedBy_ou1
])
578 self
._check
_replication
([self
.ou
, ou1
, ou2
, dc3
],
579 drsuapi
.DRSUAPI_DRS_CRITICAL_ONLY |
580 drsuapi
.DRSUAPI_DRS_GET_ANC
,
581 expected_links
=[dc3_managedBy_ou1
])
583 # GET_TGT seems to override DRS_CRITICAL_ONLY and also returns any
584 # object(s) that relate to the linked attributes (similar to GET_ANC)
585 self
._check
_replication
([ou1
, dc3
],
586 drsuapi
.DRSUAPI_DRS_CRITICAL_ONLY
,
587 more_flags
=drsuapi
.DRSUAPI_DRS_GET_TGT
,
588 expected_links
=[dc3_managedBy_ou1
], dn_ordered
=False)
590 # Change DC3's managedBy to OU2 instead of OU1
591 # Note that the OU1 managedBy linked attribute will still exist as
592 # a tombstone object (and so will be returned in the replication still)
594 m
.dn
= ldb
.Dn(self
.ldb_dc1
, dc3
)
595 m
["managedBy"] = ldb
.MessageElement(ou2
, ldb
.FLAG_MOD_REPLACE
, "managedBy")
596 self
.ldb_dc1
.modify(m
)
597 dc3_managedBy_ou1
.flags
&= ~drsuapi
.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
598 dc3_managedBy_ou2
= AbstractLink(drsuapi
.DRSUAPI_ATTID_managedBy
,
599 drsuapi
.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
,
600 dc3_id
.guid
, ou2_id
.guid
)
602 (hwm8
, utdv8
) = self
._check
_replication
([ou1
, self
.ou
, cn3
, ou2
, dc3
],
603 drsuapi
.DRSUAPI_DRS_WRIT_REP
,
604 expected_links
=[ou2_managedBy_dc3
, dc3_managedBy_ou1
, dc3_managedBy_ou2
])
606 # Can fail against Windows due to equal precedence of dc3, cn3
607 # self._check_replication([self.ou,ou1,ou2,dc3,cn3],
608 # drsuapi.DRSUAPI_DRS_WRIT_REP|
609 # drsuapi.DRSUAPI_DRS_GET_ANC,
610 # expected_links=[ou2_managedBy_dc3,dc3_managedBy_ou1,dc3_managedBy_ou2])
612 self
._check
_replication
([dc3
],
613 drsuapi
.DRSUAPI_DRS_CRITICAL_ONLY
,
614 expected_links
=[dc3_managedBy_ou1
, dc3_managedBy_ou2
])
616 self
._check
_replication
([self
.ou
, ou1
, ou2
, dc3
],
617 drsuapi
.DRSUAPI_DRS_CRITICAL_ONLY |
618 drsuapi
.DRSUAPI_DRS_GET_ANC
,
619 expected_links
=[dc3_managedBy_ou1
, dc3_managedBy_ou2
])
621 # GET_TGT will also return any DNs referenced by the linked attributes
622 # (including the Tombstone attribute)
623 self
._check
_replication
([ou1
, ou2
, dc3
],
624 drsuapi
.DRSUAPI_DRS_CRITICAL_ONLY
,
625 more_flags
=drsuapi
.DRSUAPI_DRS_GET_TGT
,
626 expected_links
=[dc3_managedBy_ou1
, dc3_managedBy_ou2
], dn_ordered
=False)
628 # Use the highwater-mark prior to changing ManagedBy - this should
629 # only return the old/Tombstone and new linked attributes (we already
631 self
._check
_replication
([],
632 drsuapi
.DRSUAPI_DRS_WRIT_REP
,
633 expected_links
=[dc3_managedBy_ou1
, dc3_managedBy_ou2
],
636 self
._check
_replication
([],
637 drsuapi
.DRSUAPI_DRS_WRIT_REP |
638 drsuapi
.DRSUAPI_DRS_GET_ANC
,
639 expected_links
=[dc3_managedBy_ou1
, dc3_managedBy_ou2
],
642 self
._check
_replication
([],
643 drsuapi
.DRSUAPI_DRS_CRITICAL_ONLY
,
644 expected_links
=[dc3_managedBy_ou1
, dc3_managedBy_ou2
],
647 self
._check
_replication
([],
648 drsuapi
.DRSUAPI_DRS_CRITICAL_ONLY |
649 drsuapi
.DRSUAPI_DRS_GET_ANC
,
650 expected_links
=[dc3_managedBy_ou1
, dc3_managedBy_ou2
],
653 self
._check
_replication
([],
654 drsuapi
.DRSUAPI_DRS_CRITICAL_ONLY
,
655 more_flags
=drsuapi
.DRSUAPI_DRS_GET_TGT
,
656 expected_links
=[dc3_managedBy_ou1
, dc3_managedBy_ou2
],
659 # Repeat the above set of tests using the uptodateness_vector
660 # instead of the highwater-mark
661 self
._check
_replication
([],
662 drsuapi
.DRSUAPI_DRS_WRIT_REP
,
663 expected_links
=[dc3_managedBy_ou1
, dc3_managedBy_ou2
],
664 uptodateness_vector
=utdv7
)
666 self
._check
_replication
([],
667 drsuapi
.DRSUAPI_DRS_WRIT_REP |
668 drsuapi
.DRSUAPI_DRS_GET_ANC
,
669 expected_links
=[dc3_managedBy_ou1
, dc3_managedBy_ou2
],
670 uptodateness_vector
=utdv7
)
672 self
._check
_replication
([],
673 drsuapi
.DRSUAPI_DRS_CRITICAL_ONLY
,
674 expected_links
=[dc3_managedBy_ou1
, dc3_managedBy_ou2
],
675 uptodateness_vector
=utdv7
)
677 self
._check
_replication
([],
678 drsuapi
.DRSUAPI_DRS_CRITICAL_ONLY |
679 drsuapi
.DRSUAPI_DRS_GET_ANC
,
680 expected_links
=[dc3_managedBy_ou1
, dc3_managedBy_ou2
],
681 uptodateness_vector
=utdv7
)
683 self
._check
_replication
([],
684 drsuapi
.DRSUAPI_DRS_CRITICAL_ONLY
,
685 more_flags
=drsuapi
.DRSUAPI_DRS_GET_TGT
,
686 expected_links
=[dc3_managedBy_ou1
, dc3_managedBy_ou2
],
687 uptodateness_vector
=utdv7
)
689 def test_FSMONotOwner(self
):
690 """Test role transfer with against DC not owner of the role"""
691 fsmo_dn
= self
.ldb_dc1
.get_schema_basedn()
692 (fsmo_owner
, fsmo_not_owner
) = self
._determine
_fSMORoleOwner
(fsmo_dn
)
694 req8
= self
._exop
_req
8(dest_dsa
=fsmo_owner
["ntds_guid"],
695 invocation_id
=fsmo_not_owner
["invocation_id"],
697 exop
=drsuapi
.DRSUAPI_EXOP_FSMO_REQ_ROLE
)
699 (drs
, drs_handle
) = self
._ds
_bind
(fsmo_not_owner
["dns_name"])
700 (level
, ctr
) = drs
.DsGetNCChanges(drs_handle
, 8, req8
)
701 self
.assertEqual(level
, 6, "Expected level 6 response!")
702 self
._check
_exop
_failed
(ctr
, drsuapi
.DRSUAPI_EXOP_ERR_FSMO_NOT_OWNER
)
703 self
.assertEqual(ctr
.source_dsa_guid
, misc
.GUID(fsmo_not_owner
["ntds_guid"]))
704 self
.assertEqual(ctr
.source_dsa_invocation_id
, misc
.GUID(fsmo_not_owner
["invocation_id"]))
706 def test_InvalidDestDSA(self
):
707 """Test role transfer with invalid destination DSA guid"""
708 fsmo_dn
= self
.ldb_dc1
.get_schema_basedn()
709 (fsmo_owner
, fsmo_not_owner
) = self
._determine
_fSMORoleOwner
(fsmo_dn
)
711 req8
= self
._exop
_req
8(dest_dsa
="9c637462-5b8c-4467-aef2-bdb1f57bc4ef",
712 invocation_id
=fsmo_owner
["invocation_id"],
714 exop
=drsuapi
.DRSUAPI_EXOP_FSMO_REQ_ROLE
)
716 (drs
, drs_handle
) = self
._ds
_bind
(fsmo_owner
["dns_name"])
717 (level
, ctr
) = drs
.DsGetNCChanges(drs_handle
, 8, req8
)
718 self
.assertEqual(level
, 6, "Expected level 6 response!")
719 self
._check
_exop
_failed
(ctr
, drsuapi
.DRSUAPI_EXOP_ERR_UNKNOWN_CALLER
)
720 self
.assertEqual(ctr
.source_dsa_guid
, misc
.GUID(fsmo_owner
["ntds_guid"]))
721 self
.assertEqual(ctr
.source_dsa_invocation_id
, misc
.GUID(fsmo_owner
["invocation_id"]))
723 def test_InvalidDestDSA_and_GUID(self
):
724 """Test role transfer with invalid destination DSA guid"""
725 fsmo_dn
= self
.ldb_dc1
.get_schema_basedn()
726 (fsmo_owner
, fsmo_not_owner
) = self
._determine
_fSMORoleOwner
(fsmo_dn
)
728 req8
= self
._exop
_req
8(dest_dsa
="9c637462-5b8c-4467-aef2-bdb1f57bc4ef",
729 invocation_id
=fsmo_owner
["invocation_id"],
731 nc_guid
=misc
.GUID("c2d2f745-1610-4e93-964b-d4ba73eb32f8"),
732 exop
=drsuapi
.DRSUAPI_EXOP_FSMO_REQ_ROLE
)
734 (drs
, drs_handle
) = self
._ds
_bind
(fsmo_owner
["dns_name"])
736 (level
, ctr
) = drs
.DsGetNCChanges(drs_handle
, 8, req8
)
737 except WERRORError
as e1
:
738 (enum
, estr
) = e1
.args
739 self
.fail(f
"DsGetNCChanges failed with {estr}")
740 self
.assertEqual(level
, 6, "Expected level 6 response!")
741 self
._check
_exop
_failed
(ctr
, drsuapi
.DRSUAPI_EXOP_ERR_UNKNOWN_CALLER
)
742 self
.assertEqual(ctr
.source_dsa_guid
, misc
.GUID(fsmo_owner
["ntds_guid"]))
743 self
.assertEqual(ctr
.source_dsa_invocation_id
, misc
.GUID(fsmo_owner
["invocation_id"]))
745 def test_InvalidDestDSA_and_GUID_RID_ALLOC(self
):
746 """Test role transfer with invalid destination DSA guid"""
747 fsmo_dn
= self
.ldb_dc1
.get_schema_basedn()
748 (fsmo_owner
, fsmo_not_owner
) = self
._determine
_fSMORoleOwner
(fsmo_dn
)
750 req8
= self
._exop
_req
8(dest_dsa
="9c637462-5b8c-4467-aef2-bdb1f57bc4ef",
751 invocation_id
=fsmo_owner
["invocation_id"],
753 nc_guid
=misc
.GUID("c2d2f745-1610-4e93-964b-d4ba73eb32f8"),
754 exop
=drsuapi
.DRSUAPI_EXOP_FSMO_RID_ALLOC
)
756 (drs
, drs_handle
) = self
._ds
_bind
(fsmo_owner
["dns_name"])
758 (level
, ctr
) = drs
.DsGetNCChanges(drs_handle
, 8, req8
)
759 except WERRORError
as e1
:
760 (enum
, estr
) = e1
.args
761 self
.fail(f
"DsGetNCChanges failed with {estr}")
762 self
.assertEqual(level
, 6, "Expected level 6 response!")
763 self
._check
_exop
_failed
(ctr
, drsuapi
.DRSUAPI_EXOP_ERR_UNKNOWN_CALLER
)
764 self
.assertEqual(ctr
.source_dsa_guid
, misc
.GUID(fsmo_owner
["ntds_guid"]))
765 self
.assertEqual(ctr
.source_dsa_invocation_id
, misc
.GUID(fsmo_owner
["invocation_id"]))
768 class DrsReplicaPrefixMapTestCase(drs_base
.DrsBaseTestCase
):
770 super(DrsReplicaPrefixMapTestCase
, self
).setUp()
771 self
.base_dn
= self
.ldb_dc1
.get_default_basedn()
772 self
.ou
= "ou=pfm_exop%d,%s" % (random
.randint(0, 4294967295),
776 "objectclass": "organizationalUnit"})
777 self
.user
= "cn=testuser,%s" % self
.ou
780 "objectclass": "user"})
783 super(DrsReplicaPrefixMapTestCase
, self
).tearDown()
785 self
.ldb_dc1
.delete(self
.ou
, ["tree_delete:1"])
786 except ldb
.LdbError
as e2
:
787 (enum
, string
) = e2
.args
788 if enum
== ldb
.ERR_NO_SUCH_OBJECT
:
791 def test_missing_prefix_map_dsa(self
):
792 partial_attribute_set
= self
.get_partial_attribute_set()
794 dc_guid_1
= self
.ldb_dc1
.get_invocation_id()
796 drs
, drs_handle
= self
._ds
_bind
(self
.dnsname_dc1
, ip
=self
.url_dc1
)
798 req8
= self
._exop
_req
8(dest_dsa
=None,
799 invocation_id
=dc_guid_1
,
801 exop
=drsuapi
.DRSUAPI_EXOP_REPL_OBJ
,
802 partial_attribute_set
=partial_attribute_set
)
805 (level
, ctr
) = drs
.DsGetNCChanges(drs_handle
, 8, req8
)
806 self
.assertEqual(ctr
.extended_ret
, drsuapi
.DRSUAPI_EXOP_ERR_SUCCESS
)
808 self
.fail("Missing prefixmap shouldn't have triggered an error")
810 def test_invalid_prefix_map_attid(self
):
811 # Request for invalid attid
812 partial_attribute_set
= self
.get_partial_attribute_set([99999])
814 dc_guid_1
= self
.ldb_dc1
.get_invocation_id()
815 drs
, drs_handle
= self
._ds
_bind
(self
.dnsname_dc1
, ip
=self
.url_dc1
)
818 pfm
= self
._samdb
_fetch
_pfm
_and
_schi
()
820 # On Windows, prefixMap isn't available over LDAP
821 req8
= self
._exop
_req
8(dest_dsa
=None,
822 invocation_id
=dc_guid_1
,
824 exop
=drsuapi
.DRSUAPI_EXOP_REPL_OBJ
)
825 (level
, ctr
) = drs
.DsGetNCChanges(drs_handle
, 8, req8
)
826 pfm
= ctr
.mapping_ctr
828 req8
= self
._exop
_req
8(dest_dsa
=None,
829 invocation_id
=dc_guid_1
,
831 exop
=drsuapi
.DRSUAPI_EXOP_REPL_OBJ
,
832 partial_attribute_set
=partial_attribute_set
,
836 (level
, ctr
) = drs
.DsGetNCChanges(drs_handle
, 8, req8
)
837 self
.fail("Invalid attid (99999) should have triggered an error")
838 except RuntimeError as e3
:
839 (ecode
, emsg
) = e3
.args
840 self
.assertEqual(ecode
, 0x000020E2, "Error code should have been "
841 "WERR_DS_DRA_SCHEMA_MISMATCH")
843 def test_secret_prefix_map_attid(self
):
844 # Request for a secret attid
845 partial_attribute_set
= self
.get_partial_attribute_set([drsuapi
.DRSUAPI_ATTID_unicodePwd
])
847 dc_guid_1
= self
.ldb_dc1
.get_invocation_id()
848 drs
, drs_handle
= self
._ds
_bind
(self
.dnsname_dc1
, ip
=self
.url_dc1
)
851 pfm
= self
._samdb
_fetch
_pfm
_and
_schi
()
853 # On Windows, prefixMap isn't available over LDAP
854 req8
= self
._exop
_req
8(dest_dsa
=None,
855 invocation_id
=dc_guid_1
,
857 exop
=drsuapi
.DRSUAPI_EXOP_REPL_OBJ
)
858 (level
, ctr
) = drs
.DsGetNCChanges(drs_handle
, 8, req8
)
859 pfm
= ctr
.mapping_ctr
861 req8
= self
._exop
_req
8(dest_dsa
=None,
862 invocation_id
=dc_guid_1
,
864 exop
=drsuapi
.DRSUAPI_EXOP_REPL_OBJ
,
865 partial_attribute_set
=partial_attribute_set
,
868 (level
, ctr
) = drs
.DsGetNCChanges(drs_handle
, 8, req8
)
871 for attr
in ctr
.first_object
.object.attribute_ctr
.attributes
:
872 if attr
.attid
== drsuapi
.DRSUAPI_ATTID_unicodePwd
:
876 self
.assertTrue(found
, "Ensure we get the unicodePwd attribute back")
878 for i
, mapping
in enumerate(pfm
.mappings
):
880 # objectClass: 2.5.4.0
881 if mapping
.oid
.binary_oid
== [85, 4]:
883 # OID: 1.2.840.113556.1.4.*
884 # unicodePwd: 1.2.840.113556.1.4.90
885 elif mapping
.oid
.binary_oid
== [42, 134, 72, 134, 247, 20, 1, 4]:
888 (pfm
.mappings
[idx1
].id_prefix
,
889 pfm
.mappings
[idx2
].id_prefix
) = (pfm
.mappings
[idx2
].id_prefix
,
890 pfm
.mappings
[idx1
].id_prefix
)
893 tmp
[idx1
], tmp
[idx2
] = tmp
[idx2
], tmp
[idx1
]
896 # 90 for unicodePwd (with new prefix = 0)
897 # 589824, 589827 for objectClass and CN
898 # Use of three ensures sorting is correct
899 partial_attribute_set
= self
.get_partial_attribute_set([90, 589824, 589827])
900 req8
= self
._exop
_req
8(dest_dsa
=None,
901 invocation_id
=dc_guid_1
,
903 exop
=drsuapi
.DRSUAPI_EXOP_REPL_OBJ
,
904 partial_attribute_set
=partial_attribute_set
,
907 (level
, ctr
) = drs
.DsGetNCChanges(drs_handle
, 8, req8
)
910 for attr
in ctr
.first_object
.object.attribute_ctr
.attributes
:
911 if attr
.attid
== drsuapi
.DRSUAPI_ATTID_unicodePwd
:
915 self
.assertTrue(found
, "Ensure we get the unicodePwd attribute back")
917 def test_regular_prefix_map_attid(self
):
918 # Request for a regular (non-secret) attid
919 partial_attribute_set
= self
.get_partial_attribute_set([drsuapi
.DRSUAPI_ATTID_name
])
921 dc_guid_1
= self
.ldb_dc1
.get_invocation_id()
922 drs
, drs_handle
= self
._ds
_bind
(self
.dnsname_dc1
, ip
=self
.url_dc1
)
925 pfm
= self
._samdb
_fetch
_pfm
_and
_schi
()
927 # On Windows, prefixMap isn't available over LDAP
928 req8
= self
._exop
_req
8(dest_dsa
=None,
929 invocation_id
=dc_guid_1
,
931 exop
=drsuapi
.DRSUAPI_EXOP_REPL_OBJ
)
932 (level
, ctr
) = drs
.DsGetNCChanges(drs_handle
, 8, req8
)
933 pfm
= ctr
.mapping_ctr
935 req8
= self
._exop
_req
8(dest_dsa
=None,
936 invocation_id
=dc_guid_1
,
938 exop
=drsuapi
.DRSUAPI_EXOP_REPL_OBJ
,
939 partial_attribute_set
=partial_attribute_set
,
942 (level
, ctr
) = drs
.DsGetNCChanges(drs_handle
, 8, req8
)
945 for attr
in ctr
.first_object
.object.attribute_ctr
.attributes
:
946 if attr
.attid
== drsuapi
.DRSUAPI_ATTID_name
:
950 self
.assertTrue(found
, "Ensure we get the name attribute back")
952 for i
, mapping
in enumerate(pfm
.mappings
):
954 # objectClass: 2.5.4.0
955 if mapping
.oid
.binary_oid
== [85, 4]:
957 # OID: 1.2.840.113556.1.4.*
958 # name: 1.2.840.113556.1.4.1
959 elif mapping
.oid
.binary_oid
== [42, 134, 72, 134, 247, 20, 1, 4]:
962 (pfm
.mappings
[idx1
].id_prefix
,
963 pfm
.mappings
[idx2
].id_prefix
) = (pfm
.mappings
[idx2
].id_prefix
,
964 pfm
.mappings
[idx1
].id_prefix
)
967 tmp
[idx1
], tmp
[idx2
] = tmp
[idx2
], tmp
[idx1
]
970 # 1 for name (with new prefix = 0)
971 partial_attribute_set
= self
.get_partial_attribute_set([1])
972 req8
= self
._exop
_req
8(dest_dsa
=None,
973 invocation_id
=dc_guid_1
,
975 exop
=drsuapi
.DRSUAPI_EXOP_REPL_OBJ
,
976 partial_attribute_set
=partial_attribute_set
,
979 (level
, ctr
) = drs
.DsGetNCChanges(drs_handle
, 8, req8
)
982 for attr
in ctr
.first_object
.object.attribute_ctr
.attributes
:
983 if attr
.attid
== drsuapi
.DRSUAPI_ATTID_name
:
987 self
.assertTrue(found
, "Ensure we get the name attribute back")
989 def test_regular_prefix_map_ex_attid(self
):
990 # Request for a regular (non-secret) attid
991 partial_attribute_set
= self
.get_partial_attribute_set([drsuapi
.DRSUAPI_ATTID_name
])
992 partial_attribute_set_ex
= self
.get_partial_attribute_set([drsuapi
.DRSUAPI_ATTID_unicodePwd
])
994 dc_guid_1
= self
.ldb_dc1
.get_invocation_id()
995 drs
, drs_handle
= self
._ds
_bind
(self
.dnsname_dc1
, ip
=self
.url_dc1
)
998 pfm
= self
._samdb
_fetch
_pfm
_and
_schi
()
1000 # On Windows, prefixMap isn't available over LDAP
1001 req8
= self
._exop
_req
8(dest_dsa
=None,
1002 invocation_id
=dc_guid_1
,
1003 nc_dn_str
=self
.user
,
1004 exop
=drsuapi
.DRSUAPI_EXOP_REPL_OBJ
)
1005 (level
, ctr
) = drs
.DsGetNCChanges(drs_handle
, 8, req8
)
1006 pfm
= ctr
.mapping_ctr
1008 req8
= self
._exop
_req
8(dest_dsa
=None,
1009 invocation_id
=dc_guid_1
,
1010 nc_dn_str
=self
.user
,
1011 exop
=drsuapi
.DRSUAPI_EXOP_REPL_OBJ
,
1012 partial_attribute_set
=partial_attribute_set
,
1013 partial_attribute_set_ex
=partial_attribute_set_ex
,
1016 (level
, ctr
) = drs
.DsGetNCChanges(drs_handle
, 8, req8
)
1019 for attr
in ctr
.first_object
.object.attribute_ctr
.attributes
:
1020 if attr
.attid
== drsuapi
.DRSUAPI_ATTID_name
:
1024 self
.assertTrue(found
, "Ensure we get the name attribute back")
1027 for attr
in ctr
.first_object
.object.attribute_ctr
.attributes
:
1028 if attr
.attid
== drsuapi
.DRSUAPI_ATTID_unicodePwd
:
1032 self
.assertTrue(found
, "Ensure we get the unicodePwd attribute back")
1034 for i
, mapping
in enumerate(pfm
.mappings
):
1036 # objectClass: 2.5.4.0
1037 if mapping
.oid
.binary_oid
== [85, 4]:
1039 # OID: 1.2.840.113556.1.4.*
1040 # name: 1.2.840.113556.1.4.1
1041 # unicodePwd: 1.2.840.113556.1.4.90
1042 elif mapping
.oid
.binary_oid
== [42, 134, 72, 134, 247, 20, 1, 4]:
1045 (pfm
.mappings
[idx1
].id_prefix
,
1046 pfm
.mappings
[idx2
].id_prefix
) = (pfm
.mappings
[idx2
].id_prefix
,
1047 pfm
.mappings
[idx1
].id_prefix
)
1050 tmp
[idx1
], tmp
[idx2
] = tmp
[idx2
], tmp
[idx1
]
1053 # 1 for name (with new prefix = 0)
1054 partial_attribute_set
= self
.get_partial_attribute_set([1])
1055 # 90 for unicodePwd (with new prefix = 0)
1056 # HOWEVER: Windows doesn't seem to respect incoming maps for PartialAttrSetEx
1057 partial_attribute_set_ex
= self
.get_partial_attribute_set([drsuapi
.DRSUAPI_ATTID_unicodePwd
])
1058 req8
= self
._exop
_req
8(dest_dsa
=None,
1059 invocation_id
=dc_guid_1
,
1060 nc_dn_str
=self
.user
,
1061 exop
=drsuapi
.DRSUAPI_EXOP_REPL_OBJ
,
1062 partial_attribute_set
=partial_attribute_set
,
1063 partial_attribute_set_ex
=partial_attribute_set_ex
,
1066 (level
, ctr
) = drs
.DsGetNCChanges(drs_handle
, 8, req8
)
1069 for attr
in ctr
.first_object
.object.attribute_ctr
.attributes
:
1070 if attr
.attid
== drsuapi
.DRSUAPI_ATTID_name
:
1074 self
.assertTrue(found
, "Ensure we get the name attribute back")
1077 for attr
in ctr
.first_object
.object.attribute_ctr
.attributes
:
1078 if attr
.attid
== drsuapi
.DRSUAPI_ATTID_unicodePwd
:
1082 self
.assertTrue(found
, "Ensure we get the unicodePwd attribute back")
1084 def _samdb_fetch_pfm_and_schi(self
):
1085 """Fetch prefixMap and schemaInfo stored in SamDB using LDB connection"""
1086 samdb
= self
.ldb_dc1
1087 res
= samdb
.search(base
=samdb
.get_schema_basedn(), scope
=SCOPE_BASE
,
1088 attrs
=["prefixMap", "schemaInfo"])
1090 pfm
= ndr_unpack(drsblobs
.prefixMapBlob
,
1091 res
[0]['prefixMap'][0])
1093 schi
= drsuapi
.DsReplicaOIDMapping()
1095 if 'schemaInfo' in res
[0]:
1096 binary_oid
= list(res
[0]['schemaInfo'][0])
1097 schi
.oid
.length
= len(binary_oid
)
1098 schi
.oid
.binary_oid
= binary_oid
1100 schema_info
= drsblobs
.schemaInfoBlob()
1101 schema_info
.revision
= 0
1102 schema_info
.marker
= 0xFF
1103 schema_info
.invocation_id
= misc
.GUID(samdb
.get_invocation_id())
1105 binary_oid
= list(ndr_pack(schema_info
))
1106 # you have to set the length before setting binary_oid
1107 schi
.oid
.length
= len(binary_oid
)
1108 schi
.oid
.binary_oid
= binary_oid
1110 pfm
.ctr
.mappings
= pfm
.ctr
.mappings
+ [schi
]
1111 pfm
.ctr
.num_mappings
+= 1
1115 class DrsReplicaSyncSortTestCase(drs_base
.DrsBaseTestCase
):
1117 super(DrsReplicaSyncSortTestCase
, self
).setUp()
1118 self
.base_dn
= self
.ldb_dc1
.get_default_basedn()
1119 self
.ou
= "ou=sort_exop%d,%s" % (random
.randint(0, 4294967295),
1123 "objectclass": "organizationalUnit"})
1126 super(DrsReplicaSyncSortTestCase
, self
).tearDown()
1127 # tidyup groups and users
1129 self
.ldb_dc1
.delete(self
.ou
, ["tree_delete:1"])
1130 except ldb
.LdbError
as e4
:
1131 (enum
, string
) = e4
.args
1132 if enum
== ldb
.ERR_NO_SUCH_OBJECT
:
1135 def add_linked_attribute(self
, src
, dest
, attr
='member'):
1137 m
.dn
= ldb
.Dn(self
.ldb_dc1
, src
)
1138 m
[attr
] = ldb
.MessageElement(dest
, ldb
.FLAG_MOD_ADD
, attr
)
1139 self
.ldb_dc1
.modify(m
)
1141 def remove_linked_attribute(self
, src
, dest
, attr
='member'):
1143 m
.dn
= ldb
.Dn(self
.ldb_dc1
, src
)
1144 m
[attr
] = ldb
.MessageElement(dest
, ldb
.FLAG_MOD_DELETE
, attr
)
1145 self
.ldb_dc1
.modify(m
)
1147 def test_sort_behaviour_single_object(self
):
1148 """Testing sorting behaviour on single objects"""
1150 user1_dn
= "cn=test_user1,%s" % self
.ou
1151 user2_dn
= "cn=test_user2,%s" % self
.ou
1152 user3_dn
= "cn=test_user3,%s" % self
.ou
1153 group_dn
= "cn=test_group,%s" % self
.ou
1155 self
.ldb_dc1
.add({"dn": user1_dn
, "objectclass": "user"})
1156 self
.ldb_dc1
.add({"dn": user2_dn
, "objectclass": "user"})
1157 self
.ldb_dc1
.add({"dn": user3_dn
, "objectclass": "user"})
1158 self
.ldb_dc1
.add({"dn": group_dn
, "objectclass": "group"})
1160 u1_guid
= misc
.GUID(self
.ldb_dc1
.search(base
=user1_dn
,
1161 attrs
=["objectGUID"])[0]['objectGUID'][0])
1162 u2_guid
= misc
.GUID(self
.ldb_dc1
.search(base
=user2_dn
,
1163 attrs
=["objectGUID"])[0]['objectGUID'][0])
1164 u3_guid
= misc
.GUID(self
.ldb_dc1
.search(base
=user3_dn
,
1165 attrs
=["objectGUID"])[0]['objectGUID'][0])
1166 g_guid
= misc
.GUID(self
.ldb_dc1
.search(base
=group_dn
,
1167 attrs
=["objectGUID"])[0]['objectGUID'][0])
1169 self
.add_linked_attribute(group_dn
, user1_dn
,
1171 self
.add_linked_attribute(group_dn
, user2_dn
,
1173 self
.add_linked_attribute(group_dn
, user3_dn
,
1175 self
.add_linked_attribute(group_dn
, user1_dn
,
1177 self
.add_linked_attribute(group_dn
, user2_dn
,
1178 attr
='nonSecurityMember')
1179 self
.add_linked_attribute(group_dn
, user3_dn
,
1180 attr
='nonSecurityMember')
1182 set_inactive
= AbstractLink(drsuapi
.DRSUAPI_ATTID_nonSecurityMember
,
1183 drsuapi
.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
,
1186 expected_links
= set([set_inactive
,
1187 AbstractLink(drsuapi
.DRSUAPI_ATTID_member
,
1188 drsuapi
.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
,
1191 AbstractLink(drsuapi
.DRSUAPI_ATTID_member
,
1192 drsuapi
.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
,
1195 AbstractLink(drsuapi
.DRSUAPI_ATTID_member
,
1196 drsuapi
.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
,
1199 AbstractLink(drsuapi
.DRSUAPI_ATTID_managedBy
,
1200 drsuapi
.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
,
1203 AbstractLink(drsuapi
.DRSUAPI_ATTID_nonSecurityMember
,
1204 drsuapi
.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
,
1209 dc_guid_1
= self
.ldb_dc1
.get_invocation_id()
1211 drs
, drs_handle
= self
._ds
_bind
(self
.dnsname_dc1
, ip
=self
.url_dc1
)
1213 req8
= self
._exop
_req
8(dest_dsa
=None,
1214 invocation_id
=dc_guid_1
,
1216 exop
=drsuapi
.DRSUAPI_EXOP_REPL_OBJ
)
1218 (level
, ctr
) = drs
.DsGetNCChanges(drs_handle
, 8, req8
)
1221 for link
in ctr
.linked_attributes
:
1222 target_guid
= ndr_unpack(drsuapi
.DsReplicaObjectIdentifier3
,
1223 link
.value
.blob
).guid
1224 no_inactive
.append((link
, target_guid
))
1225 self
.assertTrue(AbstractLink(link
.attid
, link
.flags
,
1226 link
.identifier
.guid
,
1227 target_guid
) in expected_links
)
1229 no_inactive
.sort(key
=cmp_to_key(_linked_attribute_compare
))
1231 # assert the two arrays are the same
1232 self
.assertEqual(len(expected_links
), ctr
.linked_attributes_count
)
1233 self
.assertEqual([x
[0] for x
in no_inactive
], ctr
.linked_attributes
)
1235 self
.remove_linked_attribute(group_dn
, user3_dn
,
1236 attr
='nonSecurityMember')
1238 # Set the link inactive
1239 expected_links
.remove(set_inactive
)
1240 set_inactive
.flags
= 0
1241 expected_links
.add(set_inactive
)
1244 (level
, ctr
) = drs
.DsGetNCChanges(drs_handle
, 8, req8
)
1245 for link
in ctr
.linked_attributes
:
1246 target_guid
= ndr_unpack(drsuapi
.DsReplicaObjectIdentifier3
,
1247 link
.value
.blob
).guid
1248 has_inactive
.append((link
, target_guid
))
1249 self
.assertTrue(AbstractLink(link
.attid
, link
.flags
,
1250 link
.identifier
.guid
,
1251 target_guid
) in expected_links
)
1253 has_inactive
.sort(key
=cmp_to_key(_linked_attribute_compare
))
1255 # assert the two arrays are the same
1256 self
.assertEqual(len(expected_links
), ctr
.linked_attributes_count
)
1257 self
.assertEqual([x
[0] for x
in has_inactive
], ctr
.linked_attributes
)
1259 def test_sort_behaviour_ncchanges(self
):
1260 """Testing sorting behaviour on a group of objects."""
1261 user1_dn
= "cn=test_user1,%s" % self
.ou
1262 group_dn
= "cn=test_group,%s" % self
.ou
1263 self
.ldb_dc1
.add({"dn": user1_dn
, "objectclass": "user"})
1264 self
.ldb_dc1
.add({"dn": group_dn
, "objectclass": "group"})
1266 self
.add_linked_attribute(group_dn
, user1_dn
,
1269 dc_guid_1
= self
.ldb_dc1
.get_invocation_id()
1271 drs
, drs_handle
= self
._ds
_bind
(self
.dnsname_dc1
, ip
=self
.url_dc1
)
1273 # Make sure the max objects count is high enough
1274 req8
= self
._exop
_req
8(dest_dsa
=None,
1275 invocation_id
=dc_guid_1
,
1276 nc_dn_str
=self
.base_dn
,
1279 exop
=drsuapi
.DRSUAPI_EXOP_NONE
)
1281 # Loop until we get linked attributes, or we get to the end.
1282 # Samba sends linked attributes at the end, unlike Windows.
1284 (level
, ctr
) = drs
.DsGetNCChanges(drs_handle
, 8, req8
)
1285 if ctr
.more_data
== 0 or ctr
.linked_attributes_count
!= 0:
1287 req8
.highwatermark
= ctr
.new_highwatermark
1289 self
.assertTrue(ctr
.linked_attributes_count
!= 0)
1292 for link
in ctr
.linked_attributes
:
1294 target_guid
= ndr_unpack(drsuapi
.DsReplicaObjectIdentifier3
,
1295 link
.value
.blob
).guid
1297 target_guid
= ndr_unpack(drsuapi
.DsReplicaObjectIdentifier3Binary
,
1298 link
.value
.blob
).guid
1299 no_inactive
.append((link
, target_guid
))
1301 no_inactive
.sort(key
=cmp_to_key(_linked_attribute_compare
))
1303 # assert the two arrays are the same
1304 self
.assertEqual([x
[0] for x
in no_inactive
], ctr
.linked_attributes
)