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