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