PEP8: fix E203: whitespace before ':'
[nivanova/samba-autobuild/.git] / source4 / dsdb / tests / python / ldap.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 # This is a port of the original in testprogs/ejs/ldap.js
4
5 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2008-2011
6 #
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
20 from __future__ import print_function
21 import optparse
22 import sys
23 import time
24 import base64
25 import os
26
27 sys.path.insert(0, "bin/python")
28 import samba
29 from samba.tests.subunitrun import SubunitOptions, TestProgram
30 import samba.getopt as options
31
32 from samba.auth import system_session
33 from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError
34 from ldb import ERR_NO_SUCH_OBJECT, ERR_ATTRIBUTE_OR_VALUE_EXISTS
35 from ldb import ERR_ENTRY_ALREADY_EXISTS, ERR_UNWILLING_TO_PERFORM
36 from ldb import ERR_NOT_ALLOWED_ON_NON_LEAF, ERR_OTHER, ERR_INVALID_DN_SYNTAX
37 from ldb import ERR_NO_SUCH_ATTRIBUTE, ERR_INVALID_ATTRIBUTE_SYNTAX
38 from ldb import ERR_OBJECT_CLASS_VIOLATION, ERR_NOT_ALLOWED_ON_RDN
39 from ldb import ERR_NAMING_VIOLATION, ERR_CONSTRAINT_VIOLATION
40 from ldb import Message, MessageElement, Dn
41 from ldb import FLAG_MOD_ADD, FLAG_MOD_REPLACE, FLAG_MOD_DELETE
42 from ldb import timestring
43 from samba import Ldb
44 from samba.samdb import SamDB
45 from samba.dsdb import (UF_NORMAL_ACCOUNT,
46                         UF_WORKSTATION_TRUST_ACCOUNT,
47                         UF_PASSWD_NOTREQD, UF_ACCOUNTDISABLE, ATYPE_NORMAL_ACCOUNT,
48                         ATYPE_WORKSTATION_TRUST, SYSTEM_FLAG_DOMAIN_DISALLOW_MOVE,
49                         SYSTEM_FLAG_CONFIG_ALLOW_RENAME, SYSTEM_FLAG_CONFIG_ALLOW_MOVE,
50                         SYSTEM_FLAG_CONFIG_ALLOW_LIMITED_MOVE)
51
52 from samba.ndr import ndr_pack, ndr_unpack
53 from samba.dcerpc import security, lsa
54 from samba.tests import delete_force
55
56 parser = optparse.OptionParser("ldap.py [options] <host>")
57 sambaopts = options.SambaOptions(parser)
58 parser.add_option_group(sambaopts)
59 parser.add_option_group(options.VersionOptions(parser))
60 # use command line creds if available
61 credopts = options.CredentialsOptions(parser)
62 parser.add_option_group(credopts)
63 subunitopts = SubunitOptions(parser)
64 parser.add_option_group(subunitopts)
65 opts, args = parser.parse_args()
66
67 if len(args) < 1:
68     parser.print_usage()
69     sys.exit(1)
70
71 host = args[0]
72
73 lp = sambaopts.get_loadparm()
74 creds = credopts.get_credentials(lp)
75
76 class BasicTests(samba.tests.TestCase):
77
78     def setUp(self):
79         super(BasicTests, self).setUp()
80         self.ldb = ldb
81         self.gc_ldb = gc_ldb
82         self.base_dn = ldb.domain_dn()
83         self.configuration_dn = ldb.get_config_basedn().get_linearized()
84         self.schema_dn = ldb.get_schema_basedn().get_linearized()
85         self.domain_sid = security.dom_sid(ldb.get_domain_sid())
86
87         delete_force(self.ldb, "cn=posixuser,cn=users," + self.base_dn)
88         delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
89         delete_force(self.ldb, "cn=ldaptestuser2,cn=users," + self.base_dn)
90         delete_force(self.ldb, "cn=ldaptestuser3,cn=users," + self.base_dn)
91         delete_force(self.ldb, "cn=ldaptestuser4,cn=ldaptestcontainer," + self.base_dn)
92         delete_force(self.ldb, "cn=ldaptestuser4,cn=ldaptestcontainer2," + self.base_dn)
93         delete_force(self.ldb, "cn=ldaptestuser5,cn=users," + self.base_dn)
94         delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
95         delete_force(self.ldb, "cn=ldaptestgroup2,cn=users," + self.base_dn)
96         delete_force(self.ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn)
97         delete_force(self.ldb, "cn=ldaptest2computer,cn=computers," + self.base_dn)
98         delete_force(self.ldb, "cn=ldaptestcomputer3,cn=computers," + self.base_dn)
99         delete_force(self.ldb, "cn=ldaptestutf8user èùéìòà,cn=users," + self.base_dn)
100         delete_force(self.ldb, "cn=ldaptestutf8user2  èùéìòà,cn=users," + self.base_dn)
101         delete_force(self.ldb, "cn=ldaptestcontainer," + self.base_dn)
102         delete_force(self.ldb, "cn=ldaptestcontainer2," + self.base_dn)
103         delete_force(self.ldb, "cn=parentguidtest,cn=users," + self.base_dn)
104         delete_force(self.ldb, "cn=parentguidtest,cn=testotherusers," + self.base_dn)
105         delete_force(self.ldb, "cn=testotherusers," + self.base_dn)
106         delete_force(self.ldb, "cn=ldaptestobject," + self.base_dn)
107         delete_force(self.ldb, "description=xyz,cn=users," + self.base_dn)
108         delete_force(self.ldb, "ou=testou,cn=users," + self.base_dn)
109         delete_force(self.ldb, "cn=Test Secret,cn=system," + self.base_dn)
110         delete_force(self.ldb, "cn=testtimevaluesuser1,cn=users," + self.base_dn)
111
112     def test_objectclasses(self):
113         """Test objectClass behaviour"""
114         # Invalid objectclass specified
115         try:
116             self.ldb.add({
117                 "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
118                 "objectClass": []})
119             self.fail()
120         except LdbError as e1:
121             (num, _) = e1.args
122             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
123
124         # Invalid objectclass specified
125         try:
126             self.ldb.add({
127                 "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
128                 "objectClass": "X"})
129             self.fail()
130         except LdbError as e2:
131             (num, _) = e2.args
132             self.assertEquals(num, ERR_NO_SUCH_ATTRIBUTE)
133
134         # Invalid objectCategory specified
135         try:
136             self.ldb.add({
137                 "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
138                 "objectClass": "person",
139                 "objectCategory": self.base_dn})
140             self.fail()
141         except LdbError as e3:
142             (num, _) = e3.args
143             self.assertEquals(num, ERR_OBJECT_CLASS_VIOLATION)
144
145         # Multi-valued "systemFlags"
146         try:
147             self.ldb.add({
148                 "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
149                 "objectClass": "person",
150                 "systemFlags": ["0", str(SYSTEM_FLAG_DOMAIN_DISALLOW_MOVE)]})
151             self.fail()
152         except LdbError as e4:
153             (num, _) = e4.args
154             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
155
156         # We cannot instanciate from an abstract object class ("connectionPoint"
157         # or "leaf"). In the first case we use "connectionPoint" (subclass of
158         # "leaf") to prevent a naming violation - this returns us a
159         # "ERR_UNWILLING_TO_PERFORM" since it is not structural. In the second
160         # case however we get "ERR_OBJECT_CLASS_VIOLATION" since an abstract
161         # class is also not allowed to be auxiliary.
162         try:
163             self.ldb.add({
164                 "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
165                 "objectClass": "connectionPoint"})
166             self.fail()
167         except LdbError as e5:
168             (num, _) = e5.args
169             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
170         try:
171             self.ldb.add({
172                 "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
173                 "objectClass": ["person", "leaf"]})
174             self.fail()
175         except LdbError as e6:
176             (num, _) = e6.args
177             self.assertEquals(num, ERR_OBJECT_CLASS_VIOLATION)
178
179         # Objects instanciated using "satisfied" abstract classes (concrete
180         # subclasses) are allowed
181         self.ldb.add({
182             "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
183             "objectClass": ["top", "leaf", "connectionPoint", "serviceConnectionPoint"]})
184
185         delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
186
187         # Two disjoint top-most structural object classes aren't allowed
188         try:
189             self.ldb.add({
190                 "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
191                 "objectClass": ["person", "container"]})
192             self.fail()
193         except LdbError as e7:
194             (num, _) = e7.args
195             self.assertEquals(num, ERR_OBJECT_CLASS_VIOLATION)
196
197         # Test allowed system flags
198         self.ldb.add({
199             "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
200             "objectClass": "person",
201             "systemFlags": str(~(SYSTEM_FLAG_CONFIG_ALLOW_RENAME | SYSTEM_FLAG_CONFIG_ALLOW_MOVE | SYSTEM_FLAG_CONFIG_ALLOW_LIMITED_MOVE))})
202
203         res = ldb.search("cn=ldaptestuser,cn=users," + self.base_dn,
204                          scope=SCOPE_BASE, attrs=["systemFlags"])
205         self.assertTrue(len(res) == 1)
206         self.assertEquals(res[0]["systemFlags"][0], "0")
207
208         delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
209
210         self.ldb.add({
211             "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
212             "objectClass": "person"})
213
214         # We can remove derivation classes of the structural objectclass
215         # but they're going to be readded afterwards
216         m = Message()
217         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
218         m["objectClass"] = MessageElement("top", FLAG_MOD_DELETE,
219                                           "objectClass")
220         ldb.modify(m)
221
222         res = ldb.search("cn=ldaptestuser,cn=users," + self.base_dn,
223                          scope=SCOPE_BASE, attrs=["objectClass"])
224         self.assertTrue(len(res) == 1)
225         self.assertTrue("top" in res[0]["objectClass"])
226
227         # The top-most structural class cannot be deleted since there are
228         # attributes of it in use
229         m = Message()
230         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
231         m["objectClass"] = MessageElement("person", FLAG_MOD_DELETE,
232                                           "objectClass")
233         try:
234             ldb.modify(m)
235             self.fail()
236         except LdbError as e8:
237             (num, _) = e8.args
238             self.assertEquals(num, ERR_OBJECT_CLASS_VIOLATION)
239
240         # We cannot delete classes which weren't specified
241         m = Message()
242         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
243         m["objectClass"] = MessageElement("computer", FLAG_MOD_DELETE,
244                                           "objectClass")
245         try:
246             ldb.modify(m)
247             self.fail()
248         except LdbError as e9:
249             (num, _) = e9.args
250             self.assertEquals(num, ERR_NO_SUCH_ATTRIBUTE)
251
252         # An invalid class cannot be added
253         m = Message()
254         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
255         m["objectClass"] = MessageElement("X", FLAG_MOD_ADD,
256                                           "objectClass")
257         try:
258             ldb.modify(m)
259             self.fail()
260         except LdbError as e10:
261             (num, _) = e10.args
262             self.assertEquals(num, ERR_NO_SUCH_ATTRIBUTE)
263
264         # We cannot add a the new top-most structural class "user" here since
265         # we are missing at least one new mandatory attribute (in this case
266         # "sAMAccountName")
267         m = Message()
268         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
269         m["objectClass"] = MessageElement("user", FLAG_MOD_ADD,
270                                           "objectClass")
271         try:
272             ldb.modify(m)
273             self.fail()
274         except LdbError as e11:
275             (num, _) = e11.args
276             self.assertEquals(num, ERR_OBJECT_CLASS_VIOLATION)
277
278         # An already specified objectclass cannot be added another time
279         m = Message()
280         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
281         m["objectClass"] = MessageElement("person", FLAG_MOD_ADD,
282                                           "objectClass")
283         try:
284             ldb.modify(m)
285             self.fail()
286         except LdbError as e12:
287             (num, _) = e12.args
288             self.assertEquals(num, ERR_ATTRIBUTE_OR_VALUE_EXISTS)
289
290         # Auxiliary classes can always be added
291         m = Message()
292         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
293         m["objectClass"] = MessageElement("bootableDevice", FLAG_MOD_ADD,
294                                           "objectClass")
295         ldb.modify(m)
296
297         # This does not work since object class "leaf" is not auxiliary nor it
298         # stands in direct relation to "person" (and it is abstract too!)
299         m = Message()
300         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
301         m["objectClass"] = MessageElement("leaf", FLAG_MOD_ADD,
302                                           "objectClass")
303         try:
304             ldb.modify(m)
305             self.fail()
306         except LdbError as e13:
307             (num, _) = e13.args
308             self.assertEquals(num, ERR_OBJECT_CLASS_VIOLATION)
309
310         # Objectclass replace operations can be performed as well
311         m = Message()
312         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
313         m["objectClass"] = MessageElement(["top", "person", "bootableDevice"],
314                                           FLAG_MOD_REPLACE, "objectClass")
315         ldb.modify(m)
316
317         m = Message()
318         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
319         m["objectClass"] = MessageElement(["person", "bootableDevice"],
320                                           FLAG_MOD_REPLACE, "objectClass")
321         ldb.modify(m)
322
323         # This does not work since object class "leaf" is not auxiliary nor it
324         # stands in direct relation to "person" (and it is abstract too!)
325         m = Message()
326         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
327         m["objectClass"] = MessageElement(["top", "person", "bootableDevice",
328                                            "leaf"], FLAG_MOD_REPLACE, "objectClass")
329         try:
330             ldb.modify(m)
331             self.fail()
332         except LdbError as e14:
333             (num, _) = e14.args
334             self.assertEquals(num, ERR_OBJECT_CLASS_VIOLATION)
335
336         # More than one change operation is allowed
337         m = Message()
338         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
339         m.add(MessageElement("bootableDevice", FLAG_MOD_DELETE, "objectClass"))
340         m.add(MessageElement("bootableDevice", FLAG_MOD_ADD, "objectClass"))
341         ldb.modify(m)
342
343         # We cannot remove all object classes by an empty replace
344         m = Message()
345         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
346         m["objectClass"] = MessageElement([], FLAG_MOD_REPLACE, "objectClass")
347         try:
348             ldb.modify(m)
349             self.fail()
350         except LdbError as e15:
351             (num, _) = e15.args
352             self.assertEquals(num, ERR_OBJECT_CLASS_VIOLATION)
353
354         m = Message()
355         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
356         m["objectClass"] = MessageElement(["top", "computer"], FLAG_MOD_REPLACE,
357                                           "objectClass")
358         try:
359             ldb.modify(m)
360             self.fail()
361         except LdbError as e16:
362             (num, _) = e16.args
363             self.assertEquals(num, ERR_OBJECT_CLASS_VIOLATION)
364
365         # Classes can be removed unless attributes of them are used.
366         m = Message()
367         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
368         m["objectClass"] = MessageElement("bootableDevice", FLAG_MOD_DELETE,
369                                           "objectClass")
370         ldb.modify(m)
371
372         res = ldb.search("cn=ldaptestuser,cn=users," + self.base_dn,
373                          scope=SCOPE_BASE, attrs=["objectClass"])
374         self.assertTrue(len(res) == 1)
375         self.assertFalse("bootableDevice" in res[0]["objectClass"])
376
377         m = Message()
378         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
379         m["objectClass"] = MessageElement("bootableDevice", FLAG_MOD_ADD,
380                                           "objectClass")
381         ldb.modify(m)
382
383         # Add an attribute specific to the "bootableDevice" class
384         m = Message()
385         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
386         m["bootParameter"] = MessageElement("test", FLAG_MOD_ADD,
387                                             "bootParameter")
388         ldb.modify(m)
389
390         # Classes can be removed unless attributes of them are used. Now there
391         # exist such attributes on the entry.
392         m = Message()
393         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
394         m["objectClass"] = MessageElement("bootableDevice", FLAG_MOD_DELETE,
395                                           "objectClass")
396         try:
397             ldb.modify(m)
398             self.fail()
399         except LdbError as e17:
400             (num, _) = e17.args
401             self.assertEquals(num, ERR_OBJECT_CLASS_VIOLATION)
402
403         # Remove the previously specified attribute
404         m = Message()
405         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
406         m["bootParameter"] = MessageElement("test", FLAG_MOD_DELETE,
407                                             "bootParameter")
408         ldb.modify(m)
409
410         # Classes can be removed unless attributes of them are used.
411         m = Message()
412         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
413         m["objectClass"] = MessageElement("bootableDevice", FLAG_MOD_DELETE,
414                                           "objectClass")
415         ldb.modify(m)
416
417         delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
418
419         self.ldb.add({
420             "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
421             "objectClass": "user"})
422
423         # Add a new top-most structural class "container". This does not work
424         # since it stands in no direct relation to the current one.
425         m = Message()
426         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
427         m["objectClass"] = MessageElement("container", FLAG_MOD_ADD,
428                                           "objectClass")
429         try:
430             ldb.modify(m)
431             self.fail()
432         except LdbError as e18:
433             (num, _) = e18.args
434             self.assertEquals(num, ERR_OBJECT_CLASS_VIOLATION)
435
436         # Add a new top-most structural class "inetOrgPerson" and remove it
437         # afterwards
438         m = Message()
439         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
440         m["objectClass"] = MessageElement("inetOrgPerson", FLAG_MOD_ADD,
441                                           "objectClass")
442         ldb.modify(m)
443
444         m = Message()
445         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
446         m["objectClass"] = MessageElement("inetOrgPerson", FLAG_MOD_DELETE,
447                                           "objectClass")
448         ldb.modify(m)
449
450         # Replace top-most structural class to "inetOrgPerson" and reset it
451         # back to "user"
452         m = Message()
453         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
454         m["objectClass"] = MessageElement("inetOrgPerson", FLAG_MOD_REPLACE,
455                                           "objectClass")
456         ldb.modify(m)
457
458         m = Message()
459         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
460         m["objectClass"] = MessageElement("user", FLAG_MOD_REPLACE,
461                                           "objectClass")
462         ldb.modify(m)
463
464         # Add a new auxiliary object class "posixAccount" to "ldaptestuser"
465         m = Message()
466         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
467         m["objectClass"] = MessageElement("posixAccount", FLAG_MOD_ADD,
468                                           "objectClass")
469         ldb.modify(m)
470
471         # Be sure that "top" is the first and the (most) structural object class
472         # the last value of the "objectClass" attribute - MS-ADTS 3.1.1.1.4
473         res = ldb.search("cn=ldaptestuser,cn=users," + self.base_dn,
474                          scope=SCOPE_BASE, attrs=["objectClass"])
475         self.assertTrue(len(res) == 1)
476         self.assertEquals(res[0]["objectClass"][0], "top")
477         self.assertEquals(res[0]["objectClass"][len(res[0]["objectClass"])-1], "user")
478
479         delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
480
481     def test_system_only(self):
482         """Test systemOnly objects"""
483         try:
484             self.ldb.add({
485                 "dn": "cn=ldaptestobject," + self.base_dn,
486                 "objectclass": "configuration"})
487             self.fail()
488         except LdbError as e19:
489             (num, _) = e19.args
490             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
491
492         try:
493             self.ldb.add({
494                 "dn": "cn=Test Secret,cn=system," + self.base_dn,
495                 "objectclass": "secret"})
496             self.fail()
497         except LdbError as e20:
498             (num, _) = e20.args
499             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
500
501         delete_force(self.ldb, "cn=ldaptestobject," + self.base_dn)
502         delete_force(self.ldb, "cn=Test Secret,cn=system," + self.base_dn)
503
504         # Create secret over LSA and try to change it
505
506         lsa_conn = lsa.lsarpc("ncacn_np:%s" % args[0], lp, creds)
507         lsa_handle = lsa_conn.OpenPolicy2(system_name="\\",
508                                           attr=lsa.ObjectAttribute(),
509                                           access_mask=security.SEC_FLAG_MAXIMUM_ALLOWED)
510         secret_name = lsa.String()
511         secret_name.string = "G$Test"
512         sec_handle = lsa_conn.CreateSecret(handle=lsa_handle,
513                                            name=secret_name,
514                                            access_mask=security.SEC_FLAG_MAXIMUM_ALLOWED)
515         lsa_conn.Close(lsa_handle)
516
517         m = Message()
518         m.dn = Dn(ldb, "cn=Test Secret,cn=system," + self.base_dn)
519         m["description"] = MessageElement("desc", FLAG_MOD_REPLACE,
520                                           "description")
521         try:
522             ldb.modify(m)
523             self.fail()
524         except LdbError as e21:
525             (num, _) = e21.args
526             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
527
528         delete_force(self.ldb, "cn=Test Secret,cn=system," + self.base_dn)
529
530         try:
531             self.ldb.add({
532                 "dn": "cn=ldaptestcontainer," + self.base_dn,
533                 "objectclass": "container",
534                 "isCriticalSystemObject": "TRUE"})
535             self.fail()
536         except LdbError as e22:
537             (num, _) = e22.args
538             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
539
540         self.ldb.add({
541             "dn": "cn=ldaptestcontainer," + self.base_dn,
542             "objectclass": "container"})
543
544         m = Message()
545         m.dn = Dn(ldb, "cn=ldaptestcontainer," + self.base_dn)
546         m["isCriticalSystemObject"] = MessageElement("TRUE", FLAG_MOD_REPLACE,
547                                                      "isCriticalSystemObject")
548         try:
549             ldb.modify(m)
550             self.fail()
551         except LdbError as e23:
552             (num, _) = e23.args
553             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
554
555         delete_force(self.ldb, "cn=ldaptestcontainer," + self.base_dn)
556
557         # Proof if DC SAM object has "isCriticalSystemObject" set
558         res = self.ldb.search("", scope=SCOPE_BASE, attrs=["serverName"])
559         self.assertTrue(len(res) == 1)
560         self.assertTrue("serverName" in res[0])
561         res = self.ldb.search(res[0]["serverName"][0], scope=SCOPE_BASE,
562                               attrs=["serverReference"])
563         self.assertTrue(len(res) == 1)
564         self.assertTrue("serverReference" in res[0])
565         res = self.ldb.search(res[0]["serverReference"][0], scope=SCOPE_BASE,
566                               attrs=["isCriticalSystemObject"])
567         self.assertTrue(len(res) == 1)
568         self.assertTrue("isCriticalSystemObject" in res[0])
569         self.assertEquals(res[0]["isCriticalSystemObject"][0], "TRUE")
570
571     def test_invalid_parent(self):
572         """Test adding an object with invalid parent"""
573         try:
574             self.ldb.add({
575                 "dn": "cn=ldaptestgroup,cn=thisdoesnotexist123,"
576                 + self.base_dn,
577                 "objectclass": "group"})
578             self.fail()
579         except LdbError as e24:
580             (num, _) = e24.args
581             self.assertEquals(num, ERR_NO_SUCH_OBJECT)
582
583         delete_force(self.ldb, "cn=ldaptestgroup,cn=thisdoesnotexist123,"
584                      + self.base_dn)
585
586         try:
587             self.ldb.add({
588                 "dn": "ou=testou,cn=users," + self.base_dn,
589                 "objectclass": "organizationalUnit"})
590             self.fail()
591         except LdbError as e25:
592             (num, _) = e25.args
593             self.assertEquals(num, ERR_NAMING_VIOLATION)
594
595         delete_force(self.ldb, "ou=testou,cn=users," + self.base_dn)
596
597     def test_invalid_attribute(self):
598         """Test invalid attributes on schema/objectclasses"""
599         # attributes not in schema test
600
601         # add operation
602
603         try:
604             self.ldb.add({
605                 "dn": "cn=ldaptestgroup,cn=users," + self.base_dn,
606                 "objectclass": "group",
607                 "thisdoesnotexist": "x"})
608             self.fail()
609         except LdbError as e26:
610             (num, _) = e26.args
611             self.assertEquals(num, ERR_NO_SUCH_ATTRIBUTE)
612
613         self.ldb.add({
614             "dn": "cn=ldaptestgroup,cn=users," + self.base_dn,
615             "objectclass": "group"})
616
617         # modify operation
618
619         m = Message()
620         m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
621         m["thisdoesnotexist"] = MessageElement("x", FLAG_MOD_REPLACE,
622                                                "thisdoesnotexist")
623         try:
624             ldb.modify(m)
625             self.fail()
626         except LdbError as e27:
627             (num, _) = e27.args
628             self.assertEquals(num, ERR_NO_SUCH_ATTRIBUTE)
629
630         #
631         # When searching the unknown attribute should be ignored
632         expr = "(|(cn=ldaptestgroup)(thisdoesnotexist=x))"
633         res = ldb.search(base=self.base_dn,
634                          expression=expr,
635                          scope=SCOPE_SUBTREE)
636         self.assertTrue(len(res) == 1,
637                         "Search including unknown attribute failed")
638
639         delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
640
641         # attributes not in objectclasses and mandatory attributes missing test
642         # Use here a non-SAM entry since it doesn't have special triggers
643         # associated which have an impact on the error results.
644
645         # add operations
646
647         # mandatory attribute missing
648         try:
649             self.ldb.add({
650                 "dn": "cn=ldaptestobject," + self.base_dn,
651                 "objectclass": "ipProtocol"})
652             self.fail()
653         except LdbError as e28:
654             (num, _) = e28.args
655             self.assertEquals(num, ERR_OBJECT_CLASS_VIOLATION)
656
657         # inadequate but schema-valid attribute specified
658         try:
659             self.ldb.add({
660                 "dn": "cn=ldaptestobject," + self.base_dn,
661                 "objectclass": "ipProtocol",
662                 "ipProtocolNumber": "1",
663                 "uid": "0"})
664             self.fail()
665         except LdbError as e29:
666             (num, _) = e29.args
667             self.assertEquals(num, ERR_OBJECT_CLASS_VIOLATION)
668
669         self.ldb.add({
670             "dn": "cn=ldaptestobject," + self.base_dn,
671             "objectclass": "ipProtocol",
672             "ipProtocolNumber": "1"})
673
674         # modify operations
675
676         # inadequate but schema-valid attribute add trial
677         m = Message()
678         m.dn = Dn(ldb, "cn=ldaptestobject," + self.base_dn)
679         m["uid"] = MessageElement("0", FLAG_MOD_ADD, "uid")
680         try:
681             ldb.modify(m)
682             self.fail()
683         except LdbError as e30:
684             (num, _) = e30.args
685             self.assertEquals(num, ERR_OBJECT_CLASS_VIOLATION)
686
687         # mandatory attribute delete trial
688         m = Message()
689         m.dn = Dn(ldb, "cn=ldaptestobject," + self.base_dn)
690         m["ipProtocolNumber"] = MessageElement([], FLAG_MOD_DELETE,
691                                                "ipProtocolNumber")
692         try:
693             ldb.modify(m)
694             self.fail()
695         except LdbError as e31:
696             (num, _) = e31.args
697             self.assertEquals(num, ERR_OBJECT_CLASS_VIOLATION)
698
699         # mandatory attribute delete trial
700         m = Message()
701         m.dn = Dn(ldb, "cn=ldaptestobject," + self.base_dn)
702         m["ipProtocolNumber"] = MessageElement([], FLAG_MOD_REPLACE,
703                                                "ipProtocolNumber")
704         try:
705             ldb.modify(m)
706             self.fail()
707         except LdbError as e32:
708             (num, _) = e32.args
709             self.assertEquals(num, ERR_OBJECT_CLASS_VIOLATION)
710
711         delete_force(self.ldb, "cn=ldaptestobject," + self.base_dn)
712
713     def test_single_valued_attributes(self):
714         """Test single-valued attributes"""
715         try:
716             self.ldb.add({
717                 "dn": "cn=ldaptestgroup,cn=users," + self.base_dn,
718                 "objectclass": "group",
719                 "sAMAccountName": ["nam1", "nam2"]})
720             self.fail()
721         except LdbError as e33:
722             (num, _) = e33.args
723             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
724
725         self.ldb.add({
726             "dn": "cn=ldaptestgroup,cn=users," + self.base_dn,
727             "objectclass": "group"})
728
729         m = Message()
730         m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
731         m["sAMAccountName"] = MessageElement(["nam1","nam2"], FLAG_MOD_REPLACE,
732                                              "sAMAccountName")
733         try:
734             ldb.modify(m)
735             self.fail()
736         except LdbError as e34:
737             (num, _) = e34.args
738             self.assertEquals(num, ERR_ATTRIBUTE_OR_VALUE_EXISTS)
739
740         m = Message()
741         m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
742         m["sAMAccountName"] = MessageElement("testgroupXX", FLAG_MOD_REPLACE,
743                                              "sAMAccountName")
744         ldb.modify(m)
745
746         m = Message()
747         m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
748         m["sAMAccountName"] = MessageElement("testgroupXX2", FLAG_MOD_ADD,
749                                              "sAMAccountName")
750         try:
751             ldb.modify(m)
752             self.fail()
753         except LdbError as e35:
754             (num, _) = e35.args
755             self.assertEquals(num, ERR_ATTRIBUTE_OR_VALUE_EXISTS)
756
757         delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
758
759     def test_single_valued_linked_attributes(self):
760         """Test managedBy, a single-valued linked attribute.
761
762         (The single-valuedness of this is enforced differently, in
763         repl_meta_data.c)
764         """
765         ou = 'OU=svla,%s' % (self.base_dn)
766
767         delete_force(self.ldb, ou, controls=['tree_delete:1'])
768
769         self.ldb.add({'objectclass': 'organizationalUnit',
770                       'dn': ou})
771
772
773         managers = []
774         for x in range(3):
775             m = "cn=manager%d,%s" % (x, ou)
776             self.ldb.add({
777                 "dn": m,
778                 "objectclass": "user"})
779             managers.append(m)
780
781         try:
782             self.ldb.add({
783                 "dn": "cn=group1," + ou,
784                 "objectclass": "group",
785                 "managedBy": managers
786             })
787             self.fail("failed to fail to add multiple managedBy attributes")
788         except LdbError as e36:
789             (num, _) = e36.args
790             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
791
792         managee = "cn=group2," + ou
793         self.ldb.add({
794             "dn": managee,
795             "objectclass": "group",
796             "managedBy": [managers[0]]})
797
798         m = Message()
799         m.dn = Dn(ldb, managee)
800         m["managedBy"] = MessageElement(managers, FLAG_MOD_REPLACE,
801                                         "managedBy")
802         try:
803             ldb.modify(m)
804             self.fail()
805         except LdbError as e37:
806             (num, _) = e37.args
807             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
808
809         m = Message()
810         m.dn = Dn(ldb, managee)
811         m["managedBy"] = MessageElement(managers[1], FLAG_MOD_REPLACE,
812                                         "managedBy")
813         ldb.modify(m)
814
815         m = Message()
816         m.dn = Dn(ldb, managee)
817         m["managedBy"] = MessageElement(managers[2], FLAG_MOD_ADD,
818                                         "managedBy")
819         try:
820             ldb.modify(m)
821             self.fail()
822         except LdbError as e38:
823             (num, _) = e38.args
824             self.assertEquals(num, ERR_ATTRIBUTE_OR_VALUE_EXISTS)
825
826         self.ldb.delete(ou, ['tree_delete:1'])
827
828
829     def test_multivalued_attributes(self):
830         """Test multi-valued attributes"""
831         ou = 'OU=mvattr,%s' % (self.base_dn)
832         delete_force(self.ldb, ou, controls=['tree_delete:1'])
833         self.ldb.add({'objectclass': 'organizationalUnit',
834                       'dn': ou})
835
836         # beyond 1210, Win2012r2 gives LDAP_ADMIN_LIMIT_EXCEEDED
837         ranges = (3, 30, 300, 1210)
838
839         for n in ranges:
840             self.ldb.add({
841                 "dn": "cn=ldaptestuser%d,%s" % (n, ou),
842                 "objectclass": "user",
843                 "carLicense": ["car%d" % x for x in range(n)]})
844
845         # add some more
846         for n in ranges:
847             m = Message()
848             m.dn = Dn(ldb, "cn=ldaptestuser%d,%s" % (n, ou))
849             m["carLicense"] = MessageElement(["another"],
850                                              FLAG_MOD_ADD,
851                                              "carLicense")
852             ldb.modify(m)
853
854             m = Message()
855             m.dn = Dn(ldb, "cn=ldaptestuser%d,%s" % (n, ou))
856             m["carLicense"] = MessageElement(["foo%d" % x for x in range(4)],
857                                              FLAG_MOD_ADD,
858                                              "carLicense")
859             ldb.modify(m)
860
861             m = Message()
862             m.dn = Dn(ldb, "cn=ldaptestuser%d,%s" % (n, ou))
863             m["carLicense"] = MessageElement(["bar%d" % x for x in range(40)],
864                                              FLAG_MOD_ADD,
865                                              "carLicense")
866             ldb.modify(m)
867
868         for n in ranges:
869             m = Message()
870             dn = "cn=ldaptestuser%d,%s" % (n, ou)
871             m.dn = Dn(ldb, dn)
872             m["carLicense"] = MessageElement(["replacement"],
873                                              FLAG_MOD_REPLACE,
874                                              "carLicense")
875             ldb.modify(m)
876
877             m = Message()
878             m.dn = Dn(ldb, dn)
879             m["carLicense"] = MessageElement(["replacement%d" % x for x in range(n)],
880                                              FLAG_MOD_REPLACE,
881                                              "carLicense")
882             ldb.modify(m)
883
884             m = Message()
885             m.dn = Dn(ldb, dn)
886             m["carLicense"] = MessageElement(["again%d" % x for x in range(n)],
887                                              FLAG_MOD_REPLACE,
888                                              "carLicense")
889             ldb.modify(m)
890
891             m = Message()
892             m.dn = Dn(ldb, dn)
893             m["carLicense"] = MessageElement(["andagain%d" % x for x in range(n)],
894                                              FLAG_MOD_REPLACE,
895                                              "carLicense")
896             ldb.modify(m)
897
898         self.ldb.delete(ou, ['tree_delete:1'])
899
900
901     def test_attribute_ranges(self):
902         """Test attribute ranges"""
903         # Too short (min. 1)
904         try:
905             ldb.add({
906                 "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
907                 "objectClass": "person",
908                 "sn": ""})
909             self.fail()
910         except LdbError as e39:
911             (num, _) = e39.args
912             self.assertEquals(num, ERR_INVALID_ATTRIBUTE_SYNTAX)
913
914         # Too long (max. 64)
915 #        try:
916 #            ldb.add({
917 #               "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
918 #               "objectClass": "person",
919 #               "sn": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" })
920 #            self.fail()
921 #        except LdbError, (num, _):
922 #            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
923
924         ldb.add({
925             "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
926             "objectClass": "person"})
927
928         # Too short (min. 1)
929         m = Message()
930         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
931         m["sn"] = MessageElement("", FLAG_MOD_REPLACE, "sn")
932         try:
933             ldb.modify(m)
934             self.fail()
935         except LdbError as e40:
936             (num, _) = e40.args
937             self.assertEquals(num, ERR_INVALID_ATTRIBUTE_SYNTAX)
938
939         # Too long (max. 64)
940 #        m = Message()
941 #        m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
942 #        m["sn"] = MessageElement("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", FLAG_MOD_REPLACE, "sn")
943 #        try:
944 #            ldb.modify(m)
945 #            self.fail()
946 #        except LdbError, (num, _):
947 #            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
948
949         m = Message()
950         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
951         m["sn"] = MessageElement("x", FLAG_MOD_REPLACE, "sn")
952         ldb.modify(m)
953
954         delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
955
956     def test_empty_messages(self):
957         """Test empty messages"""
958         m = Message()
959         m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
960
961         try:
962             ldb.add(m)
963             self.fail()
964         except LdbError as e41:
965             (num, _) = e41.args
966             self.assertEquals(num, ERR_OBJECT_CLASS_VIOLATION)
967
968         try:
969             ldb.modify(m)
970             self.fail()
971         except LdbError as e42:
972             (num, _) = e42.args
973             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
974
975         delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
976
977     def test_empty_attributes(self):
978         """Test empty attributes"""
979         m = Message()
980         m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
981         m["objectClass"] = MessageElement("group", FLAG_MOD_ADD, "objectClass")
982         m["description"] = MessageElement([], FLAG_MOD_ADD, "description")
983
984         try:
985             ldb.add(m)
986             self.fail()
987         except LdbError as e43:
988             (num, _) = e43.args
989             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
990
991         self.ldb.add({
992             "dn": "cn=ldaptestgroup,cn=users," + self.base_dn,
993             "objectclass": "group"})
994
995         m = Message()
996         m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
997         m["description"] = MessageElement([], FLAG_MOD_ADD, "description")
998
999         try:
1000             ldb.modify(m)
1001             self.fail()
1002         except LdbError as e44:
1003             (num, _) = e44.args
1004             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
1005
1006         m = Message()
1007         m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
1008         m["description"] = MessageElement([], FLAG_MOD_REPLACE, "description")
1009         ldb.modify(m)
1010
1011         m = Message()
1012         m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
1013         m["description"] = MessageElement([], FLAG_MOD_DELETE, "description")
1014         try:
1015             ldb.modify(m)
1016             self.fail()
1017         except LdbError as e45:
1018             (num, _) = e45.args
1019             self.assertEquals(num, ERR_NO_SUCH_ATTRIBUTE)
1020
1021         delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
1022
1023     def test_instanceType(self):
1024         """Tests the 'instanceType' attribute"""
1025         # The instance type is single-valued
1026         try:
1027             self.ldb.add({
1028                 "dn": "cn=ldaptestgroup,cn=users," + self.base_dn,
1029                 "objectclass": "group",
1030                 "instanceType": ["0", "1"]})
1031             self.fail()
1032         except LdbError as e46:
1033             (num, _) = e46.args
1034             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
1035
1036         # The head NC flag cannot be set without the write flag
1037         try:
1038             self.ldb.add({
1039                 "dn": "cn=ldaptestgroup,cn=users," + self.base_dn,
1040                 "objectclass": "group",
1041                 "instanceType": "1"})
1042             self.fail()
1043         except LdbError as e47:
1044             (num, _) = e47.args
1045             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
1046
1047         # We cannot manipulate NCs without the head NC flag
1048         try:
1049             self.ldb.add({
1050                 "dn": "cn=ldaptestgroup,cn=users," + self.base_dn,
1051                 "objectclass": "group",
1052                 "instanceType": "32"})
1053             self.fail()
1054         except LdbError as e48:
1055             (num, _) = e48.args
1056             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
1057
1058         self.ldb.add({
1059             "dn": "cn=ldaptestgroup,cn=users," + self.base_dn,
1060             "objectclass": "group"})
1061
1062         m = Message()
1063         m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
1064         m["instanceType"] = MessageElement("0", FLAG_MOD_REPLACE,
1065                                            "instanceType")
1066         try:
1067             ldb.modify(m)
1068             self.fail()
1069         except LdbError as e49:
1070             (num, _) = e49.args
1071             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
1072
1073         m = Message()
1074         m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
1075         m["instanceType"] = MessageElement([], FLAG_MOD_REPLACE,
1076                                            "instanceType")
1077         try:
1078             ldb.modify(m)
1079             self.fail()
1080         except LdbError as e50:
1081             (num, _) = e50.args
1082             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
1083
1084         m = Message()
1085         m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
1086         m["instanceType"] = MessageElement([], FLAG_MOD_DELETE, "instanceType")
1087         try:
1088             ldb.modify(m)
1089             self.fail()
1090         except LdbError as e51:
1091             (num, _) = e51.args
1092             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
1093
1094         delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
1095
1096         #only write is allowed with NC_HEAD for originating updates
1097         try:
1098             self.ldb.add({
1099                 "dn": "cn=ldaptestuser2,cn=users," + self.base_dn,
1100                 "objectclass": "user",
1101                 "instanceType": "3"})
1102             self.fail()
1103         except LdbError as e52:
1104             (num, _) = e52.args
1105             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
1106         delete_force(self.ldb, "cn=ldaptestuser2,cn=users," + self.base_dn)
1107
1108     def test_distinguished_name(self):
1109         """Tests the 'distinguishedName' attribute"""
1110         # The "dn" shortcut isn't supported
1111         m = Message()
1112         m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
1113         m["objectClass"] = MessageElement("group", 0, "objectClass")
1114         m["dn"] = MessageElement("cn=ldaptestgroup,cn=users," + self.base_dn, 0,
1115                                  "dn")
1116         try:
1117             ldb.add(m)
1118             self.fail()
1119         except LdbError as e53:
1120             (num, _) = e53.args
1121             self.assertEquals(num, ERR_NO_SUCH_ATTRIBUTE)
1122
1123         # a wrong "distinguishedName" attribute is obviously tolerated
1124         self.ldb.add({
1125             "dn": "cn=ldaptestgroup,cn=users," + self.base_dn,
1126             "objectclass": "group",
1127             "distinguishedName": "cn=ldaptest,cn=users," + self.base_dn})
1128
1129         # proof if the DN has been set correctly
1130         res = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn,
1131                          scope=SCOPE_BASE, attrs=["distinguishedName"])
1132         self.assertTrue(len(res) == 1)
1133         self.assertTrue("distinguishedName" in res[0])
1134         self.assertTrue(Dn(ldb, res[0]["distinguishedName"][0])
1135                         == Dn(ldb, "cn=ldaptestgroup, cn=users," + self.base_dn))
1136
1137         # The "dn" shortcut isn't supported
1138         m = Message()
1139         m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
1140         m["dn"] = MessageElement(
1141             "cn=ldaptestgroup,cn=users," + self.base_dn, FLAG_MOD_REPLACE,
1142             "dn")
1143         try:
1144             ldb.modify(m)
1145             self.fail()
1146         except LdbError as e54:
1147             (num, _) = e54.args
1148             self.assertEquals(num, ERR_NO_SUCH_ATTRIBUTE)
1149
1150         m = Message()
1151         m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
1152         m["distinguishedName"] = MessageElement(
1153             "cn=ldaptestuser,cn=users," + self.base_dn, FLAG_MOD_ADD,
1154             "distinguishedName")
1155
1156         try:
1157             ldb.modify(m)
1158             self.fail()
1159         except LdbError as e55:
1160             (num, _) = e55.args
1161             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
1162
1163         m = Message()
1164         m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
1165         m["distinguishedName"] = MessageElement(
1166             "cn=ldaptestuser,cn=users," + self.base_dn, FLAG_MOD_REPLACE,
1167             "distinguishedName")
1168
1169         try:
1170             ldb.modify(m)
1171             self.fail()
1172         except LdbError as e56:
1173             (num, _) = e56.args
1174             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
1175
1176         m = Message()
1177         m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
1178         m["distinguishedName"] = MessageElement(
1179             "cn=ldaptestuser,cn=users," + self.base_dn, FLAG_MOD_DELETE,
1180             "distinguishedName")
1181
1182         try:
1183             ldb.modify(m)
1184             self.fail()
1185         except LdbError as e57:
1186             (num, _) = e57.args
1187             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
1188
1189         delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
1190
1191     def test_rdn_name(self):
1192         """Tests the RDN"""
1193         # Search
1194
1195         # empty RDN
1196         try:
1197             self.ldb.search("=,cn=users," + self.base_dn, scope=SCOPE_BASE)
1198             self.fail()
1199         except LdbError as e58:
1200             (num, _) = e58.args
1201             self.assertEquals(num, ERR_INVALID_DN_SYNTAX)
1202
1203         # empty RDN name
1204         try:
1205             self.ldb.search("cn=,cn=users," + self.base_dn, scope=SCOPE_BASE)
1206             self.fail()
1207         except LdbError as e59:
1208             (num, _) = e59.args
1209             self.assertEquals(num, ERR_INVALID_DN_SYNTAX)
1210
1211         try:
1212             self.ldb.search("=ldaptestgroup,cn=users," + self.base_dn, scope=SCOPE_BASE)
1213             self.fail()
1214         except LdbError as e60:
1215             (num, _) = e60.args
1216             self.assertEquals(num, ERR_INVALID_DN_SYNTAX)
1217
1218         # Add
1219
1220         # empty RDN
1221         try:
1222             self.ldb.add({
1223                 "dn": "=,cn=users," + self.base_dn,
1224                 "objectclass": "group"})
1225             self.fail()
1226         except LdbError as e61:
1227             (num, _) = e61.args
1228             self.assertEquals(num, ERR_INVALID_DN_SYNTAX)
1229
1230         # empty RDN name
1231         try:
1232             self.ldb.add({
1233                 "dn": "=ldaptestgroup,cn=users," + self.base_dn,
1234                 "objectclass": "group"})
1235             self.fail()
1236         except LdbError as e62:
1237             (num, _) = e62.args
1238             self.assertEquals(num, ERR_INVALID_DN_SYNTAX)
1239
1240         # empty RDN value
1241         try:
1242             self.ldb.add({
1243                 "dn": "cn=,cn=users," + self.base_dn,
1244                 "objectclass": "group"})
1245             self.fail()
1246         except LdbError as e63:
1247             (num, _) = e63.args
1248             self.assertEquals(num, ERR_INVALID_DN_SYNTAX)
1249
1250         # a wrong RDN candidate
1251         try:
1252             self.ldb.add({
1253                 "dn": "description=xyz,cn=users," + self.base_dn,
1254                 "objectclass": "group"})
1255             self.fail()
1256         except LdbError as e64:
1257             (num, _) = e64.args
1258             self.assertEquals(num, ERR_NAMING_VIOLATION)
1259
1260         delete_force(self.ldb, "description=xyz,cn=users," + self.base_dn)
1261
1262         # a wrong "name" attribute is obviously tolerated
1263         self.ldb.add({
1264             "dn": "cn=ldaptestgroup,cn=users," + self.base_dn,
1265             "objectclass": "group",
1266             "name": "ldaptestgroupx"})
1267
1268         # proof if the name has been set correctly
1269         res = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn,
1270                          scope=SCOPE_BASE, attrs=["name"])
1271         self.assertTrue(len(res) == 1)
1272         self.assertTrue("name" in res[0])
1273         self.assertTrue(res[0]["name"][0] == "ldaptestgroup")
1274
1275         # Modify
1276
1277         # empty RDN value
1278         m = Message()
1279         m.dn = Dn(ldb, "cn=,cn=users," + self.base_dn)
1280         m["description"] = "test"
1281         try:
1282             self.ldb.modify(m)
1283             self.fail()
1284         except LdbError as e65:
1285             (num, _) = e65.args
1286             self.assertEquals(num, ERR_INVALID_DN_SYNTAX)
1287
1288         # Delete
1289
1290         # empty RDN value
1291         try:
1292             self.ldb.delete("cn=,cn=users," + self.base_dn)
1293             self.fail()
1294         except LdbError as e66:
1295             (num, _) = e66.args
1296             self.assertEquals(num, ERR_INVALID_DN_SYNTAX)
1297
1298         # Rename
1299
1300         # new empty RDN
1301         try:
1302             self.ldb.rename("cn=ldaptestgroup,cn=users," + self.base_dn,
1303                             "=,cn=users," + self.base_dn)
1304             self.fail()
1305         except LdbError as e67:
1306             (num, _) = e67.args
1307             self.assertEquals(num, ERR_INVALID_DN_SYNTAX)
1308
1309         # new empty RDN name
1310         try:
1311             self.ldb.rename("cn=ldaptestgroup,cn=users," + self.base_dn,
1312                             "=ldaptestgroup,cn=users," + self.base_dn)
1313             self.fail()
1314         except LdbError as e68:
1315             (num, _) = e68.args
1316             self.assertEquals(num, ERR_INVALID_DN_SYNTAX)
1317
1318         # new empty RDN value
1319         try:
1320             self.ldb.rename("cn=ldaptestgroup,cn=users," + self.base_dn,
1321                             "cn=,cn=users," + self.base_dn)
1322             self.fail()
1323         except LdbError as e69:
1324             (num, _) = e69.args
1325             self.assertEquals(num, ERR_NAMING_VIOLATION)
1326
1327         # new wrong RDN candidate
1328         try:
1329             self.ldb.rename("cn=ldaptestgroup,cn=users," + self.base_dn,
1330                             "description=xyz,cn=users," + self.base_dn)
1331             self.fail()
1332         except LdbError as e70:
1333             (num, _) = e70.args
1334             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
1335
1336         delete_force(self.ldb, "description=xyz,cn=users," + self.base_dn)
1337
1338         # old empty RDN value
1339         try:
1340             self.ldb.rename("cn=,cn=users," + self.base_dn,
1341                             "cn=ldaptestgroup,cn=users," + self.base_dn)
1342             self.fail()
1343         except LdbError as e71:
1344             (num, _) = e71.args
1345             self.assertEquals(num, ERR_INVALID_DN_SYNTAX)
1346
1347         # names
1348
1349         m = Message()
1350         m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
1351         m["name"] = MessageElement("cn=ldaptestuser", FLAG_MOD_REPLACE,
1352                                    "name")
1353         try:
1354             ldb.modify(m)
1355             self.fail()
1356         except LdbError as e72:
1357             (num, _) = e72.args
1358             self.assertEquals(num, ERR_NOT_ALLOWED_ON_RDN)
1359
1360         m = Message()
1361         m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
1362         m["cn"] = MessageElement("ldaptestuser",
1363                                  FLAG_MOD_REPLACE, "cn")
1364         try:
1365             ldb.modify(m)
1366             self.fail()
1367         except LdbError as e73:
1368             (num, _) = e73.args
1369             self.assertEquals(num, ERR_NOT_ALLOWED_ON_RDN)
1370
1371         delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
1372
1373
1374         # this test needs to be disabled until we really understand
1375         # what the rDN length constraints are
1376     def DISABLED_test_largeRDN(self):
1377         """Testing large rDN (limit 64 characters)"""
1378         rdn = "CN=a012345678901234567890123456789012345678901234567890123456789012"
1379         delete_force(self.ldb, "%s,%s" % (rdn, self.base_dn))
1380         ldif = """
1381 dn: %s,%s""" % (rdn,self.base_dn) + """
1382 objectClass: container
1383 """
1384         self.ldb.add_ldif(ldif)
1385         delete_force(self.ldb, "%s,%s" % (rdn, self.base_dn))
1386
1387         rdn = "CN=a0123456789012345678901234567890123456789012345678901234567890120"
1388         delete_force(self.ldb, "%s,%s" % (rdn, self.base_dn))
1389         try:
1390             ldif = """
1391 dn: %s,%s""" % (rdn,self.base_dn) + """
1392 objectClass: container
1393 """
1394             self.ldb.add_ldif(ldif)
1395             self.fail()
1396         except LdbError as e74:
1397             (num, _) = e74.args
1398             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
1399         delete_force(self.ldb, "%s,%s" % (rdn, self.base_dn))
1400
1401     def test_rename(self):
1402         """Tests the rename operation"""
1403         try:
1404             # cannot rename to be a child of itself
1405             ldb.rename(self.base_dn, "dc=test," + self.base_dn)
1406             self.fail()
1407         except LdbError as e75:
1408             (num, _) = e75.args
1409             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
1410
1411         try:
1412             # inexistent object
1413             ldb.rename("cn=ldaptestuser2,cn=users," + self.base_dn, "cn=ldaptestuser2,cn=users," + self.base_dn)
1414             self.fail()
1415         except LdbError as e76:
1416             (num, _) = e76.args
1417             self.assertEquals(num, ERR_NO_SUCH_OBJECT)
1418
1419         self.ldb.add({
1420             "dn": "cn=ldaptestuser2,cn=users," + self.base_dn,
1421             "objectclass": "user"})
1422
1423         ldb.rename("cn=ldaptestuser2,cn=users," + self.base_dn, "cn=ldaptestuser2,cn=users," + self.base_dn)
1424         ldb.rename("cn=ldaptestuser2,cn=users," + self.base_dn, "cn=ldaptestuser3,cn=users," + self.base_dn)
1425         ldb.rename("cn=ldaptestuser3,cn=users," + self.base_dn, "cn=ldaptestUSER3,cn=users," + self.base_dn)
1426
1427         try:
1428             # containment problem: a user entry cannot contain user entries
1429             ldb.rename("cn=ldaptestuser3,cn=users," + self.base_dn, "cn=ldaptestuser4,cn=ldaptestuser3,cn=users," + self.base_dn)
1430             self.fail()
1431         except LdbError as e77:
1432             (num, _) = e77.args
1433             self.assertEquals(num, ERR_NAMING_VIOLATION)
1434
1435         try:
1436             # invalid parent
1437             ldb.rename("cn=ldaptestuser3,cn=users," + self.base_dn, "cn=ldaptestuser3,cn=people,cn=users," + self.base_dn)
1438             self.fail()
1439         except LdbError as e78:
1440             (num, _) = e78.args
1441             self.assertEquals(num, ERR_OTHER)
1442
1443         try:
1444             # invalid target DN syntax
1445             ldb.rename("cn=ldaptestuser3,cn=users," + self.base_dn, ",cn=users," + self.base_dn)
1446             self.fail()
1447         except LdbError as e79:
1448             (num, _) = e79.args
1449             self.assertEquals(num, ERR_INVALID_DN_SYNTAX)
1450
1451         try:
1452             # invalid RDN name
1453             ldb.rename("cn=ldaptestuser3,cn=users," + self.base_dn, "ou=ldaptestuser3,cn=users," + self.base_dn)
1454             self.fail()
1455         except LdbError as e80:
1456             (num, _) = e80.args
1457             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
1458
1459         delete_force(self.ldb, "cn=ldaptestuser3,cn=users," + self.base_dn)
1460
1461         # Performs some "systemFlags" testing
1462
1463         # Move failing since no "SYSTEM_FLAG_CONFIG_ALLOW_MOVE"
1464         try:
1465             ldb.rename("CN=DisplaySpecifiers," + self.configuration_dn, "CN=DisplaySpecifiers,CN=Services," + self.configuration_dn)
1466             self.fail()
1467         except LdbError as e81:
1468             (num, _) = e81.args
1469             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
1470
1471         # Limited move failing since no "SYSTEM_FLAG_CONFIG_ALLOW_LIMITED_MOVE"
1472         try:
1473             ldb.rename("CN=Directory Service,CN=Windows NT,CN=Services," + self.configuration_dn, "CN=Directory Service,CN=RRAS,CN=Services," + self.configuration_dn)
1474             self.fail()
1475         except LdbError as e82:
1476             (num, _) = e82.args
1477             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
1478
1479         # Rename failing since no "SYSTEM_FLAG_CONFIG_ALLOW_RENAME"
1480         try:
1481             ldb.rename("CN=DisplaySpecifiers," + self.configuration_dn, "CN=DisplaySpecifiers2," + self.configuration_dn)
1482             self.fail()
1483         except LdbError as e83:
1484             (num, _) = e83.args
1485             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
1486
1487         # It's not really possible to test moves on the schema partition since
1488         # there don't exist subcontainers on it.
1489
1490         # Rename failing since "SYSTEM_FLAG_SCHEMA_BASE_OBJECT"
1491         try:
1492             ldb.rename("CN=Top," + self.schema_dn, "CN=Top2," + self.schema_dn)
1493             self.fail()
1494         except LdbError as e84:
1495             (num, _) = e84.args
1496             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
1497
1498         # Move failing since "SYSTEM_FLAG_DOMAIN_DISALLOW_MOVE"
1499         try:
1500             ldb.rename("CN=Users," + self.base_dn, "CN=Users,CN=Computers," + self.base_dn)
1501             self.fail()
1502         except LdbError as e85:
1503             (num, _) = e85.args
1504             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
1505
1506         # Rename failing since "SYSTEM_FLAG_DOMAIN_DISALLOW_RENAME"
1507         try:
1508             ldb.rename("CN=Users," + self.base_dn, "CN=Users2," + self.base_dn)
1509             self.fail()
1510         except LdbError as e86:
1511             (num, _) = e86.args
1512             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
1513
1514         # Performs some other constraints testing
1515
1516         try:
1517             ldb.rename("CN=Policies,CN=System," + self.base_dn, "CN=Users2," + self.base_dn)
1518             self.fail()
1519         except LdbError as e87:
1520             (num, _) = e87.args
1521             self.assertEquals(num, ERR_OTHER)
1522
1523     def test_rename_twice(self):
1524         """Tests the rename operation twice - this corresponds to a past bug"""
1525         self.ldb.add({
1526             "dn": "cn=ldaptestuser5,cn=users," + self.base_dn,
1527             "objectclass": "user"})
1528
1529         ldb.rename("cn=ldaptestuser5,cn=users," + self.base_dn, "cn=ldaptestUSER5,cn=users," + self.base_dn)
1530         delete_force(self.ldb, "cn=ldaptestuser5,cn=users," + self.base_dn)
1531         self.ldb.add({
1532             "dn": "cn=ldaptestuser5,cn=users," + self.base_dn,
1533             "objectclass": "user"})
1534         ldb.rename("cn=ldaptestuser5,cn=Users," + self.base_dn, "cn=ldaptestUSER5,cn=users," + self.base_dn)
1535         res = ldb.search(expression="cn=ldaptestuser5")
1536         self.assertEquals(len(res), 1, "Wrong number of hits for cn=ldaptestuser5")
1537         res = ldb.search(expression="(&(cn=ldaptestuser5)(objectclass=user))")
1538         self.assertEquals(len(res), 1, "Wrong number of hits for (&(cn=ldaptestuser5)(objectclass=user))")
1539         delete_force(self.ldb, "cn=ldaptestuser5,cn=users," + self.base_dn)
1540
1541     def test_objectGUID(self):
1542         """Test objectGUID behaviour"""
1543         # The objectGUID cannot directly be set
1544         try:
1545             self.ldb.add_ldif("""
1546 dn: cn=ldaptestcontainer,""" + self.base_dn + """
1547 objectClass: container
1548 objectGUID: bd3480c9-58af-4cd8-92df-bc4a18b6e44d
1549 """)
1550             self.fail()
1551         except LdbError as e88:
1552             (num, _) = e88.args
1553             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
1554
1555         self.ldb.add({
1556             "dn": "cn=ldaptestcontainer," + self.base_dn,
1557             "objectClass": "container"})
1558
1559         # The objectGUID cannot directly be changed
1560         try:
1561             self.ldb.modify_ldif("""
1562 dn: cn=ldaptestcontainer,""" + self.base_dn + """
1563 changetype: modify
1564 replace: objectGUID
1565 objectGUID: bd3480c9-58af-4cd8-92df-bc4a18b6e44d
1566 """)
1567             self.fail()
1568         except LdbError as e89:
1569             (num, _) = e89.args
1570             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
1571
1572         delete_force(self.ldb, "cn=ldaptestcontainer," + self.base_dn)
1573
1574     def test_parentGUID(self):
1575         """Test parentGUID behaviour"""
1576         self.ldb.add({
1577             "dn": "cn=parentguidtest,cn=users," + self.base_dn,
1578             "objectclass":"user",
1579             "samaccountname":"parentguidtest"})
1580         res1 = ldb.search(base="cn=parentguidtest,cn=users," + self.base_dn, scope=SCOPE_BASE,
1581                           attrs=["parentGUID", "samaccountname"])
1582         res2 = ldb.search(base="cn=users," + self.base_dn,scope=SCOPE_BASE,
1583                           attrs=["objectGUID"])
1584         res3 = ldb.search(base=self.base_dn, scope=SCOPE_BASE,
1585                           attrs=["parentGUID"])
1586         res4 = ldb.search(base=self.configuration_dn, scope=SCOPE_BASE,
1587                           attrs=["parentGUID"])
1588         res5 = ldb.search(base=self.schema_dn, scope=SCOPE_BASE,
1589                           attrs=["parentGUID"])
1590
1591         """Check if the parentGUID is valid """
1592         self.assertEquals(res1[0]["parentGUID"], res2[0]["objectGUID"])
1593
1594         """Check if it returns nothing when there is no parent object - default NC"""
1595         has_parentGUID = False
1596         for key in res3[0].keys():
1597             if key == "parentGUID":
1598                 has_parentGUID = True
1599                 break
1600         self.assertFalse(has_parentGUID)
1601
1602         """Check if it returns nothing when there is no parent object - configuration NC"""
1603         has_parentGUID = False
1604         for key in res4[0].keys():
1605             if key == "parentGUID":
1606                 has_parentGUID = True
1607                 break
1608         self.assertFalse(has_parentGUID)
1609
1610         """Check if it returns nothing when there is no parent object - schema NC"""
1611         has_parentGUID = False
1612         for key in res5[0].keys():
1613             if key == "parentGUID":
1614                 has_parentGUID = True
1615                 break
1616         self.assertFalse(has_parentGUID)
1617
1618         """Ensures that if you look for another object attribute after the constructed
1619             parentGUID, it will return correctly"""
1620         has_another_attribute = False
1621         for key in res1[0].keys():
1622             if key == "sAMAccountName":
1623                 has_another_attribute = True
1624                 break
1625         self.assertTrue(has_another_attribute)
1626         self.assertTrue(len(res1[0]["samaccountname"]) == 1)
1627         self.assertEquals(res1[0]["samaccountname"][0], "parentguidtest")
1628
1629         # Testing parentGUID behaviour on rename\
1630
1631         self.ldb.add({
1632             "dn": "cn=testotherusers," + self.base_dn,
1633             "objectclass":"container"})
1634         res1 = ldb.search(base="cn=testotherusers," + self.base_dn,scope=SCOPE_BASE,
1635                           attrs=["objectGUID"])
1636         ldb.rename("cn=parentguidtest,cn=users," + self.base_dn,
1637                    "cn=parentguidtest,cn=testotherusers," + self.base_dn)
1638         res2 = ldb.search(base="cn=parentguidtest,cn=testotherusers," + self.base_dn,
1639                           scope=SCOPE_BASE,
1640                           attrs=["parentGUID"])
1641         self.assertEquals(res1[0]["objectGUID"], res2[0]["parentGUID"])
1642
1643         delete_force(self.ldb, "cn=parentguidtest,cn=testotherusers," + self.base_dn)
1644         delete_force(self.ldb, "cn=testotherusers," + self.base_dn)
1645
1646     def test_usnChanged(self):
1647         """Test usnChanged behaviour"""
1648
1649         self.ldb.add({
1650             "dn": "cn=ldaptestcontainer," + self.base_dn,
1651             "objectClass": "container"})
1652
1653         res = ldb.search("cn=ldaptestcontainer," + self.base_dn,
1654                          scope=SCOPE_BASE,
1655                          attrs=["objectGUID", "uSNCreated", "uSNChanged", "whenCreated", "whenChanged", "description"])
1656         self.assertTrue(len(res) == 1)
1657         self.assertFalse("description" in res[0])
1658         self.assertTrue("objectGUID" in res[0])
1659         self.assertTrue("uSNCreated" in res[0])
1660         self.assertTrue("uSNChanged" in res[0])
1661         self.assertTrue("whenCreated" in res[0])
1662         self.assertTrue("whenChanged" in res[0])
1663
1664         delete_force(self.ldb, "cn=ldaptestcontainer," + self.base_dn)
1665
1666         # All this attributes are specificable on add operations
1667         self.ldb.add({
1668             "dn": "cn=ldaptestcontainer," + self.base_dn,
1669             "objectclass": "container",
1670             "uSNCreated": "1",
1671             "uSNChanged": "1",
1672             "whenCreated": timestring(long(time.time())),
1673             "whenChanged": timestring(long(time.time()))})
1674
1675         res = ldb.search("cn=ldaptestcontainer," + self.base_dn,
1676                          scope=SCOPE_BASE,
1677                          attrs=["objectGUID", "uSNCreated", "uSNChanged", "whenCreated", "whenChanged", "description"])
1678         self.assertTrue(len(res) == 1)
1679         self.assertFalse("description" in res[0])
1680         self.assertTrue("objectGUID" in res[0])
1681         self.assertTrue("uSNCreated" in res[0])
1682         self.assertFalse(res[0]["uSNCreated"][0] == "1") # these are corrected
1683         self.assertTrue("uSNChanged" in res[0])
1684         self.assertFalse(res[0]["uSNChanged"][0] == "1") # these are corrected
1685         self.assertTrue("whenCreated" in res[0])
1686         self.assertTrue("whenChanged" in res[0])
1687
1688         ldb.modify_ldif("""
1689 dn: cn=ldaptestcontainer,""" + self.base_dn + """
1690 changetype: modify
1691 replace: description
1692 """)
1693
1694         res2 = ldb.search("cn=ldaptestcontainer," + self.base_dn,
1695                           scope=SCOPE_BASE,
1696                           attrs=["uSNCreated", "uSNChanged", "description"])
1697         self.assertTrue(len(res) == 1)
1698         self.assertFalse("description" in res2[0])
1699         self.assertEqual(res[0]["usnCreated"], res2[0]["usnCreated"])
1700         self.assertEqual(res[0]["usnCreated"], res2[0]["usnChanged"])
1701         self.assertEqual(res[0]["usnChanged"], res2[0]["usnChanged"])
1702
1703         ldb.modify_ldif("""
1704 dn: cn=ldaptestcontainer,""" + self.base_dn + """
1705 changetype: modify
1706 replace: description
1707 description: test
1708 """)
1709
1710         res3 = ldb.search("cn=ldaptestcontainer," + self.base_dn,
1711                           scope=SCOPE_BASE,
1712                           attrs=["uSNCreated", "uSNChanged", "description"])
1713         self.assertTrue(len(res) == 1)
1714         self.assertTrue("description" in res3[0])
1715         self.assertEqual("test", str(res3[0]["description"][0]))
1716         self.assertEqual(res[0]["usnCreated"], res3[0]["usnCreated"])
1717         self.assertNotEqual(res[0]["usnCreated"], res3[0]["usnChanged"])
1718         self.assertNotEqual(res[0]["usnChanged"], res3[0]["usnChanged"])
1719
1720         ldb.modify_ldif("""
1721 dn: cn=ldaptestcontainer,""" + self.base_dn + """
1722 changetype: modify
1723 replace: description
1724 description: test
1725 """)
1726
1727         res4 = ldb.search("cn=ldaptestcontainer," + self.base_dn,
1728                           scope=SCOPE_BASE,
1729                           attrs=["uSNCreated", "uSNChanged", "description"])
1730         self.assertTrue(len(res) == 1)
1731         self.assertTrue("description" in res4[0])
1732         self.assertEqual("test", str(res4[0]["description"][0]))
1733         self.assertEqual(res[0]["usnCreated"], res4[0]["usnCreated"])
1734         self.assertNotEqual(res3[0]["usnCreated"], res4[0]["usnChanged"])
1735         self.assertEqual(res3[0]["usnChanged"], res4[0]["usnChanged"])
1736
1737         ldb.modify_ldif("""
1738 dn: cn=ldaptestcontainer,""" + self.base_dn + """
1739 changetype: modify
1740 replace: description
1741 description: test2
1742 """)
1743
1744         res5 = ldb.search("cn=ldaptestcontainer," + self.base_dn,
1745                           scope=SCOPE_BASE,
1746                           attrs=["uSNCreated", "uSNChanged", "description"])
1747         self.assertTrue(len(res) == 1)
1748         self.assertTrue("description" in res5[0])
1749         self.assertEqual("test2", str(res5[0]["description"][0]))
1750         self.assertEqual(res[0]["usnCreated"], res5[0]["usnCreated"])
1751         self.assertNotEqual(res3[0]["usnChanged"], res5[0]["usnChanged"])
1752
1753         ldb.modify_ldif("""
1754 dn: cn=ldaptestcontainer,""" + self.base_dn + """
1755 changetype: modify
1756 delete: description
1757 description: test2
1758 """)
1759
1760         res6 = ldb.search("cn=ldaptestcontainer," + self.base_dn,
1761                           scope=SCOPE_BASE,
1762                           attrs=["uSNCreated", "uSNChanged", "description"])
1763         self.assertTrue(len(res) == 1)
1764         self.assertFalse("description" in res6[0])
1765         self.assertEqual(res[0]["usnCreated"], res6[0]["usnCreated"])
1766         self.assertNotEqual(res5[0]["usnChanged"], res6[0]["usnChanged"])
1767
1768         ldb.modify_ldif("""
1769 dn: cn=ldaptestcontainer,""" + self.base_dn + """
1770 changetype: modify
1771 add: description
1772 description: test3
1773 """)
1774
1775         res7 = ldb.search("cn=ldaptestcontainer," + self.base_dn,
1776                           scope=SCOPE_BASE,
1777                           attrs=["uSNCreated", "uSNChanged", "description"])
1778         self.assertTrue(len(res) == 1)
1779         self.assertTrue("description" in res7[0])
1780         self.assertEqual("test3", str(res7[0]["description"][0]))
1781         self.assertEqual(res[0]["usnCreated"], res7[0]["usnCreated"])
1782         self.assertNotEqual(res6[0]["usnChanged"], res7[0]["usnChanged"])
1783
1784         ldb.modify_ldif("""
1785 dn: cn=ldaptestcontainer,""" + self.base_dn + """
1786 changetype: modify
1787 delete: description
1788 """)
1789
1790         res8 = ldb.search("cn=ldaptestcontainer," + self.base_dn,
1791                           scope=SCOPE_BASE,
1792                           attrs=["uSNCreated", "uSNChanged", "description"])
1793         self.assertTrue(len(res) == 1)
1794         self.assertFalse("description" in res8[0])
1795         self.assertEqual(res[0]["usnCreated"], res8[0]["usnCreated"])
1796         self.assertNotEqual(res7[0]["usnChanged"], res8[0]["usnChanged"])
1797
1798         delete_force(self.ldb, "cn=ldaptestcontainer," + self.base_dn)
1799
1800     def test_groupType_int32(self):
1801         """Test groupType (int32) behaviour (should appear to be casted to a 32 bit signed integer before comparsion)"""
1802
1803         res1 = ldb.search(base=self.base_dn, scope=SCOPE_SUBTREE,
1804                           attrs=["groupType"], expression="groupType=2147483653")
1805
1806         res2 = ldb.search(base=self.base_dn, scope=SCOPE_SUBTREE,
1807                           attrs=["groupType"], expression="groupType=-2147483643")
1808
1809         self.assertEquals(len(res1), len(res2))
1810
1811         self.assertTrue(res1.count > 0)
1812
1813         self.assertEquals(res1[0]["groupType"][0], "-2147483643")
1814
1815     def test_linked_attributes(self):
1816         """This tests the linked attribute behaviour"""
1817
1818         ldb.add({
1819             "dn": "cn=ldaptestgroup,cn=users," + self.base_dn,
1820             "objectclass": "group"})
1821
1822         # This should not work since "memberOf" is linked to "member"
1823         try:
1824             ldb.add({
1825                 "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
1826                 "objectclass": "user",
1827                 "memberOf": "cn=ldaptestgroup,cn=users," + self.base_dn})
1828         except LdbError as e90:
1829             (num, _) = e90.args
1830             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
1831
1832         ldb.add({
1833             "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
1834             "objectclass": "user"})
1835
1836         m = Message()
1837         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
1838         m["memberOf"] = MessageElement("cn=ldaptestgroup,cn=users," + self.base_dn,
1839                                        FLAG_MOD_ADD, "memberOf")
1840         try:
1841             ldb.modify(m)
1842             self.fail()
1843         except LdbError as e91:
1844             (num, _) = e91.args
1845             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
1846
1847         m = Message()
1848         m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
1849         m["member"] = MessageElement("cn=ldaptestuser,cn=users," + self.base_dn,
1850                                      FLAG_MOD_ADD, "member")
1851         ldb.modify(m)
1852
1853         m = Message()
1854         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
1855         m["memberOf"] = MessageElement("cn=ldaptestgroup,cn=users," + self.base_dn,
1856                                        FLAG_MOD_REPLACE, "memberOf")
1857         try:
1858             ldb.modify(m)
1859             self.fail()
1860         except LdbError as e92:
1861             (num, _) = e92.args
1862             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
1863
1864         m = Message()
1865         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
1866         m["memberOf"] = MessageElement("cn=ldaptestgroup,cn=users," + self.base_dn,
1867                                        FLAG_MOD_DELETE, "memberOf")
1868         try:
1869             ldb.modify(m)
1870             self.fail()
1871         except LdbError as e93:
1872             (num, _) = e93.args
1873             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
1874
1875         m = Message()
1876         m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
1877         m["member"] = MessageElement("cn=ldaptestuser,cn=users," + self.base_dn,
1878                                      FLAG_MOD_DELETE, "member")
1879         ldb.modify(m)
1880
1881         # This should yield no results since the member attribute for
1882         # "ldaptestuser" should have been deleted
1883         res1 = ldb.search("cn=ldaptestgroup, cn=users," + self.base_dn,
1884                           scope=SCOPE_BASE,
1885                           expression="(member=cn=ldaptestuser,cn=users," + self.base_dn + ")",
1886                           attrs=[])
1887         self.assertTrue(len(res1) == 0)
1888
1889         delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
1890
1891         ldb.add({
1892             "dn": "cn=ldaptestgroup,cn=users," + self.base_dn,
1893             "objectclass": "group",
1894             "member": "cn=ldaptestuser,cn=users," + self.base_dn})
1895
1896         delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
1897
1898         # Make sure that the "member" attribute for "ldaptestuser" has been
1899         # removed
1900         res = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn,
1901                          scope=SCOPE_BASE, attrs=["member"])
1902         self.assertTrue(len(res) == 1)
1903         self.assertFalse("member" in res[0])
1904
1905         delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
1906
1907     def test_wkguid(self):
1908         """Test Well known GUID behaviours (including DN+Binary)"""
1909
1910         res = self.ldb.search(base=("<WKGUID=ab1d30f3768811d1aded00c04fd8d5cd,%s>" % self.base_dn), scope=SCOPE_BASE, attrs=[])
1911         self.assertEquals(len(res), 1)
1912
1913         res2 = self.ldb.search(scope=SCOPE_BASE, attrs=["wellKnownObjects"], expression=("wellKnownObjects=B:32:ab1d30f3768811d1aded00c04fd8d5cd:%s" % res[0].dn))
1914         self.assertEquals(len(res2), 1)
1915
1916         # Prove that the matching rule is over the whole DN+Binary
1917         res2 = self.ldb.search(scope=SCOPE_BASE, attrs=["wellKnownObjects"], expression=("wellKnownObjects=B:32:ab1d30f3768811d1aded00c04fd8d5cd"))
1918         self.assertEquals(len(res2), 0)
1919         # Prove that the matching rule is over the whole DN+Binary
1920         res2 = self.ldb.search(scope=SCOPE_BASE, attrs=["wellKnownObjects"], expression=("wellKnownObjects=%s") % res[0].dn)
1921         self.assertEquals(len(res2), 0)
1922
1923     def test_subschemasubentry(self):
1924         """Test subSchemaSubEntry appears when requested, but not when not requested"""
1925
1926         res = self.ldb.search(base=self.base_dn, scope=SCOPE_BASE, attrs=["subSchemaSubEntry"])
1927         self.assertEquals(len(res), 1)
1928         self.assertEquals(res[0]["subSchemaSubEntry"][0], "CN=Aggregate,"+self.schema_dn)
1929
1930         res = self.ldb.search(base=self.base_dn, scope=SCOPE_BASE, attrs=["*"])
1931         self.assertEquals(len(res), 1)
1932         self.assertTrue("subScheamSubEntry" not in res[0])
1933
1934     def test_all(self):
1935         """Basic tests"""
1936
1937         # Testing user add
1938
1939         ldb.add({
1940             "dn": "cn=ldaptestuser,cn=uSers," + self.base_dn,
1941             "objectclass": "user",
1942             "cN": "LDAPtestUSER",
1943             "givenname": "ldap",
1944             "sn": "testy"})
1945
1946         ldb.add({
1947             "dn": "cn=ldaptestgroup,cn=uSers," + self.base_dn,
1948             "objectclass": "group",
1949             "member": "cn=ldaptestuser,cn=useRs," + self.base_dn})
1950
1951         ldb.add({
1952             "dn": "cn=ldaptestcomputer,cn=computers," + self.base_dn,
1953             "objectclass": "computer",
1954             "cN": "LDAPtestCOMPUTER"})
1955
1956         ldb.add({"dn": "cn=ldaptest2computer,cn=computers," + self.base_dn,
1957                  "objectClass": "computer",
1958                  "cn": "LDAPtest2COMPUTER",
1959                  "userAccountControl": str(UF_WORKSTATION_TRUST_ACCOUNT),
1960                  "displayname": "ldap testy"})
1961
1962         try:
1963             ldb.add({"dn": "cn=ldaptestcomputer3,cn=computers," + self.base_dn,
1964                      "objectClass": "computer",
1965                      "cn": "LDAPtest2COMPUTER"
1966                      })
1967             self.fail()
1968         except LdbError as e94:
1969             (num, _) = e94.args
1970             self.assertEquals(num, ERR_INVALID_DN_SYNTAX)
1971
1972         try:
1973             ldb.add({"dn": "cn=ldaptestcomputer3,cn=computers," + self.base_dn,
1974                      "objectClass": "computer",
1975                      "cn": "ldaptestcomputer3",
1976                      "sAMAccountType": str(ATYPE_NORMAL_ACCOUNT)
1977                      })
1978             self.fail()
1979         except LdbError as e95:
1980             (num, _) = e95.args
1981             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
1982
1983         ldb.add({"dn": "cn=ldaptestcomputer3,cn=computers," + self.base_dn,
1984                  "objectClass": "computer",
1985                  "cn": "LDAPtestCOMPUTER3"
1986                  })
1987
1988         # Testing ldb.search for (&(cn=ldaptestcomputer3)(objectClass=user))
1989         res = ldb.search(self.base_dn, expression="(&(cn=ldaptestcomputer3)(objectClass=user))")
1990         self.assertEquals(len(res), 1, "Found only %d for (&(cn=ldaptestcomputer3)(objectClass=user))" % len(res))
1991
1992         self.assertEquals(str(res[0].dn), ("CN=ldaptestcomputer3,CN=Computers," + self.base_dn))
1993         self.assertEquals(res[0]["cn"][0], "ldaptestcomputer3")
1994         self.assertEquals(res[0]["name"][0], "ldaptestcomputer3")
1995         self.assertEquals(res[0]["objectClass"][0], "top")
1996         self.assertEquals(res[0]["objectClass"][1], "person")
1997         self.assertEquals(res[0]["objectClass"][2], "organizationalPerson")
1998         self.assertEquals(res[0]["objectClass"][3], "user")
1999         self.assertEquals(res[0]["objectClass"][4], "computer")
2000         self.assertTrue("objectGUID" in res[0])
2001         self.assertTrue("whenCreated" in res[0])
2002         self.assertEquals(res[0]["objectCategory"][0], ("CN=Computer,%s" % ldb.get_schema_basedn()))
2003         self.assertEquals(int(res[0]["primaryGroupID"][0]), 513)
2004         self.assertEquals(int(res[0]["sAMAccountType"][0]), ATYPE_NORMAL_ACCOUNT)
2005         self.assertEquals(int(res[0]["userAccountControl"][0]), UF_NORMAL_ACCOUNT | UF_PASSWD_NOTREQD | UF_ACCOUNTDISABLE)
2006
2007         delete_force(self.ldb, "cn=ldaptestcomputer3,cn=computers," + self.base_dn)
2008
2009         # Testing attribute or value exists behaviour
2010         try:
2011             ldb.modify_ldif("""
2012 dn: cn=ldaptest2computer,cn=computers,""" + self.base_dn + """
2013 changetype: modify
2014 replace: servicePrincipalName
2015 servicePrincipalName: host/ldaptest2computer
2016 servicePrincipalName: host/ldaptest2computer
2017 servicePrincipalName: cifs/ldaptest2computer
2018 """)
2019             self.fail()
2020         except LdbError as e96:
2021             (num, msg) = e96.args
2022             self.assertEquals(num, ERR_ATTRIBUTE_OR_VALUE_EXISTS)
2023
2024         ldb.modify_ldif("""
2025 dn: cn=ldaptest2computer,cn=computers,""" + self.base_dn + """
2026 changetype: modify
2027 replace: servicePrincipalName
2028 servicePrincipalName: host/ldaptest2computer
2029 servicePrincipalName: cifs/ldaptest2computer
2030 """)
2031         try:
2032             ldb.modify_ldif("""
2033 dn: cn=ldaptest2computer,cn=computers,""" + self.base_dn + """
2034 changetype: modify
2035 add: servicePrincipalName
2036 servicePrincipalName: host/ldaptest2computer
2037 """)
2038             self.fail()
2039         except LdbError as e97:
2040             (num, msg) = e97.args
2041             self.assertEquals(num, ERR_ATTRIBUTE_OR_VALUE_EXISTS)
2042
2043         # Testing ranged results
2044         ldb.modify_ldif("""
2045 dn: cn=ldaptest2computer,cn=computers,""" + self.base_dn + """
2046 changetype: modify
2047 replace: servicePrincipalName
2048 """)
2049
2050         ldb.modify_ldif("""
2051 dn: cn=ldaptest2computer,cn=computers,""" + self.base_dn + """
2052 changetype: modify
2053 add: servicePrincipalName
2054 servicePrincipalName: host/ldaptest2computer0
2055 servicePrincipalName: host/ldaptest2computer1
2056 servicePrincipalName: host/ldaptest2computer2
2057 servicePrincipalName: host/ldaptest2computer3
2058 servicePrincipalName: host/ldaptest2computer4
2059 servicePrincipalName: host/ldaptest2computer5
2060 servicePrincipalName: host/ldaptest2computer6
2061 servicePrincipalName: host/ldaptest2computer7
2062 servicePrincipalName: host/ldaptest2computer8
2063 servicePrincipalName: host/ldaptest2computer9
2064 servicePrincipalName: host/ldaptest2computer10
2065 servicePrincipalName: host/ldaptest2computer11
2066 servicePrincipalName: host/ldaptest2computer12
2067 servicePrincipalName: host/ldaptest2computer13
2068 servicePrincipalName: host/ldaptest2computer14
2069 servicePrincipalName: host/ldaptest2computer15
2070 servicePrincipalName: host/ldaptest2computer16
2071 servicePrincipalName: host/ldaptest2computer17
2072 servicePrincipalName: host/ldaptest2computer18
2073 servicePrincipalName: host/ldaptest2computer19
2074 servicePrincipalName: host/ldaptest2computer20
2075 servicePrincipalName: host/ldaptest2computer21
2076 servicePrincipalName: host/ldaptest2computer22
2077 servicePrincipalName: host/ldaptest2computer23
2078 servicePrincipalName: host/ldaptest2computer24
2079 servicePrincipalName: host/ldaptest2computer25
2080 servicePrincipalName: host/ldaptest2computer26
2081 servicePrincipalName: host/ldaptest2computer27
2082 servicePrincipalName: host/ldaptest2computer28
2083 servicePrincipalName: host/ldaptest2computer29
2084 """)
2085
2086         res = ldb.search(self.base_dn, expression="(cn=ldaptest2computer))", scope=SCOPE_SUBTREE,
2087                          attrs=["servicePrincipalName;range=0-*"])
2088         self.assertEquals(len(res), 1, "Could not find (cn=ldaptest2computer)")
2089         self.assertEquals(len(res[0]["servicePrincipalName;range=0-*"]), 30)
2090
2091         res = ldb.search(self.base_dn, expression="(cn=ldaptest2computer))", scope=SCOPE_SUBTREE, attrs=["servicePrincipalName;range=0-19"])
2092         self.assertEquals(len(res), 1, "Could not find (cn=ldaptest2computer)")
2093         self.assertEquals(len(res[0]["servicePrincipalName;range=0-19"]), 20)
2094
2095
2096         res = ldb.search(self.base_dn, expression="(cn=ldaptest2computer))", scope=SCOPE_SUBTREE, attrs=["servicePrincipalName;range=0-30"])
2097         self.assertEquals(len(res), 1, "Could not find (cn=ldaptest2computer)")
2098         self.assertEquals(len(res[0]["servicePrincipalName;range=0-*"]), 30)
2099
2100         res = ldb.search(self.base_dn, expression="(cn=ldaptest2computer))", scope=SCOPE_SUBTREE, attrs=["servicePrincipalName;range=0-40"])
2101         self.assertEquals(len(res), 1, "Could not find (cn=ldaptest2computer)")
2102         self.assertEquals(len(res[0]["servicePrincipalName;range=0-*"]), 30)
2103
2104         res = ldb.search(self.base_dn, expression="(cn=ldaptest2computer))", scope=SCOPE_SUBTREE, attrs=["servicePrincipalName;range=30-40"])
2105         self.assertEquals(len(res), 1, "Could not find (cn=ldaptest2computer)")
2106         self.assertEquals(len(res[0]["servicePrincipalName;range=30-*"]), 0)
2107
2108
2109         res = ldb.search(self.base_dn, expression="(cn=ldaptest2computer))", scope=SCOPE_SUBTREE, attrs=["servicePrincipalName;range=10-40"])
2110         self.assertEquals(len(res), 1, "Could not find (cn=ldaptest2computer)")
2111         self.assertEquals(len(res[0]["servicePrincipalName;range=10-*"]), 20)
2112         # pos_11 = res[0]["servicePrincipalName;range=10-*"][18]
2113
2114         res = ldb.search(self.base_dn, expression="(cn=ldaptest2computer))", scope=SCOPE_SUBTREE, attrs=["servicePrincipalName;range=11-40"])
2115         self.assertEquals(len(res), 1, "Could not find (cn=ldaptest2computer)")
2116         self.assertEquals(len(res[0]["servicePrincipalName;range=11-*"]), 19)
2117         # self.assertEquals((res[0]["servicePrincipalName;range=11-*"][18]), pos_11)
2118
2119         res = ldb.search(self.base_dn, expression="(cn=ldaptest2computer))", scope=SCOPE_SUBTREE, attrs=["servicePrincipalName;range=11-15"])
2120         self.assertEquals(len(res), 1, "Could not find (cn=ldaptest2computer)")
2121         self.assertEquals(len(res[0]["servicePrincipalName;range=11-15"]), 5)
2122         # self.assertEquals(res[0]["servicePrincipalName;range=11-15"][4], pos_11)
2123
2124         res = ldb.search(self.base_dn, expression="(cn=ldaptest2computer))", scope=SCOPE_SUBTREE, attrs=["servicePrincipalName"])
2125         self.assertEquals(len(res), 1, "Could not find (cn=ldaptest2computer)")
2126         self.assertEquals(len(res[0]["servicePrincipalName"]), 30)
2127         # self.assertEquals(res[0]["servicePrincipalName"][18], pos_11)
2128
2129         delete_force(self.ldb, "cn=ldaptestuser2,cn=users," + self.base_dn)
2130         ldb.add({
2131             "dn": "cn=ldaptestuser2,cn=useRs," + self.base_dn,
2132             "objectClass": "user",
2133             "cn": "LDAPtestUSER2",
2134             "givenname": "testy",
2135             "sn": "ldap user2"})
2136
2137         # Testing Ambigious Name Resolution
2138         # Testing ldb.search for (&(anr=ldap testy)(objectClass=user))
2139         res = ldb.search(expression="(&(anr=ldap testy)(objectClass=user))")
2140         self.assertEquals(len(res), 3, "Found only %d of 3 for (&(anr=ldap testy)(objectClass=user))" % len(res))
2141
2142         # Testing ldb.search for (&(anr=testy ldap)(objectClass=user))
2143         res = ldb.search(expression="(&(anr=testy ldap)(objectClass=user))")
2144         self.assertEquals(len(res), 2, "Found only %d of 2 for (&(anr=testy ldap)(objectClass=user))" % len(res))
2145
2146         # Testing ldb.search for (&(anr=ldap)(objectClass=user))
2147         res = ldb.search(expression="(&(anr=ldap)(objectClass=user))")
2148         self.assertEquals(len(res), 4, "Found only %d of 4 for (&(anr=ldap)(objectClass=user))" % len(res))
2149
2150         # Testing ldb.search for (&(anr==ldap)(objectClass=user))
2151         res = ldb.search(expression="(&(anr==ldap)(objectClass=user))")
2152         self.assertEquals(len(res), 1, "Could not find (&(anr==ldap)(objectClass=user)). Found only %d for (&(anr=ldap)(objectClass=user))" % len(res))
2153
2154         self.assertEquals(str(res[0].dn), ("CN=ldaptestuser,CN=Users," + self.base_dn))
2155         self.assertEquals(res[0]["cn"][0], "ldaptestuser")
2156         self.assertEquals(str(res[0]["name"]), "ldaptestuser")
2157
2158         # Testing ldb.search for (&(anr=testy)(objectClass=user))
2159         res = ldb.search(expression="(&(anr=testy)(objectClass=user))")
2160         self.assertEquals(len(res), 2, "Found only %d for (&(anr=testy)(objectClass=user))" % len(res))
2161
2162         # Testing ldb.search for (&(anr=testy ldap)(objectClass=user))
2163         res = ldb.search(expression="(&(anr=testy ldap)(objectClass=user))")
2164         self.assertEquals(len(res), 2, "Found only %d for (&(anr=testy ldap)(objectClass=user))" % len(res))
2165
2166         # Testing ldb.search for (&(anr==testy ldap)(objectClass=user))
2167 # this test disabled for the moment, as anr with == tests are not understood
2168 #        res = ldb.search(expression="(&(anr==testy ldap)(objectClass=user))")
2169 #        self.assertEquals(len(res), 1, "Found only %d for (&(anr==testy ldap)(objectClass=user))" % len(res))
2170
2171 #        self.assertEquals(str(res[0].dn), ("CN=ldaptestuser,CN=Users," + self.base_dn))
2172 #        self.assertEquals(res[0]["cn"][0], "ldaptestuser")
2173 #        self.assertEquals(res[0]["name"][0], "ldaptestuser")
2174
2175         # Testing ldb.search for (&(anr==testy ldap)(objectClass=user))
2176 #        res = ldb.search(expression="(&(anr==testy ldap)(objectClass=user))")
2177 #        self.assertEquals(len(res), 1, "Could not find (&(anr==testy ldap)(objectClass=user))")
2178
2179 #        self.assertEquals(str(res[0].dn), ("CN=ldaptestuser,CN=Users," + self.base_dn))
2180 #        self.assertEquals(res[0]["cn"][0], "ldaptestuser")
2181 #        self.assertEquals(res[0]["name"][0], "ldaptestuser")
2182
2183         # Testing ldb.search for (&(anr=testy ldap user)(objectClass=user))
2184         res = ldb.search(expression="(&(anr=testy ldap user)(objectClass=user))")
2185         self.assertEquals(len(res), 1, "Could not find (&(anr=testy ldap user)(objectClass=user))")
2186
2187         self.assertEquals(str(res[0].dn), ("CN=ldaptestuser2,CN=Users," + self.base_dn))
2188         self.assertEquals(str(res[0]["cn"]), "ldaptestuser2")
2189         self.assertEquals(str(res[0]["name"]), "ldaptestuser2")
2190
2191         # Testing ldb.search for (&(anr==testy ldap user2)(objectClass=user))
2192 #        res = ldb.search(expression="(&(anr==testy ldap user2)(objectClass=user))")
2193 #        self.assertEquals(len(res), 1, "Could not find (&(anr==testy ldap user2)(objectClass=user))")
2194
2195         self.assertEquals(str(res[0].dn), ("CN=ldaptestuser2,CN=Users," + self.base_dn))
2196         self.assertEquals(str(res[0]["cn"]), "ldaptestuser2")
2197         self.assertEquals(str(res[0]["name"]), "ldaptestuser2")
2198
2199         # Testing ldb.search for (&(anr==ldap user2)(objectClass=user))
2200 #        res = ldb.search(expression="(&(anr==ldap user2)(objectClass=user))")
2201 #        self.assertEquals(len(res), 1, "Could not find (&(anr==ldap user2)(objectClass=user))")
2202
2203         self.assertEquals(str(res[0].dn), ("CN=ldaptestuser2,CN=Users," + self.base_dn))
2204         self.assertEquals(str(res[0]["cn"]), "ldaptestuser2")
2205         self.assertEquals(str(res[0]["name"]), "ldaptestuser2")
2206
2207         # Testing ldb.search for (&(anr==not ldap user2)(objectClass=user))
2208 #        res = ldb.search(expression="(&(anr==not ldap user2)(objectClass=user))")
2209 #        self.assertEquals(len(res), 0, "Must not find (&(anr==not ldap user2)(objectClass=user))")
2210
2211         # Testing ldb.search for (&(anr=not ldap user2)(objectClass=user))
2212         res = ldb.search(expression="(&(anr=not ldap user2)(objectClass=user))")
2213         self.assertEquals(len(res), 0, "Must not find (&(anr=not ldap user2)(objectClass=user))")
2214
2215         # Testing ldb.search for (&(anr="testy ldap")(objectClass=user)) (ie, with quotes)
2216 #        res = ldb.search(expression="(&(anr==\"testy ldap\")(objectClass=user))")
2217 #        self.assertEquals(len(res), 0, "Found (&(anr==\"testy ldap\")(objectClass=user))")
2218
2219         # Testing Renames
2220
2221         attrs = ["objectGUID", "objectSid"]
2222         # Testing ldb.search for (&(cn=ldaptestUSer2)(objectClass=user))
2223         res_user = ldb.search(self.base_dn, expression="(&(cn=ldaptestUSer2)(objectClass=user))", scope=SCOPE_SUBTREE, attrs=attrs)
2224         self.assertEquals(len(res_user), 1, "Could not find (&(cn=ldaptestUSer2)(objectClass=user))")
2225
2226         # Check rename works with extended/alternate DN forms
2227         ldb.rename("<SID=" + ldb.schema_format_value("objectSID", res_user[0]["objectSID"][0]) + ">", "cn=ldaptestUSER3,cn=users," + self.base_dn)
2228
2229         # Testing ldb.search for (&(cn=ldaptestuser3)(objectClass=user))
2230         res = ldb.search(expression="(&(cn=ldaptestuser3)(objectClass=user))")
2231         self.assertEquals(len(res), 1, "Could not find (&(cn=ldaptestuser3)(objectClass=user))")
2232
2233         self.assertEquals(str(res[0].dn), ("CN=ldaptestUSER3,CN=Users," + self.base_dn))
2234         self.assertEquals(str(res[0]["cn"]), "ldaptestUSER3")
2235         self.assertEquals(str(res[0]["name"]), "ldaptestUSER3")
2236
2237         #"Testing ldb.search for (&(&(cn=ldaptestuser3)(userAccountControl=*))(objectClass=user))"
2238         res = ldb.search(expression="(&(&(cn=ldaptestuser3)(userAccountControl=*))(objectClass=user))")
2239         self.assertEquals(len(res), 1, "(&(&(cn=ldaptestuser3)(userAccountControl=*))(objectClass=user))")
2240
2241         self.assertEquals(str(res[0].dn), ("CN=ldaptestUSER3,CN=Users," + self.base_dn))
2242         self.assertEquals(str(res[0]["cn"]), "ldaptestUSER3")
2243         self.assertEquals(str(res[0]["name"]), "ldaptestUSER3")
2244
2245         #"Testing ldb.search for (&(&(cn=ldaptestuser3)(userAccountControl=546))(objectClass=user))"
2246         res = ldb.search(expression="(&(&(cn=ldaptestuser3)(userAccountControl=546))(objectClass=user))")
2247         self.assertEquals(len(res), 1, "(&(&(cn=ldaptestuser3)(userAccountControl=546))(objectClass=user))")
2248
2249         self.assertEquals(str(res[0].dn), ("CN=ldaptestUSER3,CN=Users," + self.base_dn))
2250         self.assertEquals(str(res[0]["cn"]), "ldaptestUSER3")
2251         self.assertEquals(str(res[0]["name"]), "ldaptestUSER3")
2252
2253         #"Testing ldb.search for (&(&(cn=ldaptestuser3)(userAccountControl=547))(objectClass=user))"
2254         res = ldb.search(expression="(&(&(cn=ldaptestuser3)(userAccountControl=547))(objectClass=user))")
2255         self.assertEquals(len(res), 0, "(&(&(cn=ldaptestuser3)(userAccountControl=547))(objectClass=user))")
2256
2257         # Testing ldb.search for (dn=CN=ldaptestUSER3,CN=Users," + self.base_dn + ") - should not work
2258         res = ldb.search(expression="(dn=CN=ldaptestUSER3,CN=Users," + self.base_dn + ")")
2259         self.assertEquals(len(res), 0, "Could find (dn=CN=ldaptestUSER3,CN=Users," + self.base_dn + ")")
2260
2261         # Testing ldb.search for (distinguishedName=CN=ldaptestUSER3,CN=Users," + self.base_dn + ")
2262         res = ldb.search(expression="(distinguishedName=CN=ldaptestUSER3,CN=Users," + self.base_dn + ")")
2263         self.assertEquals(len(res), 1, "Could not find (distinguishedName=CN=ldaptestUSER3,CN=Users," + self.base_dn + ")")
2264         self.assertEquals(str(res[0].dn), ("CN=ldaptestUSER3,CN=Users," + self.base_dn))
2265         self.assertEquals(str(res[0]["cn"]), "ldaptestUSER3")
2266         self.assertEquals(str(res[0]["name"]), "ldaptestUSER3")
2267
2268         # ensure we cannot add it again
2269         try:
2270             ldb.add({"dn": "cn=ldaptestuser3,cn=userS," + self.base_dn,
2271                      "objectClass": "user",
2272                      "cn": "LDAPtestUSER3"})
2273             self.fail()
2274         except LdbError as e98:
2275             (num, _) = e98.args
2276             self.assertEquals(num, ERR_ENTRY_ALREADY_EXISTS)
2277
2278         # rename back
2279         ldb.rename("cn=ldaptestuser3,cn=users," + self.base_dn, "cn=ldaptestuser2,cn=users," + self.base_dn)
2280
2281         # ensure we cannot rename it twice
2282         try:
2283             ldb.rename("cn=ldaptestuser3,cn=users," + self.base_dn,
2284                        "cn=ldaptestuser2,cn=users," + self.base_dn)
2285             self.fail()
2286         except LdbError as e99:
2287             (num, _) = e99.args
2288             self.assertEquals(num, ERR_NO_SUCH_OBJECT)
2289
2290         # ensure can now use that name
2291         ldb.add({"dn": "cn=ldaptestuser3,cn=users," + self.base_dn,
2292                  "objectClass": "user",
2293                  "cn": "LDAPtestUSER3"})
2294
2295         # ensure we now cannot rename
2296         try:
2297             ldb.rename("cn=ldaptestuser2,cn=users," + self.base_dn, "cn=ldaptestuser3,cn=users," + self.base_dn)
2298             self.fail()
2299         except LdbError as e100:
2300             (num, _) = e100.args
2301             self.assertEquals(num, ERR_ENTRY_ALREADY_EXISTS)
2302         try:
2303             ldb.rename("cn=ldaptestuser3,cn=users,%s" % self.base_dn, "cn=ldaptestuser3,%s" % ldb.get_config_basedn())
2304             self.fail()
2305         except LdbError as e101:
2306             (num, _) = e101.args
2307             self.assertTrue(num in (71, 64))
2308
2309         ldb.rename("cn=ldaptestuser3,cn=users," + self.base_dn, "cn=ldaptestuser5,cn=users," + self.base_dn)
2310
2311         ldb.delete("cn=ldaptestuser5,cn=users," + self.base_dn)
2312
2313         delete_force(ldb, "cn=ldaptestgroup2,cn=users," + self.base_dn)
2314
2315         ldb.rename("cn=ldaptestgroup,cn=users," + self.base_dn, "cn=ldaptestgroup2,cn=users," + self.base_dn)
2316
2317         # Testing subtree renames
2318
2319         ldb.add({"dn": "cn=ldaptestcontainer," + self.base_dn,
2320                  "objectClass": "container"})
2321
2322         ldb.add({"dn": "CN=ldaptestuser4,CN=ldaptestcontainer," + self.base_dn,
2323                  "objectClass": "user",
2324                  "cn": "LDAPtestUSER4"})
2325
2326         # Here we don't enforce these hard "description" constraints
2327         ldb.modify_ldif("""
2328 dn: cn=ldaptestcontainer,""" + self.base_dn + """
2329 changetype: modify
2330 replace: description
2331 description: desc1
2332 description: desc2
2333 """)
2334
2335         ldb.modify_ldif("""
2336 dn: cn=ldaptestgroup2,cn=users,""" + self.base_dn + """
2337 changetype: modify
2338 add: member
2339 member: cn=ldaptestuser4,cn=ldaptestcontainer,""" + self.base_dn + """
2340 member: cn=ldaptestcomputer,cn=computers,""" + self.base_dn + """
2341 member: cn=ldaptestuser2,cn=users,""" + self.base_dn + """
2342 """)
2343
2344         # Testing ldb.rename of cn=ldaptestcontainer," + self.base_dn + " to cn=ldaptestcontainer2," + self.base_dn
2345         ldb.rename("CN=ldaptestcontainer," + self.base_dn, "CN=ldaptestcontainer2," + self.base_dn)
2346
2347         # Testing ldb.search for (&(cn=ldaptestuser4)(objectClass=user))
2348         res = ldb.search(expression="(&(cn=ldaptestuser4)(objectClass=user))")
2349         self.assertEquals(len(res), 1, "Could not find (&(cn=ldaptestuser4)(objectClass=user))")
2350
2351         # Testing subtree ldb.search for (&(cn=ldaptestuser4)(objectClass=user)) in (just renamed from) cn=ldaptestcontainer," + self.base_dn
2352         try:
2353             res = ldb.search("cn=ldaptestcontainer," + self.base_dn,
2354                              expression="(&(cn=ldaptestuser4)(objectClass=user))",
2355                              scope=SCOPE_SUBTREE)
2356             self.fail(res)
2357         except LdbError as e102:
2358             (num, _) = e102.args
2359             self.assertEquals(num, ERR_NO_SUCH_OBJECT)
2360
2361         # Testing one-level ldb.search for (&(cn=ldaptestuser4)(objectClass=user)) in (just renamed from) cn=ldaptestcontainer," + self.base_dn
2362         try:
2363             res = ldb.search("cn=ldaptestcontainer," + self.base_dn,
2364                              expression="(&(cn=ldaptestuser4)(objectClass=user))", scope=SCOPE_ONELEVEL)
2365             self.fail()
2366         except LdbError as e103:
2367             (num, _) = e103.args
2368             self.assertEquals(num, ERR_NO_SUCH_OBJECT)
2369
2370         # Testing ldb.search for (&(cn=ldaptestuser4)(objectClass=user)) in renamed container"
2371         res = ldb.search("cn=ldaptestcontainer2," + self.base_dn, expression="(&(cn=ldaptestuser4)(objectClass=user))", scope=SCOPE_SUBTREE)
2372         self.assertEquals(len(res), 1, "Could not find (&(cn=ldaptestuser4)(objectClass=user)) under cn=ldaptestcontainer2," + self.base_dn)
2373
2374         self.assertEquals(str(res[0].dn), ("CN=ldaptestuser4,CN=ldaptestcontainer2," + self.base_dn))
2375         self.assertEquals(res[0]["memberOf"][0].upper(), ("CN=ldaptestgroup2,CN=Users," + self.base_dn).upper())
2376
2377         time.sleep(4)
2378
2379         # Testing ldb.search for (&(member=CN=ldaptestuser4,CN=ldaptestcontainer2," + self.base_dn + ")(objectclass=group)) to check subtree renames and linked attributes"
2380         res = ldb.search(self.base_dn, expression="(&(member=CN=ldaptestuser4,CN=ldaptestcontainer2," + self.base_dn + ")(objectclass=group))", scope=SCOPE_SUBTREE)
2381         self.assertEquals(len(res), 1, "Could not find (&(member=CN=ldaptestuser4,CN=ldaptestcontainer2," + self.base_dn + ")(objectclass=group)), perhaps linked attributes are not consistant with subtree renames?")
2382
2383         # Testing ldb.rename (into itself) of cn=ldaptestcontainer2," + self.base_dn + " to cn=ldaptestcontainer,cn=ldaptestcontainer2," + self.base_dn
2384         try:
2385             ldb.rename("cn=ldaptestcontainer2," + self.base_dn, "cn=ldaptestcontainer,cn=ldaptestcontainer2," + self.base_dn)
2386             self.fail()
2387         except LdbError as e104:
2388             (num, _) = e104.args
2389             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
2390
2391         # Testing ldb.rename (into non-existent container) of cn=ldaptestcontainer2," + self.base_dn + " to cn=ldaptestcontainer,cn=ldaptestcontainer3," + self.base_dn
2392         try:
2393             ldb.rename("cn=ldaptestcontainer2," + self.base_dn, "cn=ldaptestcontainer,cn=ldaptestcontainer3," + self.base_dn)
2394             self.fail()
2395         except LdbError as e105:
2396             (num, _) = e105.args
2397             self.assertTrue(num in (ERR_UNWILLING_TO_PERFORM, ERR_OTHER))
2398
2399         # Testing delete (should fail, not a leaf node) of renamed cn=ldaptestcontainer2," + self.base_dn
2400         try:
2401             ldb.delete("cn=ldaptestcontainer2," + self.base_dn)
2402             self.fail()
2403         except LdbError as e106:
2404             (num, _) = e106.args
2405             self.assertEquals(num, ERR_NOT_ALLOWED_ON_NON_LEAF)
2406
2407         # Testing base ldb.search for CN=ldaptestuser4,CN=ldaptestcontainer2," + self.base_dn
2408         res = ldb.search(expression="(objectclass=*)", base=("CN=ldaptestuser4,CN=ldaptestcontainer2," + self.base_dn), scope=SCOPE_BASE)
2409         self.assertEquals(len(res), 1)
2410         res = ldb.search(expression="(cn=ldaptestuser40)", base=("CN=ldaptestuser4,CN=ldaptestcontainer2," + self.base_dn), scope=SCOPE_BASE)
2411         self.assertEquals(len(res), 0)
2412
2413         # Testing one-level ldb.search for (&(cn=ldaptestuser4)(objectClass=user)) in cn=ldaptestcontainer2," + self.base_dn
2414         res = ldb.search(expression="(&(cn=ldaptestuser4)(objectClass=user))", base=("cn=ldaptestcontainer2," + self.base_dn), scope=SCOPE_ONELEVEL)
2415         self.assertEquals(len(res), 1)
2416
2417         # Testing one-level ldb.search for (&(cn=ldaptestuser4)(objectClass=user)) in cn=ldaptestcontainer2," + self.base_dn
2418         res = ldb.search(expression="(&(cn=ldaptestuser4)(objectClass=user))", base=("cn=ldaptestcontainer2," + self.base_dn), scope=SCOPE_SUBTREE)
2419         self.assertEquals(len(res), 1)
2420
2421         # Testing delete of subtree renamed "+("CN=ldaptestuser4,CN=ldaptestcontainer2," + self.base_dn)
2422         ldb.delete(("CN=ldaptestuser4,CN=ldaptestcontainer2," + self.base_dn))
2423         # Testing delete of renamed cn=ldaptestcontainer2," + self.base_dn
2424         ldb.delete("cn=ldaptestcontainer2," + self.base_dn)
2425
2426         ldb.add({"dn": "cn=ldaptestutf8user èùéìòà,cn=users," + self.base_dn, "objectClass": "user"})
2427
2428         ldb.add({"dn": "cn=ldaptestutf8user2  èùéìòà,cn=users," + self.base_dn, "objectClass": "user"})
2429
2430         # Testing ldb.search for (&(cn=ldaptestuser)(objectClass=user))"
2431         res = ldb.search(expression="(&(cn=ldaptestuser)(objectClass=user))")
2432         self.assertEquals(len(res), 1, "Could not find (&(cn=ldaptestuser)(objectClass=user))")
2433
2434         self.assertEquals(str(res[0].dn), ("CN=ldaptestuser,CN=Users," + self.base_dn))
2435         self.assertEquals(str(res[0]["cn"]), "ldaptestuser")
2436         self.assertEquals(str(res[0]["name"]), "ldaptestuser")
2437         self.assertEquals(set(res[0]["objectClass"]), set(["top", "person", "organizationalPerson", "user"]))
2438         self.assertTrue("objectGUID" in res[0])
2439         self.assertTrue("whenCreated" in res[0])
2440         self.assertEquals(str(res[0]["objectCategory"]), ("CN=Person,%s" % ldb.get_schema_basedn()))
2441         self.assertEquals(int(res[0]["sAMAccountType"][0]), ATYPE_NORMAL_ACCOUNT)
2442         self.assertEquals(int(res[0]["userAccountControl"][0]), UF_NORMAL_ACCOUNT | UF_PASSWD_NOTREQD | UF_ACCOUNTDISABLE)
2443         self.assertEquals(res[0]["memberOf"][0].upper(), ("CN=ldaptestgroup2,CN=Users," + self.base_dn).upper())
2444         self.assertEquals(len(res[0]["memberOf"]), 1)
2445
2446         # Testing ldb.search for (&(cn=ldaptestuser)(objectCategory=cn=person,%s))" % ldb.get_schema_basedn()
2447         res2 = ldb.search(expression="(&(cn=ldaptestuser)(objectCategory=cn=person,%s))" % ldb.get_schema_basedn())
2448         self.assertEquals(len(res2), 1, "Could not find (&(cn=ldaptestuser)(objectCategory=cn=person,%s))" % ldb.get_schema_basedn())
2449
2450         self.assertEquals(res[0].dn, res2[0].dn)
2451
2452         # Testing ldb.search for (&(cn=ldaptestuser)(objectCategory=PerSon))"
2453         res3 = ldb.search(expression="(&(cn=ldaptestuser)(objectCategory=PerSon))")
2454         self.assertEquals(len(res3), 1, "Could not find (&(cn=ldaptestuser)(objectCategory=PerSon)): matched %d" % len(res3))
2455
2456         self.assertEquals(res[0].dn, res3[0].dn)
2457
2458         if gc_ldb is not None:
2459             # Testing ldb.search for (&(cn=ldaptestuser)(objectCategory=PerSon)) in Global Catalog"
2460             res3gc = gc_ldb.search(expression="(&(cn=ldaptestuser)(objectCategory=PerSon))")
2461             self.assertEquals(len(res3gc), 1)
2462
2463             self.assertEquals(res[0].dn, res3gc[0].dn)
2464
2465         # Testing ldb.search for (&(cn=ldaptestuser)(objectCategory=PerSon)) in with 'phantom root' control"
2466
2467         if gc_ldb is not None:
2468             res3control = gc_ldb.search(self.base_dn, expression="(&(cn=ldaptestuser)(objectCategory=PerSon))", scope=SCOPE_SUBTREE, attrs=["cn"], controls=["search_options:1:2"])
2469             self.assertEquals(len(res3control), 1, "Could not find (&(cn=ldaptestuser)(objectCategory=PerSon)) in Global Catalog")
2470
2471             self.assertEquals(res[0].dn, res3control[0].dn)
2472
2473         ldb.delete(res[0].dn)
2474
2475         # Testing ldb.search for (&(cn=ldaptestcomputer)(objectClass=user))"
2476         res = ldb.search(expression="(&(cn=ldaptestcomputer)(objectClass=user))")
2477         self.assertEquals(len(res), 1, "Could not find (&(cn=ldaptestuser)(objectClass=user))")
2478
2479         self.assertEquals(str(res[0].dn), ("CN=ldaptestcomputer,CN=Computers," + self.base_dn))
2480         self.assertEquals(str(res[0]["cn"]), "ldaptestcomputer")
2481         self.assertEquals(str(res[0]["name"]), "ldaptestcomputer")
2482         self.assertEquals(set(res[0]["objectClass"]), set(["top", "person", "organizationalPerson", "user", "computer"]))
2483         self.assertTrue("objectGUID" in res[0])
2484         self.assertTrue("whenCreated" in res[0])
2485         self.assertEquals(str(res[0]["objectCategory"]), ("CN=Computer,%s" % ldb.get_schema_basedn()))
2486         self.assertEquals(int(res[0]["primaryGroupID"][0]), 513)
2487         self.assertEquals(int(res[0]["sAMAccountType"][0]), ATYPE_NORMAL_ACCOUNT)
2488         self.assertEquals(int(res[0]["userAccountControl"][0]), UF_NORMAL_ACCOUNT | UF_PASSWD_NOTREQD | UF_ACCOUNTDISABLE)
2489         self.assertEquals(res[0]["memberOf"][0].upper(), ("CN=ldaptestgroup2,CN=Users," + self.base_dn).upper())
2490         self.assertEquals(len(res[0]["memberOf"]), 1)
2491
2492         # Testing ldb.search for (&(cn=ldaptestcomputer)(objectCategory=cn=computer,%s))" % ldb.get_schema_basedn()
2493         res2 = ldb.search(expression="(&(cn=ldaptestcomputer)(objectCategory=cn=computer,%s))" % ldb.get_schema_basedn())
2494         self.assertEquals(len(res2), 1, "Could not find (&(cn=ldaptestcomputer)(objectCategory=cn=computer,%s))" % ldb.get_schema_basedn())
2495
2496         self.assertEquals(res[0].dn, res2[0].dn)
2497
2498         if gc_ldb is not None:
2499             # Testing ldb.search for (&(cn=ldaptestcomputer)(objectCategory=cn=computer,%s)) in Global Catalog" % gc_ldb.get_schema_basedn()
2500             res2gc = gc_ldb.search(expression="(&(cn=ldaptestcomputer)(objectCategory=cn=computer,%s))" % gc_ldb.get_schema_basedn())
2501             self.assertEquals(len(res2gc), 1, "Could not find (&(cn=ldaptestcomputer)(objectCategory=cn=computer,%s)) In Global Catalog" % gc_ldb.get_schema_basedn())
2502
2503             self.assertEquals(res[0].dn, res2gc[0].dn)
2504
2505         # Testing ldb.search for (&(cn=ldaptestcomputer)(objectCategory=compuTER))"
2506         res3 = ldb.search(expression="(&(cn=ldaptestcomputer)(objectCategory=compuTER))")
2507         self.assertEquals(len(res3), 1, "Could not find (&(cn=ldaptestcomputer)(objectCategory=compuTER))")
2508
2509         self.assertEquals(res[0].dn, res3[0].dn)
2510
2511         if gc_ldb is not None:
2512             # Testing ldb.search for (&(cn=ldaptestcomputer)(objectCategory=compuTER)) in Global Catalog"
2513             res3gc = gc_ldb.search(expression="(&(cn=ldaptestcomputer)(objectCategory=compuTER))")
2514             self.assertEquals(len(res3gc), 1, "Could not find (&(cn=ldaptestcomputer)(objectCategory=compuTER)) in Global Catalog")
2515
2516             self.assertEquals(res[0].dn, res3gc[0].dn)
2517
2518         # Testing ldb.search for (&(cn=ldaptestcomp*r)(objectCategory=compuTER))"
2519         res4 = ldb.search(expression="(&(cn=ldaptestcomp*r)(objectCategory=compuTER))")
2520         self.assertEquals(len(res4), 1, "Could not find (&(cn=ldaptestcomp*r)(objectCategory=compuTER))")
2521
2522         self.assertEquals(res[0].dn, res4[0].dn)
2523
2524         # Testing ldb.search for (&(cn=ldaptestcomput*)(objectCategory=compuTER))"
2525         res5 = ldb.search(expression="(&(cn=ldaptestcomput*)(objectCategory=compuTER))")
2526         self.assertEquals(len(res5), 1, "Could not find (&(cn=ldaptestcomput*)(objectCategory=compuTER))")
2527
2528         self.assertEquals(res[0].dn, res5[0].dn)
2529
2530         # Testing ldb.search for (&(cn=*daptestcomputer)(objectCategory=compuTER))"
2531         res6 = ldb.search(expression="(&(cn=*daptestcomputer)(objectCategory=compuTER))")
2532         self.assertEquals(len(res6), 1, "Could not find (&(cn=*daptestcomputer)(objectCategory=compuTER))")
2533
2534         self.assertEquals(res[0].dn, res6[0].dn)
2535
2536         ldb.delete("<GUID=" + ldb.schema_format_value("objectGUID", res[0]["objectGUID"][0]) + ">")
2537
2538         # Testing ldb.search for (&(cn=ldaptest2computer)(objectClass=user))"
2539         res = ldb.search(expression="(&(cn=ldaptest2computer)(objectClass=user))")
2540         self.assertEquals(len(res), 1, "Could not find (&(cn=ldaptest2computer)(objectClass=user))")
2541
2542         self.assertEquals(str(res[0].dn), "CN=ldaptest2computer,CN=Computers," + self.base_dn)
2543         self.assertEquals(str(res[0]["cn"]), "ldaptest2computer")
2544         self.assertEquals(str(res[0]["name"]), "ldaptest2computer")
2545         self.assertEquals(list(res[0]["objectClass"]), ["top", "person", "organizationalPerson", "user", "computer"])
2546         self.assertTrue("objectGUID" in res[0])
2547         self.assertTrue("whenCreated" in res[0])
2548         self.assertEquals(res[0]["objectCategory"][0], "CN=Computer,%s" % ldb.get_schema_basedn())
2549         self.assertEquals(int(res[0]["sAMAccountType"][0]), ATYPE_WORKSTATION_TRUST)
2550         self.assertEquals(int(res[0]["userAccountControl"][0]), UF_WORKSTATION_TRUST_ACCOUNT)
2551
2552         ldb.delete("<SID=" + ldb.schema_format_value("objectSID", res[0]["objectSID"][0]) + ">")
2553
2554         attrs = ["cn", "name", "objectClass", "objectGUID", "objectSID", "whenCreated", "nTSecurityDescriptor", "memberOf", "allowedAttributes", "allowedAttributesEffective"]
2555         # Testing ldb.search for (&(cn=ldaptestUSer2)(objectClass=user))"
2556         res_user = ldb.search(self.base_dn, expression="(&(cn=ldaptestUSer2)(objectClass=user))", scope=SCOPE_SUBTREE, attrs=attrs)
2557         self.assertEquals(len(res_user), 1, "Could not find (&(cn=ldaptestUSer2)(objectClass=user))")
2558
2559         self.assertEquals(str(res_user[0].dn), ("CN=ldaptestuser2,CN=Users," + self.base_dn))
2560         self.assertEquals(str(res_user[0]["cn"]), "ldaptestuser2")
2561         self.assertEquals(str(res_user[0]["name"]), "ldaptestuser2")
2562         self.assertEquals(list(res_user[0]["objectClass"]), ["top", "person", "organizationalPerson", "user"])
2563         self.assertTrue("objectSid" in res_user[0])
2564         self.assertTrue("objectGUID" in res_user[0])
2565         self.assertTrue("whenCreated" in res_user[0])
2566         self.assertTrue("nTSecurityDescriptor" in res_user[0])
2567         self.assertTrue("allowedAttributes" in res_user[0])
2568         self.assertTrue("allowedAttributesEffective" in res_user[0])
2569         self.assertEquals(res_user[0]["memberOf"][0].upper(), ("CN=ldaptestgroup2,CN=Users," + self.base_dn).upper())
2570
2571         ldaptestuser2_sid = res_user[0]["objectSid"][0]
2572         ldaptestuser2_guid = res_user[0]["objectGUID"][0]
2573
2574         attrs = ["cn", "name", "objectClass", "objectGUID", "objectSID", "whenCreated", "nTSecurityDescriptor", "member", "allowedAttributes", "allowedAttributesEffective"]
2575         # Testing ldb.search for (&(cn=ldaptestgroup2)(objectClass=group))"
2576         res = ldb.search(self.base_dn, expression="(&(cn=ldaptestgroup2)(objectClass=group))", scope=SCOPE_SUBTREE, attrs=attrs)
2577         self.assertEquals(len(res), 1, "Could not find (&(cn=ldaptestgroup2)(objectClass=group))")
2578
2579         self.assertEquals(str(res[0].dn), ("CN=ldaptestgroup2,CN=Users," + self.base_dn))
2580         self.assertEquals(str(res[0]["cn"]), "ldaptestgroup2")
2581         self.assertEquals(str(res[0]["name"]), "ldaptestgroup2")
2582         self.assertEquals(list(res[0]["objectClass"]), ["top", "group"])
2583         self.assertTrue("objectGUID" in res[0])
2584         self.assertTrue("objectSid" in res[0])
2585         self.assertTrue("whenCreated" in res[0])
2586         self.assertTrue("nTSecurityDescriptor" in res[0])
2587         self.assertTrue("allowedAttributes" in res[0])
2588         self.assertTrue("allowedAttributesEffective" in res[0])
2589         memberUP = []
2590         for m in res[0]["member"]:
2591             memberUP.append(m.upper())
2592         self.assertTrue(("CN=ldaptestuser2,CN=Users," + self.base_dn).upper() in memberUP)
2593
2594         res = ldb.search(self.base_dn, expression="(&(cn=ldaptestgroup2)(objectClass=group))", scope=SCOPE_SUBTREE, attrs=attrs, controls=["extended_dn:1:1"])
2595         self.assertEquals(len(res), 1, "Could not find (&(cn=ldaptestgroup2)(objectClass=group))")
2596
2597         print(res[0]["member"])
2598         memberUP = []
2599         for m in res[0]["member"]:
2600             memberUP.append(m.upper())
2601         print(("<GUID=" + ldb.schema_format_value("objectGUID", ldaptestuser2_guid) + ">;<SID=" + ldb.schema_format_value("objectSid", ldaptestuser2_sid) + ">;CN=ldaptestuser2,CN=Users," + self.base_dn).upper())
2602
2603         self.assertTrue(("<GUID=" + ldb.schema_format_value("objectGUID", ldaptestuser2_guid) + ">;<SID=" + ldb.schema_format_value("objectSid", ldaptestuser2_sid) + ">;CN=ldaptestuser2,CN=Users," + self.base_dn).upper() in memberUP)
2604
2605         # Quicktest for linked attributes"
2606         ldb.modify_ldif("""
2607 dn: cn=ldaptestgroup2,cn=users,""" + self.base_dn + """
2608 changetype: modify
2609 replace: member
2610 member: CN=ldaptestuser2,CN=Users,""" + self.base_dn + """
2611 member: CN=ldaptestutf8user èùéìòà,CN=Users,""" + self.base_dn + """
2612 """)
2613
2614         ldb.modify_ldif("""
2615 dn: <GUID=""" + ldb.schema_format_value("objectGUID", res[0]["objectGUID"][0]) + """>
2616 changetype: modify
2617 replace: member
2618 member: CN=ldaptestutf8user èùéìòà,CN=Users,""" + self.base_dn + """
2619 """)
2620
2621         ldb.modify_ldif("""
2622 dn: <SID=""" + ldb.schema_format_value("objectSid", res[0]["objectSid"][0]) + """>
2623 changetype: modify
2624 delete: member
2625 """)
2626
2627         ldb.modify_ldif("""
2628 dn: cn=ldaptestgroup2,cn=users,""" + self.base_dn + """
2629 changetype: modify
2630 add: member
2631 member: <GUID=""" + ldb.schema_format_value("objectGUID", res[0]["objectGUID"][0]) + """>
2632 member: CN=ldaptestutf8user èùéìòà,CN=Users,""" + self.base_dn + """
2633 """)
2634
2635         ldb.modify_ldif("""
2636 dn: cn=ldaptestgroup2,cn=users,""" + self.base_dn + """
2637 changetype: modify
2638 replace: member
2639 """)
2640
2641         ldb.modify_ldif("""
2642 dn: cn=ldaptestgroup2,cn=users,""" + self.base_dn + """
2643 changetype: modify
2644 add: member
2645 member: <SID=""" + ldb.schema_format_value("objectSid", res_user[0]["objectSid"][0]) + """>
2646 member: CN=ldaptestutf8user èùéìòà,CN=Users,""" + self.base_dn + """
2647 """)
2648
2649         ldb.modify_ldif("""
2650 dn: cn=ldaptestgroup2,cn=users,""" + self.base_dn + """
2651 changetype: modify
2652 delete: member
2653 member: CN=ldaptestutf8user èùéìòà,CN=Users,""" + self.base_dn + """
2654 """)
2655
2656         res = ldb.search(self.base_dn, expression="(&(cn=ldaptestgroup2)(objectClass=group))", scope=SCOPE_SUBTREE, attrs=attrs)
2657         self.assertEquals(len(res), 1, "Could not find (&(cn=ldaptestgroup2)(objectClass=group))")
2658
2659         self.assertEquals(str(res[0].dn), ("CN=ldaptestgroup2,CN=Users," + self.base_dn))
2660         self.assertEquals(res[0]["member"][0], ("CN=ldaptestuser2,CN=Users," + self.base_dn))
2661         self.assertEquals(len(res[0]["member"]), 1)
2662
2663         ldb.delete(("CN=ldaptestuser2,CN=Users," + self.base_dn))
2664
2665         time.sleep(4)
2666
2667         attrs = ["cn", "name", "objectClass", "objectGUID", "whenCreated", "nTSecurityDescriptor", "member"]
2668         # Testing ldb.search for (&(cn=ldaptestgroup2)(objectClass=group)) to check linked delete"
2669         res = ldb.search(self.base_dn, expression="(&(cn=ldaptestgroup2)(objectClass=group))", scope=SCOPE_SUBTREE, attrs=attrs)
2670         self.assertEquals(len(res), 1, "Could not find (&(cn=ldaptestgroup2)(objectClass=group)) to check linked delete")
2671
2672         self.assertEquals(str(res[0].dn), ("CN=ldaptestgroup2,CN=Users," + self.base_dn))
2673         self.assertTrue("member" not in res[0])
2674
2675         # Testing ldb.search for (&(cn=ldaptestutf8user ÈÙÉÌÒÀ)(objectClass=user))"
2676         res = ldb.search(expression="(&(cn=ldaptestutf8user ÈÙÉÌÒÀ)(objectClass=user))")
2677         self.assertEquals(len(res), 1, "Could not find (&(cn=ldaptestutf8user ÈÙÉÌÒÀ)(objectClass=user))")
2678         res = ldb.search(expression="(&(cn=ldaptestutf8user èùéìòà)(objectclass=user))")
2679         self.assertEquals(len(res), 1, "Could not find (&(cn=ldaptestutf8user ÈÙÉÌÒÀ)(objectClass=user))")
2680
2681         self.assertEquals(str(res[0].dn), ("CN=ldaptestutf8user èùéìòà,CN=Users," + self.base_dn))
2682         self.assertEquals(str(res[0]["cn"]), "ldaptestutf8user èùéìòà")
2683         self.assertEquals(str(res[0]["name"]), "ldaptestutf8user èùéìòà")
2684         self.assertEquals(list(res[0]["objectClass"]), ["top", "person", "organizationalPerson", "user"])
2685         self.assertTrue("objectGUID" in res[0])
2686         self.assertTrue("whenCreated" in res[0])
2687
2688         # delete "ldaptestutf8user"
2689         ldb.delete(res[0].dn)
2690
2691         # Testing ldb.search for (&(cn=ldaptestutf8user2*)(objectClass=user))"
2692         res = ldb.search(expression="(&(cn=ldaptestutf8user2*)(objectClass=user))")
2693         self.assertEquals(len(res), 1, "Could not find (&(cn=ldaptestutf8user2*)(objectClass=user))")
2694
2695         # Testing ldb.search for (&(cn=ldaptestutf8user2  ÈÙÉÌÒÀ)(objectClass=user))"
2696         res = ldb.search(expression="(&(cn=ldaptestutf8user2  ÈÙÉÌÒÀ)(objectClass=user))")
2697         self.assertEquals(len(res), 1, "Could not find (&(cn=ldaptestutf8user2  ÈÙÉÌÒÀ)(objectClass=user))")
2698
2699         # delete "ldaptestutf8user2 "
2700         ldb.delete(res[0].dn)
2701
2702         ldb.delete(("CN=ldaptestgroup2,CN=Users," + self.base_dn))
2703
2704         # Testing that we can't get at the configuration DN from the main search base"
2705         res = ldb.search(self.base_dn, expression="objectClass=crossRef", scope=SCOPE_SUBTREE, attrs=["cn"])
2706         self.assertEquals(len(res), 0)
2707
2708         # Testing that we can get at the configuration DN from the main search base on the LDAP port with the 'phantom root' search_options control"
2709         res = ldb.search(self.base_dn, expression="objectClass=crossRef", scope=SCOPE_SUBTREE, attrs=["cn"], controls=["search_options:1:2"])
2710         self.assertTrue(len(res) > 0)
2711
2712         if gc_ldb is not None:
2713             # Testing that we can get at the configuration DN from the main search base on the GC port with the search_options control == 0"
2714
2715             res = gc_ldb.search(self.base_dn, expression="objectClass=crossRef", scope=SCOPE_SUBTREE, attrs=["cn"], controls=["search_options:1:0"])
2716             self.assertTrue(len(res) > 0)
2717
2718             # Testing that we do find configuration elements in the global catlog"
2719             res = gc_ldb.search(self.base_dn, expression="objectClass=crossRef", scope=SCOPE_SUBTREE, attrs=["cn"])
2720             self.assertTrue(len(res) > 0)
2721
2722             # Testing that we do find configuration elements and user elements at the same time"
2723             res = gc_ldb.search(self.base_dn, expression="(|(objectClass=crossRef)(objectClass=person))", scope=SCOPE_SUBTREE, attrs=["cn"])
2724             self.assertTrue(len(res) > 0)
2725
2726             # Testing that we do find configuration elements in the global catlog, with the configuration basedn"
2727             res = gc_ldb.search(self.configuration_dn, expression="objectClass=crossRef", scope=SCOPE_SUBTREE, attrs=["cn"])
2728             self.assertTrue(len(res) > 0)
2729
2730         # Testing that we can get at the configuration DN on the main LDAP port"
2731         res = ldb.search(self.configuration_dn, expression="objectClass=crossRef", scope=SCOPE_SUBTREE, attrs=["cn"])
2732         self.assertTrue(len(res) > 0)
2733
2734         # Testing objectCategory canonacolisation"
2735         res = ldb.search(self.configuration_dn, expression="objectCategory=ntDsDSA", scope=SCOPE_SUBTREE, attrs=["cn"])
2736         self.assertTrue(len(res) > 0, "Didn't find any records with objectCategory=ntDsDSA")
2737         self.assertTrue(len(res) != 0)
2738
2739         res = ldb.search(self.configuration_dn, expression="objectCategory=CN=ntDs-DSA," + self.schema_dn, scope=SCOPE_SUBTREE, attrs=["cn"])
2740         self.assertTrue(len(res) > 0, "Didn't find any records with objectCategory=CN=ntDs-DSA," + self.schema_dn)
2741         self.assertTrue(len(res) != 0)
2742
2743         # Testing objectClass attribute order on "+ self.base_dn
2744         res = ldb.search(expression="objectClass=domain", base=self.base_dn,
2745                          scope=SCOPE_BASE, attrs=["objectClass"])
2746         self.assertEquals(len(res), 1)
2747
2748         self.assertEquals(list(res[0]["objectClass"]), ["top", "domain", "domainDNS"])
2749
2750     #  check enumeration
2751
2752         # Testing ldb.search for objectCategory=person"
2753         res = ldb.search(self.base_dn, expression="objectCategory=person", scope=SCOPE_SUBTREE, attrs=["cn"])
2754         self.assertTrue(len(res) > 0)
2755
2756         # Testing ldb.search for objectCategory=person with domain scope control"
2757         res = ldb.search(self.base_dn, expression="objectCategory=person", scope=SCOPE_SUBTREE, attrs=["cn"], controls=["domain_scope:1"])
2758         self.assertTrue(len(res) > 0)
2759
2760         # Testing ldb.search for objectCategory=user"
2761         res = ldb.search(self.base_dn, expression="objectCategory=user", scope=SCOPE_SUBTREE, attrs=["cn"])
2762         self.assertTrue(len(res) > 0)
2763
2764         # Testing ldb.search for objectCategory=user with domain scope control"
2765         res = ldb.search(self.base_dn, expression="objectCategory=user", scope=SCOPE_SUBTREE, attrs=["cn"], controls=["domain_scope:1"])
2766         self.assertTrue(len(res) > 0)
2767
2768         # Testing ldb.search for objectCategory=group"
2769         res = ldb.search(self.base_dn, expression="objectCategory=group", scope=SCOPE_SUBTREE, attrs=["cn"])
2770         self.assertTrue(len(res) > 0)
2771
2772         # Testing ldb.search for objectCategory=group with domain scope control"
2773         res = ldb.search(self.base_dn, expression="objectCategory=group", scope=SCOPE_SUBTREE, attrs=["cn"], controls=["domain_scope:1"])
2774         self.assertTrue(len(res) > 0)
2775
2776         # Testing creating a user with the posixAccount objectClass"
2777         self.ldb.add_ldif("""dn: cn=posixuser,CN=Users,%s
2778 objectClass: top
2779 objectClass: person
2780 objectClass: posixAccount
2781 objectClass: user
2782 objectClass: organizationalPerson
2783 cn: posixuser
2784 uid: posixuser
2785 sn: posixuser
2786 uidNumber: 10126
2787 gidNumber: 10126
2788 homeDirectory: /home/posixuser
2789 loginShell: /bin/bash
2790 gecos: Posix User;;;
2791 description: A POSIX user"""% (self.base_dn))
2792
2793         # Testing removing the posixAccount objectClass from an existing user"
2794         self.ldb.modify_ldif("""dn: cn=posixuser,CN=Users,%s
2795 changetype: modify
2796 delete: objectClass
2797 objectClass: posixAccount"""% (self.base_dn))
2798
2799         # Testing adding the posixAccount objectClass to an existing user"
2800         self.ldb.modify_ldif("""dn: cn=posixuser,CN=Users,%s
2801 changetype: modify
2802 add: objectClass
2803 objectClass: posixAccount"""% (self.base_dn))
2804
2805         delete_force(self.ldb, "cn=posixuser,cn=users," + self.base_dn)
2806         delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
2807         delete_force(self.ldb, "cn=ldaptestuser2,cn=users," + self.base_dn)
2808         delete_force(self.ldb, "cn=ldaptestuser3,cn=users," + self.base_dn)
2809         delete_force(self.ldb, "cn=ldaptestuser4,cn=ldaptestcontainer," + self.base_dn)
2810         delete_force(self.ldb, "cn=ldaptestuser4,cn=ldaptestcontainer2," + self.base_dn)
2811         delete_force(self.ldb, "cn=ldaptestuser5,cn=users," + self.base_dn)
2812         delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
2813         delete_force(self.ldb, "cn=ldaptestgroup2,cn=users," + self.base_dn)
2814         delete_force(self.ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn)
2815         delete_force(self.ldb, "cn=ldaptest2computer,cn=computers," + self.base_dn)
2816         delete_force(self.ldb, "cn=ldaptestcomputer3,cn=computers," + self.base_dn)
2817         delete_force(self.ldb, "cn=ldaptestutf8user èùéìòà,cn=users," + self.base_dn)
2818         delete_force(self.ldb, "cn=ldaptestutf8user2  èùéìòà,cn=users," + self.base_dn)
2819         delete_force(self.ldb, "cn=ldaptestcontainer," + self.base_dn)
2820         delete_force(self.ldb, "cn=ldaptestcontainer2," + self.base_dn)
2821
2822     def test_security_descriptor_add(self):
2823         """ Testing ldb.add_ldif() for nTSecurityDescriptor """
2824         user_name = "testdescriptoruser1"
2825         user_dn = "CN=%s,CN=Users,%s" % (user_name, self.base_dn)
2826         #
2827         # Test an empty security descriptor (naturally this shouldn't work)
2828         #
2829         delete_force(self.ldb, user_dn)
2830         try:
2831             self.ldb.add({"dn": user_dn,
2832                            "objectClass": "user",
2833                            "sAMAccountName": user_name,
2834                            "nTSecurityDescriptor": []})
2835             self.fail()
2836         except LdbError as e107:
2837             (num, _) = e107.args
2838             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
2839         finally:
2840             delete_force(self.ldb, user_dn)
2841         #
2842         # Test add_ldif() with SDDL security descriptor input
2843         #
2844         try:
2845             sddl = "O:DUG:DUD:PAI(A;;RPWP;;;AU)S:PAI"
2846             self.ldb.add_ldif("""
2847 dn: """ + user_dn + """
2848 objectclass: user
2849 sAMAccountName: """ + user_name + """
2850 nTSecurityDescriptor: """ + sddl)
2851             res = self.ldb.search(base=user_dn, attrs=["nTSecurityDescriptor"])
2852             desc = res[0]["nTSecurityDescriptor"][0]
2853             desc = ndr_unpack(security.descriptor, desc)
2854             desc_sddl = desc.as_sddl(self.domain_sid)
2855             self.assertEqual(desc_sddl, sddl)
2856         finally:
2857             delete_force(self.ldb, user_dn)
2858         #
2859         # Test add_ldif() with BASE64 security descriptor
2860         #
2861         try:
2862             sddl = "O:DUG:DUD:PAI(A;;RPWP;;;AU)S:PAI"
2863             desc = security.descriptor.from_sddl(sddl, self.domain_sid)
2864             desc_binary = ndr_pack(desc)
2865             desc_base64 = base64.b64encode(desc_binary).decode('utf8')
2866             self.ldb.add_ldif("""
2867 dn: """ + user_dn + """
2868 objectclass: user
2869 sAMAccountName: """ + user_name + """
2870 nTSecurityDescriptor:: """ + desc_base64)
2871             res = self.ldb.search(base=user_dn, attrs=["nTSecurityDescriptor"])
2872             desc = res[0]["nTSecurityDescriptor"][0]
2873             desc = ndr_unpack(security.descriptor, desc)
2874             desc_sddl = desc.as_sddl(self.domain_sid)
2875             self.assertEqual(desc_sddl, sddl)
2876         finally:
2877             delete_force(self.ldb, user_dn)
2878
2879     def test_security_descriptor_add_neg(self):
2880         """Test add_ldif() with BASE64 security descriptor input using WRONG domain SID
2881             Negative test
2882         """
2883         user_name = "testdescriptoruser1"
2884         user_dn = "CN=%s,CN=Users,%s" % (user_name, self.base_dn)
2885         delete_force(self.ldb, user_dn)
2886         try:
2887             sddl = "O:DUG:DUD:AI(A;;RPWP;;;AU)S:PAI"
2888             desc = security.descriptor.from_sddl(sddl, security.dom_sid('S-1-5-21'))
2889             desc_base64 = base64.b64encode(ndr_pack(desc)).decode('utf8')
2890             self.ldb.add_ldif("""
2891 dn: """ + user_dn + """
2892 objectclass: user
2893 sAMAccountName: """ + user_name + """
2894 nTSecurityDescriptor:: """ + desc_base64)
2895             res = self.ldb.search(base=user_dn, attrs=["nTSecurityDescriptor"])
2896             self.assertTrue("nTSecurityDescriptor" in res[0])
2897             desc = res[0]["nTSecurityDescriptor"][0]
2898             desc = ndr_unpack(security.descriptor, desc)
2899             desc_sddl = desc.as_sddl(self.domain_sid)
2900             self.assertTrue("O:S-1-5-21-513G:S-1-5-21-513D:AI(A;;RPWP;;;AU)" in desc_sddl)
2901         finally:
2902             delete_force(self.ldb, user_dn)
2903
2904     def test_security_descriptor_modify(self):
2905         """ Testing ldb.modify_ldif() for nTSecurityDescriptor """
2906         user_name = "testdescriptoruser2"
2907         user_dn = "CN=%s,CN=Users,%s" % (user_name, self.base_dn)
2908         #
2909         # Test an empty security descriptor (naturally this shouldn't work)
2910         #
2911         delete_force(self.ldb, user_dn)
2912         self.ldb.add({"dn": user_dn,
2913                        "objectClass": "user",
2914                        "sAMAccountName": user_name})
2915
2916         m = Message()
2917         m.dn = Dn(ldb, user_dn)
2918         m["nTSecurityDescriptor"] = MessageElement([], FLAG_MOD_ADD,
2919                                                    "nTSecurityDescriptor")
2920         try:
2921             self.ldb.modify(m)
2922             self.fail()
2923         except LdbError as e108:
2924             (num, _) = e108.args
2925             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
2926
2927         m = Message()
2928         m.dn = Dn(ldb, user_dn)
2929         m["nTSecurityDescriptor"] = MessageElement([], FLAG_MOD_REPLACE,
2930                                                    "nTSecurityDescriptor")
2931         try:
2932             self.ldb.modify(m)
2933             self.fail()
2934         except LdbError as e109:
2935             (num, _) = e109.args
2936             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
2937
2938         m = Message()
2939         m.dn = Dn(ldb, user_dn)
2940         m["nTSecurityDescriptor"] = MessageElement([], FLAG_MOD_DELETE,
2941                                                    "nTSecurityDescriptor")
2942         try:
2943             self.ldb.modify(m)
2944             self.fail()
2945         except LdbError as e110:
2946             (num, _) = e110.args
2947             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
2948
2949         delete_force(self.ldb, user_dn)
2950         #
2951         # Test modify_ldif() with SDDL security descriptor input
2952         # Add ACE to the original descriptor test
2953         #
2954         try:
2955             self.ldb.add_ldif("""
2956 dn: """ + user_dn + """
2957 objectclass: user
2958 sAMAccountName: """ + user_name)
2959             # Modify descriptor
2960             res = self.ldb.search(base=user_dn, attrs=["nTSecurityDescriptor"])
2961             desc = res[0]["nTSecurityDescriptor"][0]
2962             desc = ndr_unpack(security.descriptor, desc)
2963             desc_sddl = desc.as_sddl(self.domain_sid)
2964             sddl = desc_sddl[:desc_sddl.find("(")] + "(A;;RPWP;;;AU)" + desc_sddl[desc_sddl.find("("):]
2965             mod = """
2966 dn: """ + user_dn + """
2967 changetype: modify
2968 replace: nTSecurityDescriptor
2969 nTSecurityDescriptor: """ + sddl
2970             self.ldb.modify_ldif(mod)
2971             # Read modified descriptor
2972             res = self.ldb.search(base=user_dn, attrs=["nTSecurityDescriptor"])
2973             desc = res[0]["nTSecurityDescriptor"][0]
2974             desc = ndr_unpack(security.descriptor, desc)
2975             desc_sddl&nbs