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
19 from samba
.samdb
import SamDB
20 from samba
.tests
import delete_force
22 from subunit
.run
import SubunitTestRunner
25 parser
= optparse
.OptionParser("deletetest.py [options] <host|file>")
26 sambaopts
= options
.SambaOptions(parser
)
27 parser
.add_option_group(sambaopts
)
28 parser
.add_option_group(options
.VersionOptions(parser
))
29 # use command line creds if available
30 credopts
= options
.CredentialsOptions(parser
)
31 parser
.add_option_group(credopts
)
32 opts
, args
= parser
.parse_args()
40 lp
= sambaopts
.get_loadparm()
41 creds
= credopts
.get_credentials(lp
)
43 class BasicDeleteTests(unittest
.TestCase
):
46 def GUID_string(self
, guid
):
47 return self
.ldb
.schema_format_value("objectGUID", guid
)
51 self
.base_dn
= ldb
.domain_dn()
52 self
.configuration_dn
= ldb
.get_config_basedn().get_linearized()
54 def search_guid(self
, guid
):
55 print "SEARCH by GUID %s" % self
.GUID_string(guid
)
57 res
= ldb
.search(base
="<GUID=%s>" % self
.GUID_string(guid
),
58 scope
=SCOPE_BASE
, controls
=["show_deleted:1"])
59 self
.assertEquals(len(res
), 1)
62 def search_dn(self
,dn
):
63 print "SEARCH by DN %s" % dn
65 res
= ldb
.search(expression
="(objectClass=*)",
68 controls
=["show_deleted:1"])
69 self
.assertEquals(len(res
), 1)
72 def del_attr_values(self
, delObj
):
73 print "Checking attributes for %s" % delObj
["dn"]
75 self
.assertEquals(delObj
["isDeleted"][0],"TRUE")
76 self
.assertTrue(not("objectCategory" in delObj
))
77 self
.assertTrue(not("sAMAccountType" in delObj
))
79 def preserved_attributes_list(self
, liveObj
, delObj
):
80 print "Checking for preserved attributes list"
82 preserved_list
= ["nTSecurityDescriptor", "attributeID", "attributeSyntax", "dNReferenceUpdate", "dNSHostName",
83 "flatName", "governsID", "groupType", "instanceType", "lDAPDisplayName", "legacyExchangeDN",
84 "isDeleted", "isRecycled", "lastKnownParent", "msDS-LastKnownRDN", "mS-DS-CreatorSID",
85 "mSMQOwnerID", "nCName", "objectClass", "distinguishedName", "objectGUID", "objectSid",
86 "oMSyntax", "proxiedObjectName", "name", "replPropertyMetaData", "sAMAccountName",
87 "securityIdentifier", "sIDHistory", "subClassOf", "systemFlags", "trustPartner", "trustDirection",
88 "trustType", "trustAttributes", "userAccountControl", "uSNChanged", "uSNCreated", "whenCreated"]
91 if a
in preserved_list
:
92 self
.assertTrue(a
in delObj
)
94 def check_rdn(self
, liveObj
, delObj
, rdnName
):
95 print "Checking for correct rDN"
96 rdn
=liveObj
[rdnName
][0]
97 rdn2
=delObj
[rdnName
][0]
98 name2
=delObj
[rdnName
][0]
99 guid
=liveObj
["objectGUID"][0]
100 self
.assertEquals(rdn2
, rdn
+ "\nDEL:" + self
.GUID_string(guid
))
101 self
.assertEquals(name2
, rdn
+ "\nDEL:" + self
.GUID_string(guid
))
103 def delete_deleted(self
, ldb
, dn
):
104 print "Testing the deletion of the already deleted dn %s" % dn
109 except LdbError
, (num
, _
):
110 self
.assertEquals(num
, ERR_NO_SUCH_OBJECT
)
112 def test_delete_protection(self
):
113 """Delete protection tests"""
117 delete_force(self
.ldb
, "cn=entry1,cn=ldaptestcontainer," + self
.base_dn
)
118 delete_force(self
.ldb
, "cn=entry2,cn=ldaptestcontainer," + self
.base_dn
)
119 delete_force(self
.ldb
, "cn=ldaptestcontainer," + self
.base_dn
)
122 "dn": "cn=ldaptestcontainer," + self
.base_dn
,
123 "objectclass": "container"})
125 "dn": "cn=entry1,cn=ldaptestcontainer," + self
.base_dn
,
126 "objectclass": "container"})
128 "dn": "cn=entry2,cn=ldaptestcontainer," + self
.base_dn
,
129 "objectclass": "container"})
132 ldb
.delete("cn=ldaptestcontainer," + self
.base_dn
)
134 except LdbError
, (num
, _
):
135 self
.assertEquals(num
, ERR_NOT_ALLOWED_ON_NON_LEAF
)
137 ldb
.delete("cn=ldaptestcontainer," + self
.base_dn
, ["tree_delete:1"])
140 res
= ldb
.search("cn=ldaptestcontainer," + self
.base_dn
,
141 scope
=SCOPE_BASE
, attrs
=[])
143 except LdbError
, (num
, _
):
144 self
.assertEquals(num
, ERR_NO_SUCH_OBJECT
)
146 res
= ldb
.search("cn=entry1,cn=ldaptestcontainer," + self
.base_dn
,
147 scope
=SCOPE_BASE
, attrs
=[])
149 except LdbError
, (num
, _
):
150 self
.assertEquals(num
, ERR_NO_SUCH_OBJECT
)
152 res
= ldb
.search("cn=entry2,cn=ldaptestcontainer," + self
.base_dn
,
153 scope
=SCOPE_BASE
, attrs
=[])
155 except LdbError
, (num
, _
):
156 self
.assertEquals(num
, ERR_NO_SUCH_OBJECT
)
158 delete_force(self
.ldb
, "cn=entry1,cn=ldaptestcontainer," + self
.base_dn
)
159 delete_force(self
.ldb
, "cn=entry2,cn=ldaptestcontainer," + self
.base_dn
)
160 delete_force(self
.ldb
, "cn=ldaptestcontainer," + self
.base_dn
)
162 # Performs some protected object delete testing
164 res
= ldb
.search(base
="", expression
="", scope
=SCOPE_BASE
,
165 attrs
=["dsServiceName", "dNSHostName"])
166 self
.assertEquals(len(res
), 1)
168 # Delete failing since DC's nTDSDSA object is protected
170 ldb
.delete(res
[0]["dsServiceName"][0])
172 except LdbError
, (num
, _
):
173 self
.assertEquals(num
, ERR_UNWILLING_TO_PERFORM
)
175 res
= ldb
.search(self
.base_dn
, attrs
=["rIDSetReferences"],
176 expression
="(&(objectClass=computer)(dNSHostName=" + res
[0]["dNSHostName"][0] + "))")
177 self
.assertEquals(len(res
), 1)
179 # Deletes failing since DC's rIDSet object is protected
181 ldb
.delete(res
[0]["rIDSetReferences"][0])
183 except LdbError
, (num
, _
):
184 self
.assertEquals(num
, ERR_UNWILLING_TO_PERFORM
)
186 ldb
.delete(res
[0]["rIDSetReferences"][0], ["tree_delete:1"])
188 except LdbError
, (num
, _
):
189 self
.assertEquals(num
, ERR_UNWILLING_TO_PERFORM
)
191 # Deletes failing since three main crossRef objects are protected
194 ldb
.delete("cn=Enterprise Schema,cn=Partitions," + self
.configuration_dn
)
196 except LdbError
, (num
, _
):
197 self
.assertEquals(num
, ERR_UNWILLING_TO_PERFORM
)
199 ldb
.delete("cn=Enterprise Schema,cn=Partitions," + self
.configuration_dn
, ["tree_delete:1"])
201 except LdbError
, (num
, _
):
202 self
.assertEquals(num
, ERR_UNWILLING_TO_PERFORM
)
205 ldb
.delete("cn=Enterprise Configuration,cn=Partitions," + self
.configuration_dn
)
207 except LdbError
, (num
, _
):
208 self
.assertEquals(num
, ERR_NOT_ALLOWED_ON_NON_LEAF
)
210 ldb
.delete("cn=Enterprise Configuration,cn=Partitions," + self
.configuration_dn
, ["tree_delete:1"])
212 except LdbError
, (num
, _
):
213 self
.assertEquals(num
, ERR_NOT_ALLOWED_ON_NON_LEAF
)
215 res
= ldb
.search("cn=Partitions," + self
.configuration_dn
, attrs
=[],
216 expression
="(nCName=%s)" % self
.base_dn
)
217 self
.assertEquals(len(res
), 1)
220 ldb
.delete(res
[0].dn
)
222 except LdbError
, (num
, _
):
223 self
.assertEquals(num
, ERR_NOT_ALLOWED_ON_NON_LEAF
)
225 ldb
.delete(res
[0].dn
, ["tree_delete:1"])
227 except LdbError
, (num
, _
):
228 self
.assertEquals(num
, ERR_NOT_ALLOWED_ON_NON_LEAF
)
230 # Delete failing since "SYSTEM_FLAG_DISALLOW_DELETE"
232 ldb
.delete("CN=Users," + self
.base_dn
)
234 except LdbError
, (num
, _
):
235 self
.assertEquals(num
, ERR_UNWILLING_TO_PERFORM
)
237 # Tree-delete failing since "isCriticalSystemObject"
239 ldb
.delete("CN=Computers," + self
.base_dn
, ["tree_delete:1"])
241 except LdbError
, (num
, _
):
242 self
.assertEquals(num
, ERR_UNWILLING_TO_PERFORM
)
245 """Basic delete tests"""
249 usr1
="cn=testuser,cn=users," + self
.base_dn
250 usr2
="cn=testuser2,cn=users," + self
.base_dn
251 grp1
="cn=testdelgroup1,cn=users," + self
.base_dn
252 sit1
="cn=testsite1,cn=sites," + self
.configuration_dn
253 ss1
="cn=NTDS Site Settings,cn=testsite1,cn=sites," + self
.configuration_dn
254 srv1
="cn=Servers,cn=testsite1,cn=sites," + self
.configuration_dn
255 srv2
="cn=TESTSRV,cn=Servers,cn=testsite1,cn=sites," + self
.configuration_dn
257 delete_force(self
.ldb
, usr1
)
258 delete_force(self
.ldb
, usr2
)
259 delete_force(self
.ldb
, grp1
)
260 delete_force(self
.ldb
, ss1
)
261 delete_force(self
.ldb
, srv2
)
262 delete_force(self
.ldb
, srv1
)
263 delete_force(self
.ldb
, sit1
)
267 "objectclass": "user",
268 "description": "test user description",
269 "samaccountname": "testuser"})
273 "objectclass": "user",
274 "description": "test user 2 description",
275 "samaccountname": "testuser2"})
279 "objectclass": "group",
280 "description": "test group",
281 "samaccountname": "testdelgroup1",
282 "member": [ usr1
, usr2
],
283 "isDeleted": "FALSE" })
287 "objectclass": "site" })
291 "objectclass": ["applicationSiteSettings", "nTDSSiteSettings"] })
295 "objectclass": "serversContainer" })
299 "objectClass": "server" })
301 objLive1
= self
.search_dn(usr1
)
302 guid1
=objLive1
["objectGUID"][0]
304 objLive2
= self
.search_dn(usr2
)
305 guid2
=objLive2
["objectGUID"][0]
307 objLive3
= self
.search_dn(grp1
)
308 guid3
=objLive3
["objectGUID"][0]
310 objLive4
= self
.search_dn(sit1
)
311 guid4
=objLive4
["objectGUID"][0]
313 objLive5
= self
.search_dn(ss1
)
314 guid5
=objLive5
["objectGUID"][0]
316 objLive6
= self
.search_dn(srv1
)
317 guid6
=objLive6
["objectGUID"][0]
319 objLive7
= self
.search_dn(srv2
)
320 guid7
=objLive7
["objectGUID"][0]
325 ldb
.delete(srv1
, ["tree_delete:1"])
326 ldb
.delete(sit1
, ["tree_delete:1"])
328 objDeleted1
= self
.search_guid(guid1
)
329 objDeleted2
= self
.search_guid(guid2
)
330 objDeleted3
= self
.search_guid(guid3
)
331 objDeleted4
= self
.search_guid(guid4
)
332 objDeleted5
= self
.search_guid(guid5
)
333 objDeleted6
= self
.search_guid(guid6
)
334 objDeleted7
= self
.search_guid(guid7
)
336 self
.del_attr_values(objDeleted1
)
337 self
.del_attr_values(objDeleted2
)
338 self
.del_attr_values(objDeleted3
)
339 self
.del_attr_values(objDeleted4
)
340 self
.del_attr_values(objDeleted5
)
341 self
.del_attr_values(objDeleted6
)
342 self
.del_attr_values(objDeleted7
)
344 self
.preserved_attributes_list(objLive1
, objDeleted1
)
345 self
.preserved_attributes_list(objLive2
, objDeleted2
)
346 self
.preserved_attributes_list(objLive3
, objDeleted3
)
347 self
.preserved_attributes_list(objLive4
, objDeleted4
)
348 self
.preserved_attributes_list(objLive5
, objDeleted5
)
349 self
.preserved_attributes_list(objLive6
, objDeleted6
)
350 self
.preserved_attributes_list(objLive7
, objDeleted7
)
352 self
.check_rdn(objLive1
, objDeleted1
, "cn")
353 self
.check_rdn(objLive2
, objDeleted2
, "cn")
354 self
.check_rdn(objLive3
, objDeleted3
, "cn")
355 self
.check_rdn(objLive4
, objDeleted4
, "cn")
356 self
.check_rdn(objLive5
, objDeleted5
, "cn")
357 self
.check_rdn(objLive6
, objDeleted6
, "cn")
358 self
.check_rdn(objLive7
, objDeleted7
, "cn")
360 self
.delete_deleted(ldb
, usr1
)
361 self
.delete_deleted(ldb
, usr2
)
362 self
.delete_deleted(ldb
, grp1
)
363 self
.delete_deleted(ldb
, sit1
)
364 self
.delete_deleted(ldb
, ss1
)
365 self
.delete_deleted(ldb
, srv1
)
366 self
.delete_deleted(ldb
, srv2
)
368 self
.assertTrue("CN=Deleted Objects" in str(objDeleted1
.dn
))
369 self
.assertTrue("CN=Deleted Objects" in str(objDeleted2
.dn
))
370 self
.assertTrue("CN=Deleted Objects" in str(objDeleted3
.dn
))
371 self
.assertFalse("CN=Deleted Objects" in str(objDeleted4
.dn
))
372 self
.assertTrue("CN=Deleted Objects" in str(objDeleted5
.dn
))
373 self
.assertFalse("CN=Deleted Objects" in str(objDeleted6
.dn
))
374 self
.assertFalse("CN=Deleted Objects" in str(objDeleted7
.dn
))
376 if not "://" in host
:
377 if os
.path
.isfile(host
):
378 host
= "tdb://%s" % host
380 host
= "ldap://%s" % host
382 ldb
= SamDB(host
, credentials
=creds
, session_info
=system_session(), lp
=lp
)
384 runner
= SubunitTestRunner()
386 if not runner
.run(unittest
.makeSuite(BasicDeleteTests
)).wasSuccessful():