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