2 # -*- coding: utf-8 -*-
8 sys.path.append("bin/python")
10 samba.ensure_external_module("testtools", "testtools")
11 samba.ensure_external_module("subunit", "subunit/python")
13 import samba.getopt as options
15 from samba.auth import system_session
16 from ldb import SCOPE_BASE, LdbError
17 from ldb import ERR_NO_SUCH_OBJECT, ERR_NOT_ALLOWED_ON_NON_LEAF
18 from ldb import ERR_UNWILLING_TO_PERFORM
21 from subunit.run import SubunitTestRunner
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 opts, args = parser.parse_args()
39 lp = sambaopts.get_loadparm()
40 creds = credopts.get_credentials(lp)
42 class BasicDeleteTests(unittest.TestCase):
44 def delete_force(self, ldb, dn):
47 except LdbError, (num, _):
48 self.assertEquals(num, ERR_NO_SUCH_OBJECT)
50 def GUID_string(self, guid):
51 return self.ldb.schema_format_value("objectGUID", guid)
53 def find_basedn(self, ldb):
54 res = ldb.search(base="", expression="", scope=SCOPE_BASE,
55 attrs=["defaultNamingContext"])
56 self.assertEquals(len(res), 1)
57 return res[0]["defaultNamingContext"][0]
59 def find_configurationdn(self, ldb):
60 res = ldb.search(base="", expression="", scope=SCOPE_BASE,
61 attrs=["configurationNamingContext"])
62 self.assertEquals(len(res), 1)
63 return res[0]["configurationNamingContext"][0]
67 self.base_dn = self.find_basedn(ldb)
68 self.configuration_dn = self.find_configurationdn(ldb)
70 def search_guid(self, guid):
71 print "SEARCH by GUID %s" % self.GUID_string(guid)
73 res = ldb.search(base="<GUID=%s>" % self.GUID_string(guid),
74 scope=SCOPE_BASE, controls=["show_deleted:1"])
75 self.assertEquals(len(res), 1)
78 def search_dn(self,dn):
79 print "SEARCH by DN %s" % dn
81 res = ldb.search(expression="(objectClass=*)",
84 controls=["show_deleted:1"])
85 self.assertEquals(len(res), 1)
88 def del_attr_values(self, delObj):
89 print "Checking attributes for %s" % delObj["dn"]
91 self.assertEquals(delObj["isDeleted"][0],"TRUE")
92 self.assertTrue(not("objectCategory" in delObj))
93 self.assertTrue(not("sAMAccountType" in delObj))
95 def preserved_attributes_list(self, liveObj, delObj):
96 print "Checking for preserved attributes list"
98 preserved_list = ["nTSecurityDescriptor", "attributeID", "attributeSyntax", "dNReferenceUpdate", "dNSHostName",
99 "flatName", "governsID", "groupType", "instanceType", "lDAPDisplayName", "legacyExchangeDN",
100 "isDeleted", "isRecycled", "lastKnownParent", "msDS-LastKnownRDN", "mS-DS-CreatorSID",
101 "mSMQOwnerID", "nCName", "objectClass", "distinguishedName", "objectGUID", "objectSid",
102 "oMSyntax", "proxiedObjectName", "name", "replPropertyMetaData", "sAMAccountName",
103 "securityIdentifier", "sIDHistory", "subClassOf", "systemFlags", "trustPartner", "trustDirection",
104 "trustType", "trustAttributes", "userAccountControl", "uSNChanged", "uSNCreated", "whenCreated"]
107 if a in preserved_list:
108 self.assertTrue(a in delObj)
110 def check_rdn(self, liveObj, delObj, rdnName):
111 print "Checking for correct rDN"
112 rdn=liveObj[rdnName][0]
113 rdn2=delObj[rdnName][0]
114 name2=delObj[rdnName][0]
115 guid=liveObj["objectGUID"][0]
116 self.assertEquals(rdn2, rdn + "\nDEL:" + self.GUID_string(guid))
117 self.assertEquals(name2, rdn + "\nDEL:" + self.GUID_string(guid))
119 def delete_deleted(self, ldb, dn):
120 print "Testing the deletion of the already deleted dn %s" % dn
125 except LdbError, (num, _):
126 self.assertEquals(num, ERR_NO_SUCH_OBJECT)
128 def test_delete_protection(self):
129 """Delete protection tests"""
133 self.delete_force(self.ldb, "cn=entry1,cn=ldaptestcontainer," + self.base_dn)
134 self.delete_force(self.ldb, "cn=entry2,cn=ldaptestcontainer," + self.base_dn)
135 self.delete_force(self.ldb, "cn=ldaptestcontainer," + self.base_dn)
138 "dn": "cn=ldaptestcontainer," + self.base_dn,
139 "objectclass": "container"})
141 "dn": "cn=entry1,cn=ldaptestcontainer," + self.base_dn,
142 "objectclass": "container"})
144 "dn": "cn=entry2,cn=ldaptestcontainer," + self.base_dn,
145 "objectclass": "container"})
148 ldb.delete("cn=ldaptestcontainer," + self.base_dn)
150 except LdbError, (num, _):
151 self.assertEquals(num, ERR_NOT_ALLOWED_ON_NON_LEAF)
153 ldb.delete("cn=ldaptestcontainer," + self.base_dn, ["tree_delete:1"])
156 res = ldb.search("cn=ldaptestcontainer," + self.base_dn,
157 scope=SCOPE_BASE, attrs=[])
159 except LdbError, (num, _):
160 self.assertEquals(num, ERR_NO_SUCH_OBJECT)
162 res = ldb.search("cn=entry1,cn=ldaptestcontainer," + self.base_dn,
163 scope=SCOPE_BASE, attrs=[])
165 except LdbError, (num, _):
166 self.assertEquals(num, ERR_NO_SUCH_OBJECT)
168 res = ldb.search("cn=entry2,cn=ldaptestcontainer," + self.base_dn,
169 scope=SCOPE_BASE, attrs=[])
171 except LdbError, (num, _):
172 self.assertEquals(num, ERR_NO_SUCH_OBJECT)
174 self.delete_force(self.ldb, "cn=entry1,cn=ldaptestcontainer," + self.base_dn)
175 self.delete_force(self.ldb, "cn=entry2,cn=ldaptestcontainer," + self.base_dn)
176 self.delete_force(self.ldb, "cn=ldaptestcontainer," + self.base_dn)
178 # Performs some protected object delete testing
180 res = ldb.search(base="", expression="", scope=SCOPE_BASE,
181 attrs=["dsServiceName", "dNSHostName"])
182 self.assertEquals(len(res), 1)
184 # Delete failing since DC's nTDSDSA object is protected
186 ldb.delete(res[0]["dsServiceName"][0])
188 except LdbError, (num, _):
189 self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
191 res = ldb.search(self.base_dn, attrs=["rIDSetReferences"],
192 expression="(&(objectClass=computer)(dNSHostName=" + res[0]["dNSHostName"][0] + "))")
193 self.assertEquals(len(res), 1)
195 # Deletes failing since DC's rIDSet object is protected
197 ldb.delete(res[0]["rIDSetReferences"][0])
199 except LdbError, (num, _):
200 self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
202 ldb.delete(res[0]["rIDSetReferences"][0], ["tree_delete:1"])
204 except LdbError, (num, _):
205 self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
207 # Deletes failing since three main crossRef objects are protected
210 ldb.delete("cn=Enterprise Schema,cn=Partitions," + self.configuration_dn)
212 except LdbError, (num, _):
213 self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
215 ldb.delete("cn=Enterprise Schema,cn=Partitions," + self.configuration_dn, ["tree_delete:1"])
217 except LdbError, (num, _):
218 self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
221 ldb.delete("cn=Enterprise Configuration,cn=Partitions," + self.configuration_dn)
223 except LdbError, (num, _):
224 self.assertEquals(num, ERR_NOT_ALLOWED_ON_NON_LEAF)
226 ldb.delete("cn=Enterprise Configuration,cn=Partitions," + self.configuration_dn, ["tree_delete:1"])
228 except LdbError, (num, _):
229 self.assertEquals(num, ERR_NOT_ALLOWED_ON_NON_LEAF)
231 res = ldb.search("cn=Partitions," + self.configuration_dn, attrs=[],
232 expression="(nCName=%s)" % self.base_dn)
233 self.assertEquals(len(res), 1)
236 ldb.delete(res[0].dn)
238 except LdbError, (num, _):
239 self.assertEquals(num, ERR_NOT_ALLOWED_ON_NON_LEAF)
241 ldb.delete(res[0].dn, ["tree_delete:1"])
243 except LdbError, (num, _):
244 self.assertEquals(num, ERR_NOT_ALLOWED_ON_NON_LEAF)
246 # Delete failing since "SYSTEM_FLAG_DISALLOW_DELETE"
248 ldb.delete("CN=Users," + self.base_dn)
250 except LdbError, (num, _):
251 self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
253 # Tree-delete failing since "isCriticalSystemObject"
255 ldb.delete("CN=Computers," + self.base_dn, ["tree_delete:1"])
257 except LdbError, (num, _):
258 self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
261 """Basic delete tests"""
265 usr1="cn=testuser,cn=users," + self.base_dn
266 usr2="cn=testuser2,cn=users," + self.base_dn
267 grp1="cn=testdelgroup1,cn=users," + self.base_dn
268 sit1="cn=testsite1,cn=sites," + self.configuration_dn
269 ss1="cn=NTDS Site Settings,cn=testsite1,cn=sites," + self.configuration_dn
270 srv1="cn=Servers,cn=testsite1,cn=sites," + self.configuration_dn
271 srv2="cn=TESTSRV,cn=Servers,cn=testsite1,cn=sites," + self.configuration_dn
273 self.delete_force(self.ldb, usr1)
274 self.delete_force(self.ldb, usr2)
275 self.delete_force(self.ldb, grp1)
276 self.delete_force(self.ldb, ss1)
277 self.delete_force(self.ldb, srv2)
278 self.delete_force(self.ldb, srv1)
279 self.delete_force(self.ldb, sit1)
283 "objectclass": "user",
284 "description": "test user description",
285 "samaccountname": "testuser"})
289 "objectclass": "user",
290 "description": "test user 2 description",
291 "samaccountname": "testuser2"})
295 "objectclass": "group",
296 "description": "test group",
297 "samaccountname": "testdelgroup1",
298 "member": [ usr1, usr2 ],
299 "isDeleted": "FALSE" })
303 "objectclass": "site" })
307 "objectclass": ["applicationSiteSettings", "nTDSSiteSettings"] })
311 "objectclass": "serversContainer" })
315 "objectClass": "server" })
317 objLive1 = self.search_dn(usr1)
318 guid1=objLive1["objectGUID"][0]
320 objLive2 = self.search_dn(usr2)
321 guid2=objLive2["objectGUID"][0]
323 objLive3 = self.search_dn(grp1)
324 guid3=objLive3["objectGUID"][0]
326 objLive4 = self.search_dn(sit1)
327 guid4=objLive4["objectGUID"][0]
329 objLive5 = self.search_dn(ss1)
330 guid5=objLive5["objectGUID"][0]
332 objLive6 = self.search_dn(srv1)
333 guid6=objLive6["objectGUID"][0]
335 objLive7 = self.search_dn(srv2)
336 guid7=objLive7["objectGUID"][0]
341 ldb.delete(srv1, ["tree_delete:1"])
342 ldb.delete(sit1, ["tree_delete:1"])
344 objDeleted1 = self.search_guid(guid1)
345 objDeleted2 = self.search_guid(guid2)
346 objDeleted3 = self.search_guid(guid3)
347 objDeleted4 = self.search_guid(guid4)
348 objDeleted5 = self.search_guid(guid5)
349 objDeleted6 = self.search_guid(guid6)
350 objDeleted7 = self.search_guid(guid7)
352 self.del_attr_values(objDeleted1)
353 self.del_attr_values(objDeleted2)
354 self.del_attr_values(objDeleted3)
355 self.del_attr_values(objDeleted4)
356 self.del_attr_values(objDeleted5)
357 self.del_attr_values(objDeleted6)
358 self.del_attr_values(objDeleted7)
360 self.preserved_attributes_list(objLive1, objDeleted1)
361 self.preserved_attributes_list(objLive2, objDeleted2)
362 self.preserved_attributes_list(objLive3, objDeleted3)
363 self.preserved_attributes_list(objLive4, objDeleted4)
364 self.preserved_attributes_list(objLive5, objDeleted5)
365 self.preserved_attributes_list(objLive6, objDeleted6)
366 self.preserved_attributes_list(objLive7, objDeleted7)
368 self.check_rdn(objLive1, objDeleted1, "cn")
369 self.check_rdn(objLive2, objDeleted2, "cn")
370 self.check_rdn(objLive3, objDeleted3, "cn")
371 self.check_rdn(objLive4, objDeleted4, "cn")
372 self.check_rdn(objLive5, objDeleted5, "cn")
373 self.check_rdn(objLive6, objDeleted6, "cn")
374 self.check_rdn(objLive7, objDeleted7, "cn")
376 self.delete_deleted(ldb, usr1)
377 self.delete_deleted(ldb, usr2)
378 self.delete_deleted(ldb, grp1)
379 self.delete_deleted(ldb, sit1)
380 self.delete_deleted(ldb, ss1)
381 self.delete_deleted(ldb, srv1)
382 self.delete_deleted(ldb, srv2)
384 self.assertTrue("CN=Deleted Objects" in str(objDeleted1.dn))
385 self.assertTrue("CN=Deleted Objects" in str(objDeleted2.dn))
386 self.assertTrue("CN=Deleted Objects" in str(objDeleted3.dn))
387 self.assertFalse("CN=Deleted Objects" in str(objDeleted4.dn))
388 self.assertTrue("CN=Deleted Objects" in str(objDeleted5.dn))
389 self.assertFalse("CN=Deleted Objects" in str(objDeleted6.dn))
390 self.assertFalse("CN=Deleted Objects" in str(objDeleted7.dn))
392 if not "://" in host:
393 if os.path.isfile(host):
394 host = "tdb://%s" % host
396 host = "ldap://%s" % host
398 ldb = Ldb(host, credentials=creds, session_info=system_session(), lp=lp)
400 runner = SubunitTestRunner()
402 if not runner.run(unittest.makeSuite(BasicDeleteTests)).wasSuccessful():