2 # -*- coding: utf-8 -*-
4 from __future__ import print_function
9 sys.path.insert(0, "bin/python")
12 from samba.tests.subunitrun import SubunitOptions, TestProgram
14 import samba.getopt as options
16 from samba.auth import system_session
17 from ldb import SCOPE_BASE, LdbError, Message, MessageElement, Dn, FLAG_MOD_ADD, FLAG_MOD_DELETE, FLAG_MOD_REPLACE
18 from ldb import ERR_NO_SUCH_OBJECT, ERR_NOT_ALLOWED_ON_NON_LEAF, ERR_ENTRY_ALREADY_EXISTS, ERR_ATTRIBUTE_OR_VALUE_EXISTS
19 from ldb import ERR_UNWILLING_TO_PERFORM, ERR_OPERATIONS_ERROR
20 from samba.samdb import SamDB
21 from samba.tests import delete_force
22 from samba import dsdb
24 parser = optparse.OptionParser("deletetest.py [options] <host|file>")
25 sambaopts = options.SambaOptions(parser)
26 parser.add_option_group(sambaopts)
27 parser.add_option_group(options.VersionOptions(parser))
28 # use command line creds if available
29 credopts = options.CredentialsOptions(parser)
30 parser.add_option_group(credopts)
31 subunitopts = SubunitOptions(parser)
32 parser.add_option_group(subunitopts)
33 opts, args = parser.parse_args()
41 lp = sambaopts.get_loadparm()
42 creds = credopts.get_credentials(lp)
44 class BaseDeleteTests(samba.tests.TestCase):
46 def GUID_string(self, guid):
47 return self.ldb.schema_format_value("objectGUID", guid)
50 super(BaseDeleteTests, self).setUp()
51 self.ldb = SamDB(host, credentials=creds, session_info=system_session(lp), lp=lp)
53 self.base_dn = self.ldb.domain_dn()
54 self.configuration_dn = self.ldb.get_config_basedn().get_linearized()
56 def search_guid(self, guid):
57 print("SEARCH by GUID %s" % self.GUID_string(guid))
59 res = self.ldb.search(base="<GUID=%s>" % self.GUID_string(guid),
61 controls=["show_deleted:1"],
62 attrs=["*", "parentGUID"])
63 self.assertEquals(len(res), 1)
66 def search_dn(self,dn):
67 print("SEARCH by DN %s" % dn)
69 res = self.ldb.search(expression="(objectClass=*)",
72 controls=["show_deleted:1"],
73 attrs=["*", "parentGUID"])
74 self.assertEquals(len(res), 1)
78 class BasicDeleteTests(BaseDeleteTests):
81 super(BasicDeleteTests, self).setUp()
83 def del_attr_values(self, delObj):
84 print("Checking attributes for %s" % delObj["dn"])
86 self.assertEquals(delObj["isDeleted"][0],"TRUE")
87 self.assertTrue(not("objectCategory" in delObj))
88 self.assertTrue(not("sAMAccountType" in delObj))
90 def preserved_attributes_list(self, liveObj, delObj):
91 print("Checking for preserved attributes list")
93 preserved_list = ["nTSecurityDescriptor", "attributeID", "attributeSyntax", "dNReferenceUpdate", "dNSHostName",
94 "flatName", "governsID", "groupType", "instanceType", "lDAPDisplayName", "legacyExchangeDN",
95 "isDeleted", "isRecycled", "lastKnownParent", "msDS-LastKnownRDN", "mS-DS-CreatorSID",
96 "mSMQOwnerID", "nCName", "objectClass", "distinguishedName", "objectGUID", "objectSid",
97 "oMSyntax", "proxiedObjectName", "name", "replPropertyMetaData", "sAMAccountName",
98 "securityIdentifier", "sIDHistory", "subClassOf", "systemFlags", "trustPartner", "trustDirection",
99 "trustType", "trustAttributes", "userAccountControl", "uSNChanged", "uSNCreated", "whenCreated"]
102 if a in preserved_list:
103 self.assertTrue(a in delObj)
105 def check_rdn(self, liveObj, delObj, rdnName):
106 print("Checking for correct rDN")
107 rdn=liveObj[rdnName][0]
108 rdn2=delObj[rdnName][0]
109 name2=delObj["name"][0]
110 dn_rdn=delObj.dn.get_rdn_value()
111 guid=liveObj["objectGUID"][0]
112 self.assertEquals(rdn2, rdn + "\nDEL:" + self.GUID_string(guid))
113 self.assertEquals(name2, rdn + "\nDEL:" + self.GUID_string(guid))
114 self.assertEquals(name2, dn_rdn)
116 def delete_deleted(self, ldb, dn):
117 print("Testing the deletion of the already deleted dn %s" % dn)
122 except LdbError as e:
124 self.assertEquals(num, ERR_NO_SUCH_OBJECT)
126 def test_delete_protection(self):
127 """Delete protection tests"""
131 delete_force(self.ldb, "cn=entry1,cn=ldaptestcontainer," + self.base_dn)
132 delete_force(self.ldb, "cn=entry2,cn=ldaptestcontainer," + self.base_dn)
133 delete_force(self.ldb, "cn=ldaptestcontainer," + self.base_dn)
136 "dn": "cn=ldaptestcontainer," + self.base_dn,
137 "objectclass": "container"})
139 "dn": "cn=entry1,cn=ldaptestcontainer," + self.base_dn,
140 "objectclass": "container"})
142 "dn": "cn=entry2,cn=ldaptestcontainer," + self.base_dn,
143 "objectclass": "container"})
146 self.ldb.delete("cn=ldaptestcontainer," + self.base_dn)
148 except LdbError as e1:
150 self.assertEquals(num, ERR_NOT_ALLOWED_ON_NON_LEAF)
152 self.ldb.delete("cn=ldaptestcontainer," + self.base_dn, ["tree_delete:1"])
155 res = self.ldb.search("cn=ldaptestcontainer," + self.base_dn,
156 scope=SCOPE_BASE, attrs=[])
158 except LdbError as e2:
160 self.assertEquals(num, ERR_NO_SUCH_OBJECT)
162 res = self.ldb.search("cn=entry1,cn=ldaptestcontainer," + self.base_dn,
163 scope=SCOPE_BASE, attrs=[])
165 except LdbError as e3:
167 self.assertEquals(num, ERR_NO_SUCH_OBJECT)
169 res = self.ldb.search("cn=entry2,cn=ldaptestcontainer," + self.base_dn,
170 scope=SCOPE_BASE, attrs=[])
172 except LdbError as e4:
174 self.assertEquals(num, ERR_NO_SUCH_OBJECT)
176 delete_force(self.ldb, "cn=entry1,cn=ldaptestcontainer," + self.base_dn)
177 delete_force(self.ldb, "cn=entry2,cn=ldaptestcontainer," + self.base_dn)
178 delete_force(self.ldb, "cn=ldaptestcontainer," + self.base_dn)
180 # Performs some protected object delete testing
182 res = self.ldb.search(base="", expression="", scope=SCOPE_BASE,
183 attrs=["dsServiceName", "dNSHostName"])
184 self.assertEquals(len(res), 1)
186 # Delete failing since DC's nTDSDSA object is protected
188 self.ldb.delete(res[0]["dsServiceName"][0])
190 except LdbError as e5:
192 self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
194 res = self.ldb.search(self.base_dn, attrs=["rIDSetReferences"],
195 expression="(&(objectClass=computer)(dNSHostName=" + res[0]["dNSHostName"][0] + "))")
196 self.assertEquals(len(res), 1)
198 # Deletes failing since DC's rIDSet object is protected
200 self.ldb.delete(res[0]["rIDSetReferences"][0])
202 except LdbError as e6:
204 self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
206 self.ldb.delete(res[0]["rIDSetReferences"][0], ["tree_delete:1"])
208 except LdbError as e7:
210 self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
212 # Deletes failing since three main crossRef objects are protected
215 self.ldb.delete("cn=Enterprise Schema,cn=Partitions," + self.configuration_dn)
217 except LdbError as e8:
219 self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
221 self.ldb.delete("cn=Enterprise Schema,cn=Partitions," + self.configuration_dn, ["tree_delete:1"])
223 except LdbError as e9:
225 self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
228 self.ldb.delete("cn=Enterprise Configuration,cn=Partitions," + self.configuration_dn)
230 except LdbError as e10:
232 self.assertEquals(num, ERR_NOT_ALLOWED_ON_NON_LEAF)
234 self.ldb.delete("cn=Enterprise Configuration,cn=Partitions," + self.configuration_dn, ["tree_delete:1"])
236 except LdbError as e11:
238 self.assertEquals(num, ERR_NOT_ALLOWED_ON_NON_LEAF)
240 res = self.ldb.search("cn=Partitions," + self.configuration_dn, attrs=[],
241 expression="(nCName=%s)" % self.base_dn)
242 self.assertEquals(len(res), 1)
245 self.ldb.delete(res[0].dn)
247 except LdbError as e12:
249 self.assertEquals(num, ERR_NOT_ALLOWED_ON_NON_LEAF)
251 self.ldb.delete(res[0].dn, ["tree_delete:1"])
253 except LdbError as e13:
255 self.assertEquals(num, ERR_NOT_ALLOWED_ON_NON_LEAF)
257 # Delete failing since "SYSTEM_FLAG_DISALLOW_DELETE"
259 self.ldb.delete("CN=Users," + self.base_dn)
261 except LdbError as e14:
263 self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
265 # Tree-delete failing since "isCriticalSystemObject"
267 self.ldb.delete("CN=Computers," + self.base_dn, ["tree_delete:1"])
269 except LdbError as e15:
271 self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
273 class BasicTreeDeleteTests(BasicDeleteTests):
276 super(BasicTreeDeleteTests, self).setUp()
278 # user current time in ms to make unique objects
280 marker = str(int(round(time.time()*1000)))
281 usr1_name = "u_" + marker
282 usr2_name = "u2_" + marker
283 grp_name = "g1_" + marker
284 site_name = "s1_" + marker
286 self.usr1 = "cn=%s,cn=users,%s" % (usr1_name, self.base_dn)
287 self.usr2 = "cn=%s,cn=users,%s" % (usr2_name, self.base_dn)
288 self.grp1 = "cn=%s,cn=users,%s" % (grp_name, self.base_dn)
289 self.sit1 = "cn=%s,cn=sites,%s" % (site_name, self.configuration_dn)
290 self.ss1 = "cn=NTDS Site Settings,cn=%s,cn=sites,%s" % (site_name, self.configuration_dn)
291 self.srv1 = "cn=Servers,cn=%s,cn=sites,%s" % (site_name, self.configuration_dn)
292 self.srv2 = "cn=TESTSRV,cn=Servers,cn=%s,cn=sites,%s" % (site_name, self.configuration_dn)
294 delete_force(self.ldb, self.usr1)
295 delete_force(self.ldb, self.usr2)
296 delete_force(self.ldb, self.grp1)
297 delete_force(self.ldb, self.ss1)
298 delete_force(self.ldb, self.srv2)
299 delete_force(self.ldb, self.srv1)
300 delete_force(self.ldb, self.sit1)
304 "objectclass": "user",
305 "description": "test user description",
306 "samaccountname": usr1_name})
310 "objectclass": "user",
311 "description": "test user 2 description",
312 "samaccountname": usr2_name})
316 "objectclass": "group",
317 "description": "test group",
318 "samaccountname": grp_name,
319 "member": [self.usr1, self.usr2],
320 "isDeleted": "FALSE"})
324 "objectclass": "site"})
328 "objectclass": ["applicationSiteSettings", "nTDSSiteSettings"]})
332 "objectclass": "serversContainer"})
336 "objectClass": "server"})
338 self.objLive1 = self.search_dn(self.usr1)
339 self.guid1=self.objLive1["objectGUID"][0]
341 self.objLive2 = self.search_dn(self.usr2)
342 self.guid2=self.objLive2["objectGUID"][0]
344 self.objLive3 = self.search_dn(self.grp1)
345 self.guid3=self.objLive3["objectGUID"][0]
347 self.objLive4 = self.search_dn(self.sit1)
348 self.guid4=self.objLive4["objectGUID"][0]
350 self.objLive5 = self.search_dn(self.ss1)
351 self.guid5=self.objLive5["objectGUID"][0]
353 self.objLive6 = self.search_dn(self.srv1)
354 self.guid6=self.objLive6["objectGUID"][0]
356 self.objLive7 = self.search_dn(self.srv2)
357 self.guid7=self.objLive7["objectGUID"][0]
359 self.deleted_objects_config_dn \
360 = self.ldb.get_wellknown_dn(self.ldb.get_config_basedn(),
361 dsdb.DS_GUID_DELETED_OBJECTS_CONTAINER)
362 deleted_objects_config_obj \
363 = self.search_dn(self.deleted_objects_config_dn)
365 self.deleted_objects_config_guid \
366 = deleted_objects_config_obj["objectGUID"][0]
368 self.deleted_objects_domain_dn \
369 = self.ldb.get_wellknown_dn(self.ldb.get_default_basedn(),
370 dsdb.DS_GUID_DELETED_OBJECTS_CONTAINER)
371 deleted_objects_domain_obj \
372 = self.search_dn(self.deleted_objects_domain_dn)
374 self.deleted_objects_domain_guid \
375 = deleted_objects_domain_obj["objectGUID"][0]
377 self.deleted_objects_domain_dn \
378 = self.ldb.get_wellknown_dn(self.ldb.get_default_basedn(),
379 dsdb.DS_GUID_DELETED_OBJECTS_CONTAINER)
380 sites_obj = self.search_dn("cn=sites,%s" \
381 % self.ldb.get_config_basedn())
382 self.sites_dn = sites_obj.dn
384 = sites_obj["objectGUID"][0]
387 """Basic delete tests"""
389 self.ldb.delete(self.usr1)
390 self.ldb.delete(self.usr2)
391 self.ldb.delete(self.grp1)
392 self.ldb.delete(self.srv1, ["tree_delete:1"])
393 self.ldb.delete(self.sit1, ["tree_delete:1"])
397 def test_tree_delete(self):
398 """Basic delete tests,
399 but use just one tree delete for the config records
402 self.ldb.delete(self.usr1)
403 self.ldb.delete(self.usr2)
404 self.ldb.delete(self.grp1)
405 self.ldb.delete(self.sit1, ["tree_delete:1"])
410 objDeleted1 = self.search_guid(self.guid1)
411 objDeleted2 = self.search_guid(self.guid2)
412 objDeleted3 = self.search_guid(self.guid3)
413 objDeleted4 = self.search_guid(self.guid4)
414 objDeleted5 = self.search_guid(self.guid5)
415 objDeleted6 = self.search_guid(self.guid6)
416 objDeleted7 = self.search_guid(self.guid7)
418 self.del_attr_values(objDeleted1)
419 self.del_attr_values(objDeleted2)
420 self.del_attr_values(objDeleted3)
421 self.del_attr_values(objDeleted4)
422 self.del_attr_values(objDeleted5)
423 self.del_attr_values(objDeleted6)
424 self.del_attr_values(objDeleted7)
426 self.preserved_attributes_list(self.objLive1, objDeleted1)
427 self.preserved_attributes_list(self.objLive2, objDeleted2)
428 self.preserved_attributes_list(self.objLive3, objDeleted3)
429 self.preserved_attributes_list(self.objLive4, objDeleted4)
430 self.preserved_attributes_list(self.objLive5, objDeleted5)
431 self.preserved_attributes_list(self.objLive6, objDeleted6)
432 self.preserved_attributes_list(self.objLive7, objDeleted7)
434 self.check_rdn(self.objLive1, objDeleted1, "cn")
435 self.check_rdn(self.objLive2, objDeleted2, "cn")
436 self.check_rdn(self.objLive3, objDeleted3, "cn")
437 self.check_rdn(self.objLive4, objDeleted4, "cn")
438 self.check_rdn(self.objLive5, objDeleted5, "cn")
439 self.check_rdn(self.objLive6, objDeleted6, "cn")
440 self.check_rdn(self.objLive7, objDeleted7, "cn")
442 self.delete_deleted(self.ldb, self.usr1)
443 self.delete_deleted(self.ldb, self.usr2)
444 self.delete_deleted(self.ldb, self.grp1)
445 self.delete_deleted(self.ldb, self.sit1)
446 self.delete_deleted(self.ldb, self.ss1)
447 self.delete_deleted(self.ldb, self.srv1)
448 self.delete_deleted(self.ldb, self.srv2)
450 self.assertTrue("CN=Deleted Objects" in str(objDeleted1.dn))
451 self.assertEqual(objDeleted1.dn.parent(),
452 self.deleted_objects_domain_dn)
453 self.assertEqual(objDeleted1["parentGUID"][0],
454 self.deleted_objects_domain_guid)
456 self.assertTrue("CN=Deleted Objects" in str(objDeleted2.dn))
457 self.assertEqual(objDeleted2.dn.parent(),
458 self.deleted_objects_domain_dn)
459 self.assertEqual(objDeleted2["parentGUID"][0],
460 self.deleted_objects_domain_guid)
462 self.assertTrue("CN=Deleted Objects" in str(objDeleted3.dn))
463 self.assertEqual(objDeleted3.dn.parent(),
464 self.deleted_objects_domain_dn)
465 self.assertEqual(objDeleted3["parentGUID"][0],
466 self.deleted_objects_domain_guid)
468 self.assertFalse("CN=Deleted Objects" in str(objDeleted4.dn))
469 self.assertEqual(objDeleted4.dn.parent(),
471 self.assertEqual(objDeleted4["parentGUID"][0],
474 self.assertTrue("CN=Deleted Objects" in str(objDeleted5.dn))
475 self.assertEqual(objDeleted5.dn.parent(),
476 self.deleted_objects_config_dn)
477 self.assertEqual(objDeleted5["parentGUID"][0],
478 self.deleted_objects_config_guid)
480 self.assertFalse("CN=Deleted Objects" in str(objDeleted6.dn))
481 self.assertEqual(objDeleted6.dn.parent(),
483 self.assertEqual(objDeleted6["parentGUID"][0],
484 objDeleted4["objectGUID"][0])
486 self.assertFalse("CN=Deleted Objects" in str(objDeleted7.dn))
487 self.assertEqual(objDeleted7.dn.parent(),
489 self.assertEqual(objDeleted7["parentGUID"][0],
490 objDeleted6["objectGUID"][0])
493 objDeleted1 = self.search_guid(self.guid1)
494 objDeleted2 = self.search_guid(self.guid2)
495 objDeleted3 = self.search_guid(self.guid3)
496 objDeleted4 = self.search_guid(self.guid4)
497 objDeleted5 = self.search_guid(self.guid5)
498 objDeleted6 = self.search_guid(self.guid6)
499 objDeleted7 = self.search_guid(self.guid7)
501 self.del_attr_values(objDeleted1)
502 self.del_attr_values(objDeleted2)
503 self.del_attr_values(objDeleted3)
504 self.del_attr_values(objDeleted4)
505 self.del_attr_values(objDeleted5)
506 self.del_attr_values(objDeleted6)
507 self.del_attr_values(objDeleted7)
509 self.preserved_attributes_list(self.objLive1, objDeleted1)
510 self.preserved_attributes_list(self.objLive2, objDeleted2)
511 self.preserved_attributes_list(self.objLive3, objDeleted3)
512 self.preserved_attributes_list(self.objLive4, objDeleted4)
513 self.preserved_attributes_list(self.objLive5, objDeleted5)
514 self.preserved_attributes_list(self.objLive6, objDeleted6)
515 self.preserved_attributes_list(self.objLive7, objDeleted7)
517 self.check_rdn(self.objLive1, objDeleted1, "cn")
518 self.check_rdn(self.objLive2, objDeleted2, "cn")
519 self.check_rdn(self.objLive3, objDeleted3, "cn")
520 self.check_rdn(self.objLive4, objDeleted4, "cn")
521 self.check_rdn(self.objLive5, objDeleted5, "cn")
522 self.check_rdn(self.objLive6, objDeleted6, "cn")
523 self.check_rdn(self.objLive7, objDeleted7, "cn")
525 self.delete_deleted(self.ldb, self.usr1)
526 self.delete_deleted(self.ldb, self.usr2)
527 self.delete_deleted(self.ldb, self.grp1)
528 self.delete_deleted(self.ldb, self.sit1)
529 self.delete_deleted(self.ldb, self.ss1)
530 self.delete_deleted(self.ldb, self.srv1)
531 self.delete_deleted(self.ldb, self.srv2)
533 self.assertTrue("CN=Deleted Objects" in str(objDeleted1.dn))
534 self.assertEqual(objDeleted1.dn.parent(),
535 self.deleted_objects_domain_dn)
536 self.assertEqual(objDeleted1["parentGUID"][0],
537 self.deleted_objects_domain_guid)
538 self.assertTrue("CN=Deleted Objects" in str(objDeleted2.dn))
539 self.assertEqual(objDeleted2.dn.parent(),
540 self.deleted_objects_domain_dn)
541 self.assertEqual(objDeleted2["parentGUID"][0],
542 self.deleted_objects_domain_guid)
543 self.assertTrue("CN=Deleted Objects" in str(objDeleted3.dn))
544 self.assertEqual(objDeleted3.dn.parent(),
545 self.deleted_objects_domain_dn)
546 self.assertEqual(objDeleted3["parentGUID"][0],
547 self.deleted_objects_domain_guid)
548 self.assertFalse("CN=Deleted Objects" in str(objDeleted4.dn))
549 self.assertTrue("CN=Deleted Objects" in str(objDeleted5.dn))
550 self.assertEqual(objDeleted5.dn.parent(),
551 self.deleted_objects_config_dn)
552 self.assertEqual(objDeleted5["parentGUID"][0],
553 self.deleted_objects_config_guid)
554 self.assertFalse("CN=Deleted Objects" in str(objDeleted6.dn))
555 self.assertFalse("CN=Deleted Objects" in str(objDeleted7.dn))
559 if not "://" in host:
560 if os.path.isfile(host):
561 host = "tdb://%s" % host
563 host = "ldap://%s" % host
565 TestProgram(module=__name__, opts=subunitopts)