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