s4:dsdb python tests - use "ldb.domain_dn"
[sfrench/samba-autobuild/.git] / source4 / dsdb / tests / python / deletetest.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 import optparse
5 import sys
6 import os
7
8 sys.path.append("bin/python")
9 import samba
10 samba.ensure_external_module("testtools", "testtools")
11 samba.ensure_external_module("subunit", "subunit/python")
12
13 import samba.getopt as options
14
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
21 from subunit.run import SubunitTestRunner
22 import unittest
23
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()
32
33 if len(args) < 1:
34     parser.print_usage()
35     sys.exit(1)
36
37 host = args[0]
38
39 lp = sambaopts.get_loadparm()
40 creds = credopts.get_credentials(lp)
41
42 class BasicDeleteTests(unittest.TestCase):
43
44     def delete_force(self, ldb, dn):
45         try:
46             ldb.delete(dn)
47         except LdbError, (num, _):
48             self.assertEquals(num, ERR_NO_SUCH_OBJECT)
49
50     def GUID_string(self, guid):
51         return self.ldb.schema_format_value("objectGUID", guid)
52
53     def find_configurationdn(self, ldb):
54         res = ldb.search(base="", expression="", scope=SCOPE_BASE,
55                          attrs=["configurationNamingContext"])
56         self.assertEquals(len(res), 1)
57         return res[0]["configurationNamingContext"][0]
58
59     def setUp(self):
60         self.ldb = ldb
61         self.base_dn = ldb.domain_dn()
62         self.configuration_dn = self.find_configurationdn(ldb)
63
64     def search_guid(self, guid):
65         print "SEARCH by GUID %s" % self.GUID_string(guid)
66
67         res = ldb.search(base="<GUID=%s>" % self.GUID_string(guid),
68                          scope=SCOPE_BASE, controls=["show_deleted:1"])
69         self.assertEquals(len(res), 1)
70         return res[0]
71
72     def search_dn(self,dn):
73         print "SEARCH by DN %s" % dn
74
75         res = ldb.search(expression="(objectClass=*)",
76                          base=dn,
77                          scope=SCOPE_BASE,
78                          controls=["show_deleted:1"])
79         self.assertEquals(len(res), 1)
80         return res[0]
81
82     def del_attr_values(self, delObj):
83         print "Checking attributes for %s" % delObj["dn"]
84
85         self.assertEquals(delObj["isDeleted"][0],"TRUE")
86         self.assertTrue(not("objectCategory" in delObj))
87         self.assertTrue(not("sAMAccountType" in delObj))
88
89     def preserved_attributes_list(self, liveObj, delObj):
90         print "Checking for preserved attributes list"
91
92         preserved_list = ["nTSecurityDescriptor", "attributeID", "attributeSyntax", "dNReferenceUpdate", "dNSHostName",
93         "flatName", "governsID", "groupType", "instanceType", "lDAPDisplayName", "legacyExchangeDN",
94         "isDeleted", "isRecycled", "lastKnownParent", "msDS-LastKnownRDN", "mS-DS-CreatorSID",
95         "mSMQOwnerID", "nCName", "objectClass", "distinguishedName", "objectGUID", "objectSid",
96         "oMSyntax", "proxiedObjectName", "name", "replPropertyMetaData", "sAMAccountName",
97         "securityIdentifier", "sIDHistory", "subClassOf", "systemFlags", "trustPartner", "trustDirection",
98         "trustType", "trustAttributes", "userAccountControl", "uSNChanged", "uSNCreated", "whenCreated"]
99
100         for a in liveObj:
101             if a in preserved_list:
102                 self.assertTrue(a in delObj)
103
104     def check_rdn(self, liveObj, delObj, rdnName):
105         print "Checking for correct rDN"
106         rdn=liveObj[rdnName][0]
107         rdn2=delObj[rdnName][0]
108         name2=delObj[rdnName][0]
109         guid=liveObj["objectGUID"][0]
110         self.assertEquals(rdn2, rdn + "\nDEL:" + self.GUID_string(guid))
111         self.assertEquals(name2, rdn + "\nDEL:" + self.GUID_string(guid))
112
113     def delete_deleted(self, ldb, dn):
114         print "Testing the deletion of the already deleted dn %s" % dn
115
116         try:
117             ldb.delete(dn)
118             self.fail()
119         except LdbError, (num, _):
120             self.assertEquals(num, ERR_NO_SUCH_OBJECT)
121
122     def test_delete_protection(self):
123         """Delete protection tests"""
124
125         print self.base_dn
126
127         self.delete_force(self.ldb, "cn=entry1,cn=ldaptestcontainer," + self.base_dn)
128         self.delete_force(self.ldb, "cn=entry2,cn=ldaptestcontainer," + self.base_dn)
129         self.delete_force(self.ldb, "cn=ldaptestcontainer," + self.base_dn)
130
131         ldb.add({
132             "dn": "cn=ldaptestcontainer," + self.base_dn,
133             "objectclass": "container"})
134         ldb.add({
135             "dn": "cn=entry1,cn=ldaptestcontainer," + self.base_dn,
136             "objectclass": "container"})
137         ldb.add({
138             "dn": "cn=entry2,cn=ldaptestcontainer," + self.base_dn,
139             "objectclass": "container"})
140
141         try:
142             ldb.delete("cn=ldaptestcontainer," + self.base_dn)
143             self.fail()
144         except LdbError, (num, _):
145             self.assertEquals(num, ERR_NOT_ALLOWED_ON_NON_LEAF)
146
147         ldb.delete("cn=ldaptestcontainer," + self.base_dn, ["tree_delete:1"])
148
149         try:
150             res = ldb.search("cn=ldaptestcontainer," + self.base_dn,
151                              scope=SCOPE_BASE, attrs=[])
152             self.fail()
153         except LdbError, (num, _):
154             self.assertEquals(num, ERR_NO_SUCH_OBJECT)
155         try:
156             res = ldb.search("cn=entry1,cn=ldaptestcontainer," + self.base_dn,
157                              scope=SCOPE_BASE, attrs=[])
158             self.fail()
159         except LdbError, (num, _):
160             self.assertEquals(num, ERR_NO_SUCH_OBJECT)
161         try:
162             res = ldb.search("cn=entry2,cn=ldaptestcontainer," + self.base_dn,
163                              scope=SCOPE_BASE, attrs=[])
164             self.fail()
165         except LdbError, (num, _):
166             self.assertEquals(num, ERR_NO_SUCH_OBJECT)
167
168         self.delete_force(self.ldb, "cn=entry1,cn=ldaptestcontainer," + self.base_dn)
169         self.delete_force(self.ldb, "cn=entry2,cn=ldaptestcontainer," + self.base_dn)
170         self.delete_force(self.ldb, "cn=ldaptestcontainer," + self.base_dn)
171
172         # Performs some protected object delete testing
173
174         res = ldb.search(base="", expression="", scope=SCOPE_BASE,
175                          attrs=["dsServiceName", "dNSHostName"])
176         self.assertEquals(len(res), 1)
177
178         # Delete failing since DC's nTDSDSA object is protected
179         try:
180             ldb.delete(res[0]["dsServiceName"][0])
181             self.fail()
182         except LdbError, (num, _):
183             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
184
185         res = ldb.search(self.base_dn, attrs=["rIDSetReferences"],
186                          expression="(&(objectClass=computer)(dNSHostName=" + res[0]["dNSHostName"][0] + "))")
187         self.assertEquals(len(res), 1)
188
189         # Deletes failing since DC's rIDSet object is protected
190         try:
191             ldb.delete(res[0]["rIDSetReferences"][0])
192             self.fail()
193         except LdbError, (num, _):
194             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
195         try:
196             ldb.delete(res[0]["rIDSetReferences"][0], ["tree_delete:1"])
197             self.fail()
198         except LdbError, (num, _):
199             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
200
201         # Deletes failing since three main crossRef objects are protected
202
203         try:
204             ldb.delete("cn=Enterprise Schema,cn=Partitions," + self.configuration_dn)
205             self.fail()
206         except LdbError, (num, _):
207             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
208         try:
209             ldb.delete("cn=Enterprise Schema,cn=Partitions," + self.configuration_dn, ["tree_delete:1"])
210             self.fail()
211         except LdbError, (num, _):
212             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
213
214         try:
215             ldb.delete("cn=Enterprise Configuration,cn=Partitions," + self.configuration_dn)
216             self.fail()
217         except LdbError, (num, _):
218             self.assertEquals(num, ERR_NOT_ALLOWED_ON_NON_LEAF)
219         try:
220             ldb.delete("cn=Enterprise Configuration,cn=Partitions," + self.configuration_dn, ["tree_delete:1"])
221             self.fail()
222         except LdbError, (num, _):
223             self.assertEquals(num, ERR_NOT_ALLOWED_ON_NON_LEAF)
224
225         res = ldb.search("cn=Partitions," + self.configuration_dn, attrs=[],
226                          expression="(nCName=%s)" % self.base_dn)
227         self.assertEquals(len(res), 1)
228
229         try:
230             ldb.delete(res[0].dn)
231             self.fail()
232         except LdbError, (num, _):
233             self.assertEquals(num, ERR_NOT_ALLOWED_ON_NON_LEAF)
234         try:
235             ldb.delete(res[0].dn, ["tree_delete:1"])
236             self.fail()
237         except LdbError, (num, _):
238             self.assertEquals(num, ERR_NOT_ALLOWED_ON_NON_LEAF)
239
240         # Delete failing since "SYSTEM_FLAG_DISALLOW_DELETE"
241         try:
242             ldb.delete("CN=Users," + self.base_dn)
243             self.fail()
244         except LdbError, (num, _):
245             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
246
247         # Tree-delete failing since "isCriticalSystemObject"
248         try:
249             ldb.delete("CN=Computers," + self.base_dn, ["tree_delete:1"])
250             self.fail()
251         except LdbError, (num, _):
252             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
253
254     def test_all(self):
255         """Basic delete tests"""
256
257         print self.base_dn
258
259         usr1="cn=testuser,cn=users," + self.base_dn
260         usr2="cn=testuser2,cn=users," + self.base_dn
261         grp1="cn=testdelgroup1,cn=users," + self.base_dn
262         sit1="cn=testsite1,cn=sites," + self.configuration_dn
263         ss1="cn=NTDS Site Settings,cn=testsite1,cn=sites," + self.configuration_dn
264         srv1="cn=Servers,cn=testsite1,cn=sites," + self.configuration_dn
265         srv2="cn=TESTSRV,cn=Servers,cn=testsite1,cn=sites," + self.configuration_dn
266
267         self.delete_force(self.ldb, usr1)
268         self.delete_force(self.ldb, usr2)
269         self.delete_force(self.ldb, grp1)
270         self.delete_force(self.ldb, ss1)
271         self.delete_force(self.ldb, srv2)
272         self.delete_force(self.ldb, srv1)
273         self.delete_force(self.ldb, sit1)
274
275         ldb.add({
276             "dn": usr1,
277             "objectclass": "user",
278             "description": "test user description",
279             "samaccountname": "testuser"})
280
281         ldb.add({
282             "dn": usr2,
283             "objectclass": "user",
284             "description": "test user 2 description",
285             "samaccountname": "testuser2"})
286
287         ldb.add({
288             "dn": grp1,
289             "objectclass": "group",
290             "description": "test group",
291             "samaccountname": "testdelgroup1",
292             "member": [ usr1, usr2 ],
293             "isDeleted": "FALSE" })
294
295         ldb.add({
296             "dn": sit1,
297             "objectclass": "site" })
298
299         ldb.add({
300             "dn": ss1,
301             "objectclass": ["applicationSiteSettings", "nTDSSiteSettings"] })
302
303         ldb.add({
304             "dn": srv1,
305             "objectclass": "serversContainer" })
306
307         ldb.add({
308             "dn": srv2,
309             "objectClass": "server" })
310
311         objLive1 = self.search_dn(usr1)
312         guid1=objLive1["objectGUID"][0]
313
314         objLive2 = self.search_dn(usr2)
315         guid2=objLive2["objectGUID"][0]
316
317         objLive3 = self.search_dn(grp1)
318         guid3=objLive3["objectGUID"][0]
319
320         objLive4 = self.search_dn(sit1)
321         guid4=objLive4["objectGUID"][0]
322
323         objLive5 = self.search_dn(ss1)
324         guid5=objLive5["objectGUID"][0]
325
326         objLive6 = self.search_dn(srv1)
327         guid6=objLive6["objectGUID"][0]
328
329         objLive7 = self.search_dn(srv2)
330         guid7=objLive7["objectGUID"][0]
331
332         ldb.delete(usr1)
333         ldb.delete(usr2)
334         ldb.delete(grp1)
335         ldb.delete(srv1, ["tree_delete:1"])
336         ldb.delete(sit1, ["tree_delete:1"])
337
338         objDeleted1 = self.search_guid(guid1)
339         objDeleted2 = self.search_guid(guid2)
340         objDeleted3 = self.search_guid(guid3)
341         objDeleted4 = self.search_guid(guid4)
342         objDeleted5 = self.search_guid(guid5)
343         objDeleted6 = self.search_guid(guid6)
344         objDeleted7 = self.search_guid(guid7)
345
346         self.del_attr_values(objDeleted1)
347         self.del_attr_values(objDeleted2)
348         self.del_attr_values(objDeleted3)
349         self.del_attr_values(objDeleted4)
350         self.del_attr_values(objDeleted5)
351         self.del_attr_values(objDeleted6)
352         self.del_attr_values(objDeleted7)
353
354         self.preserved_attributes_list(objLive1, objDeleted1)
355         self.preserved_attributes_list(objLive2, objDeleted2)
356         self.preserved_attributes_list(objLive3, objDeleted3)
357         self.preserved_attributes_list(objLive4, objDeleted4)
358         self.preserved_attributes_list(objLive5, objDeleted5)
359         self.preserved_attributes_list(objLive6, objDeleted6)
360         self.preserved_attributes_list(objLive7, objDeleted7)
361
362         self.check_rdn(objLive1, objDeleted1, "cn")
363         self.check_rdn(objLive2, objDeleted2, "cn")
364         self.check_rdn(objLive3, objDeleted3, "cn")
365         self.check_rdn(objLive4, objDeleted4, "cn")
366         self.check_rdn(objLive5, objDeleted5, "cn")
367         self.check_rdn(objLive6, objDeleted6, "cn")
368         self.check_rdn(objLive7, objDeleted7, "cn")
369
370         self.delete_deleted(ldb, usr1)
371         self.delete_deleted(ldb, usr2)
372         self.delete_deleted(ldb, grp1)
373         self.delete_deleted(ldb, sit1)
374         self.delete_deleted(ldb, ss1)
375         self.delete_deleted(ldb, srv1)
376         self.delete_deleted(ldb, srv2)
377
378         self.assertTrue("CN=Deleted Objects" in str(objDeleted1.dn))
379         self.assertTrue("CN=Deleted Objects" in str(objDeleted2.dn))
380         self.assertTrue("CN=Deleted Objects" in str(objDeleted3.dn))
381         self.assertFalse("CN=Deleted Objects" in str(objDeleted4.dn))
382         self.assertTrue("CN=Deleted Objects" in str(objDeleted5.dn))
383         self.assertFalse("CN=Deleted Objects" in str(objDeleted6.dn))
384         self.assertFalse("CN=Deleted Objects" in str(objDeleted7.dn))
385
386 if not "://" in host:
387     if os.path.isfile(host):
388         host = "tdb://%s" % host
389     else:
390         host = "ldap://%s" % host
391
392 ldb = SamDB(host, credentials=creds, session_info=system_session(), lp=lp)
393
394 runner = SubunitTestRunner()
395 rc = 0
396 if not runner.run(unittest.makeSuite(BasicDeleteTests)).wasSuccessful():
397     rc = 1
398
399 sys.exit(rc)