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