dsdb python tests: convert 'except X, (tuple)' to 'except X as e'
[metze/samba/wip.git] / source4 / dsdb / tests / python / ldap_schema.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 # This is a port of the original in testprogs/ejs/ldap.js
4
5 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2008-2011
6 # Copyright (C) Catalyst.Net Ltd 2017
7 #
8 # This program is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 3 of the License, or
11 # (at your option) any later version.
12 #
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the GNU General Public License
19 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
21
22
23 import optparse
24 import sys
25 import time
26 import random
27 import os
28
29 sys.path.insert(0, "bin/python")
30 import samba
31 from samba.tests.subunitrun import TestProgram, SubunitOptions
32
33 import samba.getopt as options
34
35 from samba.auth import system_session
36 from ldb import SCOPE_ONELEVEL, SCOPE_BASE, LdbError
37 from ldb import ERR_NO_SUCH_OBJECT
38 from ldb import ERR_UNWILLING_TO_PERFORM
39 from ldb import ERR_ENTRY_ALREADY_EXISTS
40 from ldb import ERR_CONSTRAINT_VIOLATION
41 from ldb import ERR_OBJECT_CLASS_VIOLATION
42 from ldb import Message, MessageElement, Dn
43 from ldb import FLAG_MOD_REPLACE
44 from samba.samdb import SamDB
45 from samba.dsdb import DS_DOMAIN_FUNCTION_2003
46 from samba.tests import delete_force
47 from samba.ndr import ndr_unpack
48 from samba.dcerpc import drsblobs
49
50 parser = optparse.OptionParser("ldap_schema.py [options] <host>")
51 sambaopts = options.SambaOptions(parser)
52 parser.add_option_group(sambaopts)
53 parser.add_option_group(options.VersionOptions(parser))
54 # use command line creds if available
55 credopts = options.CredentialsOptions(parser)
56 parser.add_option_group(credopts)
57 subunitopts = SubunitOptions(parser)
58 parser.add_option_group(subunitopts)
59 opts, args = parser.parse_args()
60
61 if len(args) < 1:
62     parser.print_usage()
63     sys.exit(1)
64
65 host = args[0]
66
67 lp = sambaopts.get_loadparm()
68 creds = credopts.get_credentials(lp)
69
70
71 class SchemaTests(samba.tests.TestCase):
72
73     def setUp(self):
74         super(SchemaTests, self).setUp()
75         self.ldb = SamDB(host, credentials=creds,
76             session_info=system_session(lp), lp=lp, options=ldb_options)
77         self.base_dn = self.ldb.domain_dn()
78         self.schema_dn = self.ldb.get_schema_basedn().get_linearized()
79
80     def test_generated_schema(self):
81         """Testing we can read the generated schema via LDAP"""
82         res = self.ldb.search("cn=aggregate,"+self.schema_dn, scope=SCOPE_BASE,
83                 attrs=["objectClasses", "attributeTypes", "dITContentRules"])
84         self.assertEquals(len(res), 1)
85         self.assertTrue("dITContentRules" in res[0])
86         self.assertTrue("objectClasses" in res[0])
87         self.assertTrue("attributeTypes" in res[0])
88
89     def test_generated_schema_is_operational(self):
90         """Testing we don't get the generated schema via LDAP by default"""
91         # Must keep the "*" form
92         res = self.ldb.search("cn=aggregate,"+self.schema_dn, scope=SCOPE_BASE,
93                               attrs=["*"])
94         self.assertEquals(len(res), 1)
95         self.assertFalse("dITContentRules" in res[0])
96         self.assertFalse("objectClasses" in res[0])
97         self.assertFalse("attributeTypes" in res[0])
98
99     def test_schemaUpdateNow(self):
100         """Testing schemaUpdateNow"""
101         rand = str(random.randint(1,100000))
102         attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + "-" + rand
103         attr_ldap_display_name = attr_name.replace("-", "")
104
105         ldif = """
106 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
107 objectClass: top
108 objectClass: attributeSchema
109 adminDescription: """ + attr_name + """
110 adminDisplayName: """ + attr_name + """
111 cn: """ + attr_name + """
112 attributeId: 1.3.6.1.4.1.7165.4.6.1.6.1.""" + rand + """
113 attributeSyntax: 2.5.5.12
114 omSyntax: 64
115 instanceType: 4
116 isSingleValued: TRUE
117 systemOnly: FALSE
118 """
119         self.ldb.add_ldif(ldif)
120         # We must do a schemaUpdateNow otherwise it's not 100% sure that the schema
121         # will contain the new attribute
122         ldif = """
123 dn:
124 changetype: modify
125 add: schemaUpdateNow
126 schemaUpdateNow: 1
127 """
128         self.ldb.modify_ldif(ldif)
129
130         # Search for created attribute
131         res = []
132         res = self.ldb.search("cn=%s,%s" % (attr_name, self.schema_dn), scope=SCOPE_BASE,
133                               attrs=["lDAPDisplayName","schemaIDGUID", "msDS-IntID"])
134         self.assertEquals(len(res), 1)
135         self.assertEquals(res[0]["lDAPDisplayName"][0], attr_ldap_display_name)
136         self.assertTrue("schemaIDGUID" in res[0])
137         if "msDS-IntId" in res[0]:
138             msDS_IntId = int(res[0]["msDS-IntId"][0])
139             if msDS_IntId < 0:
140                 msDS_IntId += (1 << 32)
141         else:
142             msDS_IntId = None
143
144         class_name = "test-Class" + time.strftime("%s", time.gmtime())
145         class_ldap_display_name = class_name.replace("-", "")
146
147         # First try to create a class with a wrong "defaultObjectCategory"
148         ldif = """
149 dn: CN=%s,%s""" % (class_name, self.schema_dn) + """
150 objectClass: top
151 objectClass: classSchema
152 defaultObjectCategory: CN=_
153 adminDescription: """ + class_name + """
154 adminDisplayName: """ + class_name + """
155 cn: """ + class_name + """
156 governsId: 1.3.6.1.4.1.7165.4.6.2.6.1.""" + str(random.randint(1,100000)) + """
157 instanceType: 4
158 objectClassCategory: 1
159 subClassOf: organizationalPerson
160 systemFlags: 16
161 rDNAttID: cn
162 systemMustContain: cn
163 systemMustContain: """ + attr_ldap_display_name + """
164 systemOnly: FALSE
165 """
166         try:
167                  self.ldb.add_ldif(ldif)
168                  self.fail()
169         except LdbError as e1:
170                  (num, _) = e1.args
171                  self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
172
173         ldif = """
174 dn: CN=%s,%s""" % (class_name, self.schema_dn) + """
175 objectClass: top
176 objectClass: classSchema
177 adminDescription: """ + class_name + """
178 adminDisplayName: """ + class_name + """
179 cn: """ + class_name + """
180 governsId: 1.3.6.1.4.1.7165.4.6.2.6.2.""" + str(random.randint(1,100000)) + """
181 instanceType: 4
182 objectClassCategory: 1
183 subClassOf: organizationalPerson
184 systemFlags: 16
185 rDNAttID: cn
186 systemMustContain: cn
187 systemMustContain: """ + attr_ldap_display_name + """
188 systemOnly: FALSE
189 """
190         self.ldb.add_ldif(ldif)
191
192         # Search for created objectclass
193         res = []
194         res = self.ldb.search("cn=%s,%s" % (class_name, self.schema_dn), scope=SCOPE_BASE,
195                               attrs=["lDAPDisplayName", "defaultObjectCategory", "schemaIDGUID", "distinguishedName"])
196         self.assertEquals(len(res), 1)
197         self.assertEquals(res[0]["lDAPDisplayName"][0], class_ldap_display_name)
198         self.assertEquals(res[0]["defaultObjectCategory"][0], res[0]["distinguishedName"][0])
199         self.assertTrue("schemaIDGUID" in res[0])
200
201         ldif = """
202 dn:
203 changetype: modify
204 add: schemaUpdateNow
205 schemaUpdateNow: 1
206 """
207         self.ldb.modify_ldif(ldif)
208
209         object_name = "obj" + time.strftime("%s", time.gmtime())
210
211         ldif = """
212 dn: CN=%s,CN=Users,%s"""% (object_name, self.base_dn) + """
213 objectClass: organizationalPerson
214 objectClass: person
215 objectClass: """ + class_ldap_display_name + """
216 objectClass: top
217 cn: """ + object_name + """
218 instanceType: 4
219 objectCategory: CN=%s,%s"""% (class_name, self.schema_dn) + """
220 distinguishedName: CN=%s,CN=Users,%s"""% (object_name, self.base_dn) + """
221 name: """ + object_name + """
222 """ + attr_ldap_display_name + """: test
223 """
224         self.ldb.add_ldif(ldif)
225
226         # Search for created object
227         obj_res = self.ldb.search("cn=%s,cn=Users,%s" % (object_name, self.base_dn), scope=SCOPE_BASE, attrs=["replPropertyMetaData"])
228
229         self.assertEquals(len(obj_res), 1)
230         self.assertTrue("replPropertyMetaData" in obj_res[0])
231         val = obj_res[0]["replPropertyMetaData"][0]
232         repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob, str(val))
233         obj = repl.ctr
234
235         # Windows 2000 functional level won't have this.  It is too
236         # hard to work it out from the prefixmap however, so we skip
237         # this test in that case.
238         if msDS_IntId is not None:
239             found = False
240             for o in repl.ctr.array:
241                 if o.attid == msDS_IntId:
242                     found = True
243                     break
244             self.assertTrue(found, "Did not find 0x%08x in replPropertyMetaData" % msDS_IntId)
245         # Delete the object
246         delete_force(self.ldb, "cn=%s,cn=Users,%s" % (object_name, self.base_dn))
247
248     def test_subClassOf(self):
249         """ Testing usage of custom child schamaClass
250         """
251
252         class_name = "my-Class" + time.strftime("%s", time.gmtime())
253         class_ldap_display_name = class_name.replace("-", "")
254
255         ldif = """
256 dn: CN=%s,%s""" % (class_name, self.schema_dn) + """
257 objectClass: top
258 objectClass: classSchema
259 adminDescription: """ + class_name + """
260 adminDisplayName: """ + class_name + """
261 cn: """ + class_name + """
262 governsId: 1.3.6.1.4.1.7165.4.6.2.6.3.""" + str(random.randint(1,100000)) + """
263 instanceType: 4
264 objectClassCategory: 1
265 subClassOf: organizationalUnit
266 systemFlags: 16
267 systemOnly: FALSE
268 """
269         self.ldb.add_ldif(ldif)
270
271         # Search for created objectclass
272         res = []
273         res = self.ldb.search("cn=%s,%s" % (class_name, self.schema_dn), scope=SCOPE_BASE,
274                               attrs=["lDAPDisplayName", "defaultObjectCategory",
275                                      "schemaIDGUID", "distinguishedName"])
276         self.assertEquals(len(res), 1)
277         self.assertEquals(res[0]["lDAPDisplayName"][0], class_ldap_display_name)
278         self.assertEquals(res[0]["defaultObjectCategory"][0], res[0]["distinguishedName"][0])
279         self.assertTrue("schemaIDGUID" in res[0])
280
281         ldif = """
282 dn:
283 changetype: modify
284 add: schemaUpdateNow
285 schemaUpdateNow: 1
286 """
287         self.ldb.modify_ldif(ldif)
288
289         object_name = "org" + time.strftime("%s", time.gmtime())
290
291         ldif = """
292 dn: OU=%s,%s""" % (object_name, self.base_dn) + """
293 objectClass: """ + class_ldap_display_name + """
294 ou: """ + object_name + """
295 instanceType: 4
296 """
297         self.ldb.add_ldif(ldif)
298
299         # Search for created object
300         res = []
301         res = self.ldb.search("ou=%s,%s" % (object_name, self.base_dn), scope=SCOPE_BASE, attrs=["dn"])
302         self.assertEquals(len(res), 1)
303         # Delete the object
304         delete_force(self.ldb, "ou=%s,%s" % (object_name, self.base_dn))
305
306
307     def test_duplicate_attributeID(self):
308         """Testing creating a duplicate attribute"""
309         rand = str(random.randint(1,100000))
310         attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + "-" + rand
311         attr_ldap_display_name = attr_name.replace("-", "")
312         attributeID = "1.3.6.1.4.1.7165.4.6.1.6.2." + rand
313         ldif = """
314 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
315 objectClass: top
316 objectClass: attributeSchema
317 adminDescription: """ + attr_name + """
318 adminDisplayName: """ + attr_name + """
319 cn: """ + attr_name + """
320 attributeId: """ + attributeID + """
321 attributeSyntax: 2.5.5.12
322 omSyntax: 64
323 instanceType: 4
324 isSingleValued: TRUE
325 systemOnly: FALSE
326 """
327         self.ldb.add_ldif(ldif)
328
329         ldif = """
330 dn: CN=%s-dup,%s""" % (attr_name, self.schema_dn) + """
331 objectClass: top
332 objectClass: attributeSchema
333 adminDescription: """ + attr_name + """dup
334 adminDisplayName: """ + attr_name + """dup
335 cn: """ + attr_name + """-dup
336 attributeId: """ + attributeID + """
337 attributeSyntax: 2.5.5.12
338 omSyntax: 64
339 instanceType: 4
340 isSingleValued: TRUE
341 systemOnly: FALSE
342 """
343         try:
344             self.ldb.add_ldif(ldif)
345             self.fail("Should have failed to add duplicate attributeID value")
346         except LdbError as e2:
347             (enum, estr) = e2.args
348             self.assertEquals(enum, ERR_UNWILLING_TO_PERFORM)
349
350
351     def test_duplicate_attributeID_governsID(self):
352         """Testing creating a duplicate attribute and class"""
353         rand = str(random.randint(1,100000))
354         attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + "-" + rand
355         attr_ldap_display_name = attr_name.replace("-", "")
356         attributeID = "1.3.6.1.4.1.7165.4.6.1.6.3." + rand
357         ldif = """
358 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
359 objectClass: top
360 objectClass: attributeSchema
361 adminDescription: """ + attr_name + """
362 adminDisplayName: """ + attr_name + """
363 cn: """ + attr_name + """
364 attributeId: """ + attributeID + """
365 attributeSyntax: 2.5.5.12
366 omSyntax: 64
367 instanceType: 4
368 isSingleValued: TRUE
369 systemOnly: FALSE
370 """
371         self.ldb.add_ldif(ldif)
372
373         ldif = """
374 dn: CN=%s-dup,%s""" % (attr_name, self.schema_dn) + """
375 objectClass: top
376 objectClass: classSchema
377 adminDescription: """ + attr_name + """dup
378 adminDisplayName: """ + attr_name + """dup
379 cn: """ + attr_name + """-dup
380 governsId: """ + attributeID + """
381 instanceType: 4
382 objectClassCategory: 1
383 subClassOf: organizationalPerson
384 rDNAttID: cn
385 systemMustContain: cn
386 systemOnly: FALSE
387 """
388         try:
389             self.ldb.add_ldif(ldif)
390             self.fail("Should have failed to add duplicate governsID conflicting with attributeID value")
391         except LdbError as e3:
392             (enum, estr) = e3.args
393             self.assertEquals(enum, ERR_UNWILLING_TO_PERFORM)
394
395
396     def test_duplicate_cn(self):
397         """Testing creating a duplicate attribute"""
398         rand = str(random.randint(1,100000))
399         attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + "-" + rand
400         attr_ldap_display_name = attr_name.replace("-", "")
401         attributeID = "1.3.6.1.4.1.7165.4.6.1.6.4." + rand
402         ldif = """
403 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
404 objectClass: top
405 objectClass: attributeSchema
406 adminDescription: """ + attr_name + """
407 adminDisplayName: """ + attr_name + """
408 cn: """ + attr_name + """
409 attributeId: """ + attributeID + """
410 attributeSyntax: 2.5.5.12
411 omSyntax: 64
412 instanceType: 4
413 isSingleValued: TRUE
414 systemOnly: FALSE
415 """
416         self.ldb.add_ldif(ldif)
417
418         ldif = """
419 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
420 objectClass: top
421 objectClass: attributeSchema
422 adminDescription: """ + attr_name + """dup
423 adminDisplayName: """ + attr_name + """dup
424 cn: """ + attr_name + """
425 attributeId: """ + attributeID + """.1
426 attributeSyntax: 2.5.5.12
427 omSyntax: 64
428 instanceType: 4
429 isSingleValued: TRUE
430 systemOnly: FALSE
431 """
432         try:
433             self.ldb.add_ldif(ldif)
434             self.fail("Should have failed to add attribute with duplicate CN")
435         except LdbError as e4:
436             (enum, estr) = e4.args
437             self.assertEquals(enum, ERR_ENTRY_ALREADY_EXISTS)
438
439     def test_duplicate_implicit_ldapdisplayname(self):
440         """Testing creating a duplicate attribute ldapdisplayname"""
441         rand = str(random.randint(1,100000))
442         attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + "-" + rand
443         attr_ldap_display_name = attr_name.replace("-", "")
444         attributeID = "1.3.6.1.4.1.7165.4.6.1.6.5." + rand
445         ldif = """
446 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
447 objectClass: top
448 objectClass: attributeSchema
449 adminDescription: """ + attr_name + """
450 adminDisplayName: """ + attr_name + """
451 cn: """ + attr_name + """
452 attributeId: """ + attributeID + """
453 attributeSyntax: 2.5.5.12
454 omSyntax: 64
455 instanceType: 4
456 isSingleValued: TRUE
457 systemOnly: FALSE
458 """
459         self.ldb.add_ldif(ldif)
460
461         ldif = """
462 dn: CN=%s-dup,%s""" % (attr_name, self.schema_dn) + """
463 objectClass: top
464 objectClass: attributeSchema
465 adminDescription: """ + attr_name + """dup
466 adminDisplayName: """ + attr_name + """dup
467 cn: """ + attr_name + """-dup
468 ldapDisplayName: """ + attr_ldap_display_name + """
469 attributeId: """ + attributeID + """.1
470 attributeSyntax: 2.5.5.12
471 omSyntax: 64
472 instanceType: 4
473 isSingleValued: TRUE
474 systemOnly: FALSE
475 """
476         try:
477             self.ldb.add_ldif(ldif)
478             self.fail("Should have failed to add attribute with duplicate of the implicit ldapDisplayName")
479         except LdbError as e5:
480             (enum, estr) = e5.args
481             self.assertEquals(enum, ERR_UNWILLING_TO_PERFORM)
482
483
484     def test_duplicate_explicit_ldapdisplayname(self):
485         """Testing creating a duplicate attribute ldapdisplayname"""
486         rand = str(random.randint(1,100000))
487         attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + "-" + rand
488         attr_ldap_display_name = attr_name.replace("-", "")
489         attributeID = "1.3.6.1.4.1.7165.4.6.1.6.6." + rand
490         ldif = """
491 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
492 objectClass: top
493 objectClass: attributeSchema
494 adminDescription: """ + attr_name + """
495 adminDisplayName: """ + attr_name + """
496 cn: """ + attr_name + """
497 attributeId: """ + attributeID + """
498 attributeSyntax: 2.5.5.12
499 ldapDisplayName: """ + attr_ldap_display_name + """
500 omSyntax: 64
501 instanceType: 4
502 isSingleValued: TRUE
503 systemOnly: FALSE
504 """
505         self.ldb.add_ldif(ldif)
506
507         ldif = """
508 dn: CN=%s-dup,%s""" % (attr_name, self.schema_dn) + """
509 objectClass: top
510 objectClass: attributeSchema
511 adminDescription: """ + attr_name + """dup
512 adminDisplayName: """ + attr_name + """dup
513 cn: """ + attr_name + """-dup
514 ldapDisplayName: """ + attr_ldap_display_name + """
515 attributeId: """ + attributeID + """.1
516 attributeSyntax: 2.5.5.12
517 omSyntax: 64
518 instanceType: 4
519 isSingleValued: TRUE
520 systemOnly: FALSE
521 """
522         try:
523             self.ldb.add_ldif(ldif)
524             self.fail("Should have failed to add attribute with duplicate ldapDisplayName")
525         except LdbError as e6:
526             (enum, estr) = e6.args
527             self.assertEquals(enum, ERR_UNWILLING_TO_PERFORM)
528
529
530     def test_duplicate_explicit_ldapdisplayname_with_class(self):
531         """Testing creating a duplicate attribute ldapdisplayname between
532         and attribute and a class"""
533         rand = str(random.randint(1,100000))
534         attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + "-" + rand
535         attr_ldap_display_name = attr_name.replace("-", "")
536         attributeID = "1.3.6.1.4.1.7165.4.6.1.6.7." + rand
537         governsID   = "1.3.6.1.4.1.7165.4.6.2.6.4." + rand
538         ldif = """
539 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
540 objectClass: top
541 objectClass: attributeSchema
542 adminDescription: """ + attr_name + """
543 adminDisplayName: """ + attr_name + """
544 cn: """ + attr_name + """
545 attributeId: """ + attributeID + """
546 attributeSyntax: 2.5.5.12
547 ldapDisplayName: """ + attr_ldap_display_name + """
548 omSyntax: 64
549 instanceType: 4
550 isSingleValued: TRUE
551 systemOnly: FALSE
552 """
553         self.ldb.add_ldif(ldif)
554
555         ldif = """
556 dn: CN=%s-dup,%s""" % (attr_name, self.schema_dn) + """
557 objectClass: top
558 objectClass: classSchema
559 adminDescription: """ + attr_name + """dup
560 adminDisplayName: """ + attr_name + """dup
561 cn: """ + attr_name + """-dup
562 ldapDisplayName: """ + attr_ldap_display_name + """
563 governsID: """ + governsID + """
564 instanceType: 4
565 objectClassCategory: 1
566 subClassOf: organizationalPerson
567 rDNAttID: cn
568 systemMustContain: cn
569 systemOnly: FALSE
570 """
571         try:
572             self.ldb.add_ldif(ldif)
573             self.fail("Should have failed to add class with duplicate ldapDisplayName")
574         except LdbError as e7:
575             (enum, estr) = e7.args
576             self.assertEquals(enum, ERR_UNWILLING_TO_PERFORM)
577
578
579     def test_duplicate_via_rename_ldapdisplayname(self):
580         """Testing creating a duplicate attribute ldapdisplayname"""
581         rand = str(random.randint(1,100000))
582         attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + "-" + rand
583         attr_ldap_display_name = attr_name.replace("-", "")
584         attributeID = "1.3.6.1.4.1.7165.4.6.1.6.8." + rand
585         ldif = """
586 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
587 objectClass: top
588 objectClass: attributeSchema
589 adminDescription: """ + attr_name + """
590 adminDisplayName: """ + attr_name + """
591 cn: """ + attr_name + """
592 attributeId: """ + attributeID + """
593 attributeSyntax: 2.5.5.12
594 ldapDisplayName: """ + attr_ldap_display_name + """
595 omSyntax: 64
596 instanceType: 4
597 isSingleValued: TRUE
598 systemOnly: FALSE
599 """
600         self.ldb.add_ldif(ldif)
601
602         ldif = """
603 dn: CN=%s-dup,%s""" % (attr_name, self.schema_dn) + """
604 objectClass: top
605 objectClass: attributeSchema
606 adminDescription: """ + attr_name + """dup
607 adminDisplayName: """ + attr_name + """dup
608 cn: """ + attr_name + """-dup
609 ldapDisplayName: """ + attr_ldap_display_name + """dup
610 attributeId: """ + attributeID + """.1
611 attributeSyntax: 2.5.5.12
612 omSyntax: 64
613 instanceType: 4
614 isSingleValued: TRUE
615 systemOnly: FALSE
616 """
617         self.ldb.add_ldif(ldif)
618
619         ldif = """
620 dn: CN=%s-dup,%s""" % (attr_name, self.schema_dn) + """
621 changetype: modify
622 replace: ldapDisplayName
623 ldapDisplayName: """ + attr_ldap_display_name + """
624 -
625 """
626         try:
627             self.ldb.modify_ldif(ldif)
628             self.fail("Should have failed to modify schema to have attribute with duplicate ldapDisplayName")
629         except LdbError as e8:
630             (enum, estr) = e8.args
631             self.assertEquals(enum, ERR_UNWILLING_TO_PERFORM)
632
633
634     def test_duplicate_via_rename_attributeID(self):
635         """Testing creating a duplicate attributeID"""
636         rand = str(random.randint(1,100000))
637         attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + "-" + rand
638         attr_ldap_display_name = attr_name.replace("-", "")
639         attributeID = "1.3.6.1.4.1.7165.4.6.1.6.9." + rand
640         ldif = """
641 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
642 objectClass: top
643 objectClass: attributeSchema
644 adminDescription: """ + attr_name + """
645 adminDisplayName: """ + attr_name + """
646 cn: """ + attr_name + """
647 attributeId: """ + attributeID + """
648 attributeSyntax: 2.5.5.12
649 ldapDisplayName: """ + attr_ldap_display_name + """
650 omSyntax: 64
651 instanceType: 4
652 isSingleValued: TRUE
653 systemOnly: FALSE
654 """
655         self.ldb.add_ldif(ldif)
656
657         ldif = """
658 dn: CN=%s-dup,%s""" % (attr_name, self.schema_dn) + """
659 objectClass: top
660 objectClass: attributeSchema
661 adminDescription: """ + attr_name + """dup
662 adminDisplayName: """ + attr_name + """dup
663 cn: """ + attr_name + """-dup
664 ldapDisplayName: """ + attr_ldap_display_name + """dup
665 attributeId: """ + attributeID + """.1
666 attributeSyntax: 2.5.5.12
667 omSyntax: 64
668 instanceType: 4
669 isSingleValued: TRUE
670 systemOnly: FALSE
671 """
672         self.ldb.add_ldif(ldif)
673
674         ldif = """
675 dn: CN=%s-dup,%s""" % (attr_name, self.schema_dn) + """
676 changetype: modify
677 replace: attributeId
678 attributeId: """ + attributeID + """
679 -
680 """
681         try:
682             self.ldb.modify_ldif(ldif)
683             self.fail("Should have failed to modify schema to have attribute with duplicate attributeID")
684         except LdbError as e9:
685             (enum, estr) = e9.args
686             self.assertEquals(enum, ERR_CONSTRAINT_VIOLATION)
687
688     def test_remove_ldapdisplayname(self):
689         """Testing removing the ldapdisplayname"""
690         rand = str(random.randint(1,100000))
691         attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + "-" + rand
692         attr_ldap_display_name = attr_name.replace("-", "")
693         attributeID = "1.3.6.1.4.1.7165.4.6.1.6.10." + rand
694         ldif = """
695 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
696 objectClass: top
697 objectClass: attributeSchema
698 adminDescription: """ + attr_name + """
699 adminDisplayName: """ + attr_name + """
700 cn: """ + attr_name + """
701 attributeId: """ + attributeID + """
702 attributeSyntax: 2.5.5.12
703 ldapDisplayName: """ + attr_ldap_display_name + """
704 omSyntax: 64
705 instanceType: 4
706 isSingleValued: TRUE
707 systemOnly: FALSE
708 """
709         self.ldb.add_ldif(ldif)
710
711         ldif = """
712 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
713 changetype: modify
714 replace: ldapDisplayName
715 -
716 """
717         try:
718             self.ldb.modify_ldif(ldif)
719             self.fail("Should have failed to remove the ldapdisplayname")
720         except LdbError as e10:
721             (enum, estr) = e10.args
722             self.assertEquals(enum, ERR_OBJECT_CLASS_VIOLATION)
723
724     def test_rename_ldapdisplayname(self):
725         """Testing renaming ldapdisplayname"""
726         rand = str(random.randint(1,100000))
727         attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + "-" + rand
728         attr_ldap_display_name = attr_name.replace("-", "")
729         attributeID = "1.3.6.1.4.1.7165.4.6.1.6.11." + rand
730         ldif = """
731 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
732 objectClass: top
733 objectClass: attributeSchema
734 adminDescription: """ + attr_name + """
735 adminDisplayName: """ + attr_name + """
736 cn: """ + attr_name + """
737 attributeId: """ + attributeID + """
738 attributeSyntax: 2.5.5.12
739 ldapDisplayName: """ + attr_ldap_display_name + """
740 omSyntax: 64
741 instanceType: 4
742 isSingleValued: TRUE
743 systemOnly: FALSE
744 """
745         self.ldb.add_ldif(ldif)
746
747         ldif = """
748 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
749 changetype: modify
750 replace: ldapDisplayName
751 ldapDisplayName: """ + attr_ldap_display_name + """2
752 -
753 """
754         self.ldb.modify_ldif(ldif)
755
756
757     def test_change_attributeID(self):
758         """Testing change the attributeID"""
759         rand = str(random.randint(1,100000))
760         attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + "-" + rand
761         attr_ldap_display_name = attr_name.replace("-", "")
762         attributeID = "1.3.6.1.4.1.7165.4.6.1.6.12." + rand
763         ldif = """
764 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
765 objectClass: top
766 objectClass: attributeSchema
767 adminDescription: """ + attr_name + """
768 adminDisplayName: """ + attr_name + """
769 cn: """ + attr_name + """
770 attributeId: """ + attributeID + """
771 attributeSyntax: 2.5.5.12
772 ldapDisplayName: """ + attr_ldap_display_name + """
773 omSyntax: 64
774 instanceType: 4
775 isSingleValued: TRUE
776 systemOnly: FALSE
777 """
778         self.ldb.add_ldif(ldif)
779
780         ldif = """
781 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
782 changetype: modify
783 replace: attributeID
784 attributeId: """ + attributeID + """.1
785
786 """
787         try:
788             self.ldb.modify_ldif(ldif)
789             self.fail("Should have failed to modify schema to have different attributeID")
790         except LdbError as e11:
791             (enum, estr) = e11.args
792             self.assertEquals(enum, ERR_CONSTRAINT_VIOLATION)
793
794
795     def test_change_attributeID_same(self):
796         """Testing change the attributeID to the same value"""
797         rand = str(random.randint(1,100000))
798         attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + "-" + rand
799         attr_ldap_display_name = attr_name.replace("-", "")
800         attributeID = "1.3.6.1.4.1.7165.4.6.1.6.13." + rand
801         ldif = """
802 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
803 objectClass: top
804 objectClass: attributeSchema
805 adminDescription: """ + attr_name + """
806 adminDisplayName: """ + attr_name + """
807 cn: """ + attr_name + """
808 attributeId: """ + attributeID + """
809 attributeSyntax: 2.5.5.12
810 ldapDisplayName: """ + attr_ldap_display_name + """
811 omSyntax: 64
812 instanceType: 4
813 isSingleValued: TRUE
814 systemOnly: FALSE
815 """
816         self.ldb.add_ldif(ldif)
817
818         ldif = """
819 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
820 changetype: modify
821 replace: attributeID
822 attributeId: """ + attributeID + """
823
824 """
825         try:
826             self.ldb.modify_ldif(ldif)
827             self.fail("Should have failed to modify schema to have the same attributeID")
828         except LdbError as e12:
829             (enum, estr) = e12.args
830             self.assertEquals(enum, ERR_CONSTRAINT_VIOLATION)
831
832
833     def test_generated_linkID(self):
834         """
835         Test that we automatically generate a linkID if the
836         OID "1.2.840.113556.1.2.50" is given as the linkID
837         of a new attribute, and that we don't get/can't add
838         duplicate linkIDs. Also test that we can add a backlink
839         by providing the attributeID or ldapDisplayName of
840         a forwards link in the linkID attribute.
841         """
842
843         # linkID generation isn't available before 2003
844         res = self.ldb.search(base="", expression="", scope=SCOPE_BASE,
845                          attrs=["domainControllerFunctionality"])
846         self.assertEquals(len(res), 1)
847         dc_level = int(res[0]["domainControllerFunctionality"][0])
848         if dc_level < DS_DOMAIN_FUNCTION_2003:
849             return
850
851         rand = str(random.randint(1,100000))
852
853         attr_name_1 = "test-generated-linkID" + time.strftime("%s", time.gmtime()) + "-" + rand
854         attr_ldap_display_name_1 = attr_name_1.replace("-", "")
855         attributeID_1 = "1.3.6.1.4.1.7165.4.6.1.6.16." + rand
856         ldif = """
857 dn: CN=%s,%s""" % (attr_name_1, self.schema_dn) + """
858 objectClass: top
859 objectClass: attributeSchema
860 adminDescription: """ + attr_name_1 + """
861 adminDisplayName: """ + attr_name_1 + """
862 cn: """ + attr_name_1 + """
863 attributeId: """ + attributeID_1 + """
864 linkID: 1.2.840.113556.1.2.50
865 attributeSyntax: 2.5.5.1
866 ldapDisplayName: """ + attr_ldap_display_name_1 + """
867 omSyntax: 127
868 instanceType: 4
869 isSingleValued: TRUE
870 systemOnly: FALSE
871 """
872
873         try:
874             self.ldb.add_ldif(ldif)
875         except LdbError as e13:
876             (enum, estr) = e13.args
877             self.fail(estr)
878
879         attr_name_2 = "test-generated-linkID-2" + time.strftime("%s", time.gmtime()) + "-" + rand
880         attr_ldap_display_name_2 = attr_name_2.replace("-", "")
881         attributeID_2 = "1.3.6.1.4.1.7165.4.6.1.6.17." + rand
882         ldif = """
883 dn: CN=%s,%s""" % (attr_name_2, self.schema_dn) + """
884 objectClass: top
885 objectClass: attributeSchema
886 adminDescription: """ + attr_name_2 + """
887 adminDisplayName: """ + attr_name_2 + """
888 cn: """ + attr_name_2 + """
889 attributeId: """ + attributeID_2 + """
890 linkID: 1.2.840.113556.1.2.50
891 attributeSyntax: 2.5.5.1
892 ldapDisplayName: """ + attr_ldap_display_name_2 + """
893 omSyntax: 127
894 instanceType: 4
895 isSingleValued: TRUE
896 systemOnly: FALSE
897 """
898
899         try:
900             self.ldb.add_ldif(ldif)
901         except LdbError as e14:
902             (enum, estr) = e14.args
903             self.fail(estr)
904
905         res = self.ldb.search("CN=%s,%s" % (attr_name_1, self.schema_dn),
906                               scope=SCOPE_BASE,
907                               attrs=["linkID"])
908         self.assertEquals(len(res), 1)
909         linkID_1 = int(res[0]["linkID"][0])
910
911         res = self.ldb.search("CN=%s,%s" % (attr_name_2, self.schema_dn),
912                               scope=SCOPE_BASE,
913                               attrs=["linkID"])
914         self.assertEquals(len(res), 1)
915         linkID_2 = int(res[0]["linkID"][0])
916
917         # 0 should never be generated as a linkID
918         self.assertFalse(linkID_1 == 0)
919         self.assertFalse(linkID_2 == 0)
920
921         # The generated linkID should always be even, because
922         # it should assume we're adding a forward link.
923         self.assertTrue(linkID_1 % 2 == 0)
924         self.assertTrue(linkID_2 % 2 == 0)
925
926         self.assertFalse(linkID_1 == linkID_2)
927
928         # This is only necessary against Windows, since we depend
929         # on the previously added links in the next ones and Windows
930         # won't refresh the schema as we add them.
931         ldif = """
932 dn:
933 changetype: modify
934 replace: schemaupdatenow
935 schemaupdatenow: 1
936 """
937         self.ldb.modify_ldif(ldif)
938
939         # If we add a new link with the same linkID, it should fail
940         attr_name = "test-generated-linkID-duplicate" + time.strftime("%s", time.gmtime()) + "-" + rand
941         attr_ldap_display_name = attr_name.replace("-", "")
942         attributeID = "1.3.6.1.4.1.7165.4.6.1.6.18." + rand
943         ldif = """
944 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
945 objectClass: top
946 objectClass: attributeSchema
947 adminDescription: """ + attr_name + """
948 adminDisplayName: """ + attr_name + """
949 cn: """ + attr_name + """
950 attributeId: """ + attributeID + """
951 linkID: """ + str(linkID_1) + """
952 attributeSyntax: 2.5.5.1
953 ldapDisplayName: """ + attr_ldap_display_name + """
954 omSyntax: 127
955 instanceType: 4
956 isSingleValued: TRUE
957 systemOnly: FALSE
958 """
959
960         try:
961             self.ldb.add_ldif(ldif)
962             self.fail("Should have failed to add duplicate linkID value")
963         except LdbError as e15:
964             (enum, estr) = e15.args
965             self.assertEquals(enum, ERR_UNWILLING_TO_PERFORM)
966
967         # If we add another attribute with the attributeID or lDAPDisplayName
968         # of a forward link in its linkID field, it should add as a backlink
969
970         attr_name_3 = "test-generated-linkID-backlink" + time.strftime("%s", time.gmtime()) + "-" + rand
971         attr_ldap_display_name_3 = attr_name_3.replace("-", "")
972         attributeID_3 = "1.3.6.1.4.1.7165.4.6.1.6.19." + rand
973         ldif = """
974 dn: CN=%s,%s""" % (attr_name_3, self.schema_dn) + """
975 objectClass: top
976 objectClass: attributeSchema
977 adminDescription: """ + attr_name_3 + """
978 adminDisplayName: """ + attr_name_3 + """
979 cn: """ + attr_name_3 + """
980 attributeId: """ + attributeID_3 + """
981 linkID: """ + str(linkID_1+1) + """
982 attributeSyntax: 2.5.5.1
983 ldapDisplayName: """ + attr_ldap_display_name_3 + """
984 omSyntax: 127
985 instanceType: 4
986 isSingleValued: TRUE
987 systemOnly: FALSE
988 """
989
990         try:
991             self.ldb.add_ldif(ldif)
992         except LdbError as e16:
993             (enum, estr) = e16.args
994             self.fail(estr)
995
996         res = self.ldb.search("CN=%s,%s" % (attr_name_3, self.schema_dn),
997                               scope=SCOPE_BASE,
998                               attrs=["linkID"])
999         self.assertEquals(len(res), 1)
1000         linkID = int(res[0]["linkID"][0])
1001         self.assertEquals(linkID, linkID_1 + 1)
1002
1003         attr_name_4 = "test-generated-linkID-backlink-2" + time.strftime("%s", time.gmtime()) + "-" + rand
1004         attr_ldap_display_name_4 = attr_name_4.replace("-", "")
1005         attributeID_4 = "1.3.6.1.4.1.7165.4.6.1.6.20." + rand
1006         ldif = """
1007 dn: CN=%s,%s""" % (attr_name_4, self.schema_dn) + """
1008 objectClass: top
1009 objectClass: attributeSchema
1010 adminDescription: """ + attr_name_4 + """
1011 adminDisplayName: """ + attr_name_4 + """
1012 cn: """ + attr_name_4 + """
1013 attributeId: """ + attributeID_4 + """
1014 linkID: """ + attr_ldap_display_name_2 + """
1015 attributeSyntax: 2.5.5.1
1016 ldapDisplayName: """ + attr_ldap_display_name_4 + """
1017 omSyntax: 127
1018 instanceType: 4
1019 isSingleValued: TRUE
1020 systemOnly: FALSE
1021 """
1022
1023         try:
1024             self.ldb.add_ldif(ldif)
1025         except LdbError as e17:
1026             (enum, estr) = e17.args
1027             self.fail(estr)
1028
1029         res = self.ldb.search("CN=%s,%s" % (attr_name_4, self.schema_dn),
1030                               scope=SCOPE_BASE,
1031                               attrs=["linkID"])
1032         self.assertEquals(len(res), 1)
1033         linkID = int(res[0]["linkID"][0])
1034         self.assertEquals(linkID, linkID_2 + 1)
1035
1036         # If we then try to add another backlink in the same way
1037         # for the same forwards link, we should fail.
1038
1039         attr_name = "test-generated-linkID-backlink-duplicate" + time.strftime("%s", time.gmtime()) + "-" + rand
1040         attr_ldap_display_name = attr_name.replace("-", "")
1041         attributeID = "1.3.6.1.4.1.7165.4.6.1.6.21." + rand
1042         ldif = """
1043 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
1044 objectClass: top
1045 objectClass: attributeSchema
1046 adminDescription: """ + attr_name + """
1047 adminDisplayName: """ + attr_name + """
1048 cn: """ + attr_name + """
1049 attributeId: """ + attributeID + """
1050 linkID: """ + attributeID_1 + """
1051 attributeSyntax: 2.5.5.1
1052 ldapDisplayName: """ + attr_ldap_display_name + """
1053 omSyntax: 127
1054 instanceType: 4
1055 isSingleValued: TRUE
1056 systemOnly: FALSE
1057 """
1058
1059         try:
1060             self.ldb.add_ldif(ldif)
1061             self.fail("Should have failed to add duplicate backlink")
1062         except LdbError as e18:
1063             (enum, estr) = e18.args
1064             self.assertEquals(enum, ERR_UNWILLING_TO_PERFORM)
1065
1066         # If we try to supply the attributeID or ldapDisplayName
1067         # of an existing backlink in the linkID field of a new link,
1068         # it should fail.
1069
1070         attr_name = "test-generated-linkID-backlink-invalid" + time.strftime("%s", time.gmtime()) + "-" + rand
1071         attr_ldap_display_name = attr_name.replace("-", "")
1072         attributeID = "1.3.6.1.4.1.7165.4.6.1.6.22." + rand
1073         ldif = """
1074 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
1075 objectClass: top
1076 objectClass: attributeSchema
1077 adminDescription: """ + attr_name + """
1078 adminDisplayName: """ + attr_name + """
1079 cn: """ + attr_name + """
1080 attributeId: """ + attributeID + """
1081 linkID: """ + attributeID_3 + """
1082 attributeSyntax: 2.5.5.1
1083 ldapDisplayName: """ + attr_ldap_display_name + """
1084 omSyntax: 127
1085 instanceType: 4
1086 isSingleValued: TRUE
1087 systemOnly: FALSE
1088 """
1089
1090         try:
1091             self.ldb.add_ldif(ldif)
1092             self.fail("Should have failed to add backlink of backlink")
1093         except LdbError as e19:
1094             (enum, estr) = e19.args
1095             self.assertEquals(enum, ERR_UNWILLING_TO_PERFORM)
1096
1097         attr_name = "test-generated-linkID-backlink-invalid-2" + time.strftime("%s", time.gmtime()) + "-" + rand
1098         attr_ldap_display_name = attr_name.replace("-", "")
1099         attributeID = "1.3.6.1.4.1.7165.4.6.1.6.23." + rand
1100         ldif = """
1101 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
1102 objectClass: top
1103 objectClass: attributeSchema
1104 adminDescription: """ + attr_name + """
1105 adminDisplayName: """ + attr_name + """
1106 cn: """ + attr_name + """
1107 attributeId: """ + attributeID + """
1108 linkID: """ + attr_ldap_display_name_4 + """
1109 attributeSyntax: 2.5.5.1
1110 ldapDisplayName: """ + attr_ldap_display_name + """
1111 omSyntax: 127
1112 instanceType: 4
1113 isSingleValued: TRUE
1114 systemOnly: FALSE
1115 """
1116
1117         try:
1118             self.ldb.add_ldif(ldif)
1119             self.fail("Should have failed to add backlink of backlink")
1120         except LdbError as e20:
1121             (enum, estr) = e20.args
1122             self.assertEquals(enum, ERR_UNWILLING_TO_PERFORM)
1123
1124     def test_generated_mAPIID(self):
1125         """
1126         Test that we automatically generate a mAPIID if the
1127         OID "1.2.840.113556.1.2.49" is given as the mAPIID
1128         of a new attribute, and that we don't get/can't add
1129         duplicate mAPIIDs.
1130         """
1131
1132         rand = str(random.randint(1,100000))
1133
1134         attr_name_1 = "test-generated-mAPIID" + time.strftime("%s", time.gmtime()) + "-" + rand
1135         attr_ldap_display_name_1 = attr_name_1.replace("-", "")
1136         attributeID_1 = "1.3.6.1.4.1.7165.4.6.1.6.24." + rand
1137         ldif = """
1138 dn: CN=%s,%s""" % (attr_name_1, self.schema_dn) + """
1139 objectClass: top
1140 objectClass: attributeSchema
1141 adminDescription: """ + attr_name_1 + """
1142 adminDisplayName: """ + attr_name_1 + """
1143 cn: """ + attr_name_1 + """
1144 attributeId: """ + attributeID_1 + """
1145 mAPIID: 1.2.840.113556.1.2.49
1146 attributeSyntax: 2.5.5.1
1147 ldapDisplayName: """ + attr_ldap_display_name_1 + """
1148 omSyntax: 127
1149 instanceType: 4
1150 isSingleValued: TRUE
1151 systemOnly: FALSE
1152 """
1153
1154         try:
1155             self.ldb.add_ldif(ldif)
1156         except LdbError as e21:
1157             (enum, estr) = e21.args
1158             self.fail(estr)
1159
1160         res = self.ldb.search("CN=%s,%s" % (attr_name_1, self.schema_dn),
1161                               scope=SCOPE_BASE,
1162                               attrs=["mAPIID"])
1163         self.assertEquals(len(res), 1)
1164         mAPIID_1 = int(res[0]["mAPIID"][0])
1165
1166         ldif = """
1167 dn:
1168 changetype: modify
1169 replace: schemaupdatenow
1170 schemaupdatenow: 1
1171 """
1172         self.ldb.modify_ldif(ldif)
1173
1174         # If we add a new attribute with the same mAPIID, it should fail
1175         attr_name = "test-generated-mAPIID-duplicate" + time.strftime("%s", time.gmtime()) + "-" + rand
1176         attr_ldap_display_name = attr_name.replace("-", "")
1177         attributeID = "1.3.6.1.4.1.7165.4.6.1.6.25." + rand
1178         ldif = """
1179 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
1180 objectClass: top
1181 objectClass: attributeSchema
1182 adminDescription: """ + attr_name + """
1183 adminDisplayName: """ + attr_name + """
1184 cn: """ + attr_name + """
1185 attributeId: """ + attributeID + """
1186 mAPIID: """ + str(mAPIID_1) + """
1187 attributeSyntax: 2.5.5.1
1188 ldapDisplayName: """ + attr_ldap_display_name + """
1189 omSyntax: 127
1190 instanceType: 4
1191 isSingleValued: TRUE
1192 systemOnly: FALSE
1193 """
1194
1195         try:
1196             self.ldb.add_ldif(ldif)
1197             self.fail("Should have failed to add duplicate mAPIID value")
1198         except LdbError as e22:
1199             (enum, estr) = e22.args
1200             self.assertEquals(enum, ERR_UNWILLING_TO_PERFORM)
1201
1202
1203     def test_change_governsID(self):
1204         """Testing change the governsID"""
1205         rand = str(random.randint(1,100000))
1206         class_name = "test-Class" + time.strftime("%s", time.gmtime()) + "-" + rand
1207         class_ldap_display_name = class_name.replace("-", "")
1208         governsID = "1.3.6.1.4.1.7165.4.6.2.6.5." + rand
1209         ldif = """
1210 dn: CN=%s,%s""" % (class_name, self.schema_dn) + """
1211 objectClass: top
1212 objectClass: classSchema
1213 adminDescription: """ + class_name + """
1214 adminDisplayName: """ + class_name + """
1215 cn: """ + class_name + """
1216 governsId: """ + governsID + """
1217 ldapDisplayName: """ + class_ldap_display_name + """
1218 instanceType: 4
1219 objectClassCategory: 1
1220 subClassOf: organizationalPerson
1221 rDNAttID: cn
1222 systemMustContain: cn
1223 systemOnly: FALSE
1224 """
1225         self.ldb.add_ldif(ldif)
1226
1227         ldif = """
1228 dn: CN=%s,%s""" % (class_name, self.schema_dn) + """
1229 changetype: modify
1230 replace: governsID
1231 governsId: """ + governsID + """.1
1232
1233 """
1234         try:
1235             self.ldb.modify_ldif(ldif)
1236             self.fail("Should have failed to modify schema to have different governsID")
1237         except LdbError as e23:
1238             (enum, estr) = e23.args
1239             self.assertEquals(enum, ERR_CONSTRAINT_VIOLATION)
1240
1241
1242     def test_change_governsID_same(self):
1243         """Testing change the governsID"""
1244         rand = str(random.randint(1,100000))
1245         class_name = "test-Class" + time.strftime("%s", time.gmtime()) + "-" + rand
1246         class_ldap_display_name = class_name.replace("-", "")
1247         governsID = "1.3.6.1.4.1.7165.4.6.2.6.6." + rand
1248         ldif = """
1249 dn: CN=%s,%s""" % (class_name, self.schema_dn) + """
1250 objectClass: top
1251 objectClass: classSchema
1252 adminDescription: """ + class_name + """
1253 adminDisplayName: """ + class_name + """
1254 cn: """ + class_name + """
1255 governsId: """ + governsID + """
1256 ldapDisplayName: """ + class_ldap_display_name + """
1257 instanceType: 4
1258 objectClassCategory: 1
1259 subClassOf: organizationalPerson
1260 rDNAttID: cn
1261 systemMustContain: cn
1262 systemOnly: FALSE
1263 """
1264         self.ldb.add_ldif(ldif)
1265
1266         ldif = """
1267 dn: CN=%s,%s""" % (class_name, self.schema_dn) + """
1268 changetype: modify
1269 replace: governsID
1270 governsId: """ + governsID + """.1
1271
1272 """
1273         try:
1274             self.ldb.modify_ldif(ldif)
1275             self.fail("Should have failed to modify schema to have the same governsID")
1276         except LdbError as e24:
1277             (enum, estr) = e24.args
1278             self.assertEquals(enum, ERR_CONSTRAINT_VIOLATION)
1279
1280
1281     def test_subClassOf(self):
1282         """ Testing usage of custom child classSchema
1283         """
1284
1285         class_name = "my-Class" + time.strftime("%s", time.gmtime())
1286         class_ldap_display_name = class_name.replace("-", "")
1287
1288         ldif = """
1289 dn: CN=%s,%s""" % (class_name, self.schema_dn) + """
1290 objectClass: top
1291 objectClass: classSchema
1292 adminDescription: """ + class_name + """
1293 adminDisplayName: """ + class_name + """
1294 cn: """ + class_name + """
1295 governsId: 1.3.6.1.4.1.7165.4.6.2.6.7.""" + str(random.randint(1,100000)) + """
1296 instanceType: 4
1297 objectClassCategory: 1
1298 subClassOf: organizationalUnit
1299 systemFlags: 16
1300 systemOnly: FALSE
1301 """
1302         self.ldb.add_ldif(ldif)
1303
1304         # Search for created objectclass
1305         res = []
1306         res = self.ldb.search("cn=%s,%s" % (class_name, self.schema_dn), scope=SCOPE_BASE,
1307                               attrs=["lDAPDisplayName", "defaultObjectCategory",
1308                                      "schemaIDGUID", "distinguishedName"])
1309         self.assertEquals(len(res), 1)
1310         self.assertEquals(res[0]["lDAPDisplayName"][0], class_ldap_display_name)
1311         self.assertEquals(res[0]["defaultObjectCategory"][0], res[0]["distinguishedName"][0])
1312         self.assertTrue("schemaIDGUID" in res[0])
1313
1314         ldif = """
1315 dn:
1316 changetype: modify
1317 add: schemaUpdateNow
1318 schemaUpdateNow: 1
1319 """
1320         self.ldb.modify_ldif(ldif)
1321
1322         object_name = "org" + time.strftime("%s", time.gmtime())
1323
1324         ldif = """
1325 dn: OU=%s,%s""" % (object_name, self.base_dn) + """
1326 objectClass: """ + class_ldap_display_name + """
1327 ou: """ + object_name + """
1328 instanceType: 4
1329 """
1330         self.ldb.add_ldif(ldif)
1331
1332         # Search for created object
1333         res = []
1334         res = self.ldb.search("ou=%s,%s" % (object_name, self.base_dn), scope=SCOPE_BASE, attrs=["dn"])
1335         self.assertEquals(len(res), 1)
1336         # Delete the object
1337         delete_force(self.ldb, "ou=%s,%s" % (object_name, self.base_dn))
1338
1339
1340 class SchemaTests_msDS_IntId(samba.tests.TestCase):
1341
1342     def setUp(self):
1343         super(SchemaTests_msDS_IntId, self).setUp()
1344         self.ldb = SamDB(host, credentials=creds,
1345             session_info=system_session(lp), lp=lp, options=ldb_options)
1346         res = self.ldb.search(base="", expression="", scope=SCOPE_BASE,
1347                          attrs=["schemaNamingContext", "defaultNamingContext",
1348                                 "forestFunctionality"])
1349         self.assertEquals(len(res), 1)
1350         self.schema_dn = res[0]["schemaNamingContext"][0]
1351         self.base_dn = res[0]["defaultNamingContext"][0]
1352         self.forest_level = int(res[0]["forestFunctionality"][0])
1353
1354     def _ldap_schemaUpdateNow(self):
1355         ldif = """
1356 dn:
1357 changetype: modify
1358 add: schemaUpdateNow
1359 schemaUpdateNow: 1
1360 """
1361         self.ldb.modify_ldif(ldif)
1362
1363     def _make_obj_names(self, prefix):
1364         class_name = prefix + time.strftime("%s", time.gmtime())
1365         class_ldap_name = class_name.replace("-", "")
1366         class_dn = "CN=%s,%s" % (class_name, self.schema_dn)
1367         return (class_name, class_ldap_name, class_dn)
1368
1369     def _is_schema_base_object(self, ldb_msg):
1370         """Test systemFlags for SYSTEM_FLAG_SCHEMA_BASE_OBJECT (16)"""
1371         systemFlags = 0
1372         if "systemFlags" in ldb_msg:
1373             systemFlags = int(ldb_msg["systemFlags"][0])
1374         return (systemFlags & 16) != 0
1375
1376     def _make_attr_ldif(self, attr_name, attr_dn):
1377         ldif = """
1378 dn: """ + attr_dn + """
1379 objectClass: top
1380 objectClass: attributeSchema
1381 adminDescription: """ + attr_name + """
1382 adminDisplayName: """ + attr_name + """
1383 cn: """ + attr_name + """
1384 attributeId: 1.3.6.1.4.1.7165.4.6.1.6.14.""" + str(random.randint(1,100000)) + """
1385 attributeSyntax: 2.5.5.12
1386 omSyntax: 64
1387 instanceType: 4
1388 isSingleValued: TRUE
1389 systemOnly: FALSE
1390 """
1391         return ldif
1392
1393     def test_msDS_IntId_on_attr(self):
1394         """Testing msDs-IntId creation for Attributes.
1395         See MS-ADTS - 3.1.1.Attributes
1396
1397         This test should verify that:
1398         - Creating attribute with 'msDS-IntId' fails with ERR_UNWILLING_TO_PERFORM
1399         - Adding 'msDS-IntId' on existing attribute fails with ERR_CONSTRAINT_VIOLATION
1400         - Creating attribute with 'msDS-IntId' set and FLAG_SCHEMA_BASE_OBJECT flag
1401           set fails with ERR_UNWILLING_TO_PERFORM
1402         - Attributes created with FLAG_SCHEMA_BASE_OBJECT not set have
1403           'msDS-IntId' attribute added internally
1404         """
1405
1406         # 1. Create attribute without systemFlags
1407         # msDS-IntId should be created if forest functional
1408         # level is >= DS_DOMAIN_FUNCTION_2003
1409         # and missing otherwise
1410         (attr_name, attr_ldap_name, attr_dn) = self._make_obj_names("msDS-IntId-Attr-1-")
1411         ldif = self._make_attr_ldif(attr_name, attr_dn)
1412
1413         # try to add msDS-IntId during Attribute creation
1414         ldif_fail = ldif + "msDS-IntId: -1993108831\n"
1415         try:
1416             self.ldb.add_ldif(ldif_fail)
1417             self.fail("Adding attribute with preset msDS-IntId should fail")
1418         except LdbError as e25:
1419             (num, _) = e25.args
1420             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
1421
1422         # add the new attribute and update schema
1423         self.ldb.add_ldif(ldif)
1424         self._ldap_schemaUpdateNow()
1425
1426         # Search for created attribute
1427         res = []
1428         res = self.ldb.search(attr_dn, scope=SCOPE_BASE,
1429                               attrs=["lDAPDisplayName", "msDS-IntId", "systemFlags"])
1430         self.assertEquals(len(res), 1)
1431         self.assertEquals(res[0]["lDAPDisplayName"][0], attr_ldap_name)
1432         if self.forest_level >= DS_DOMAIN_FUNCTION_2003:
1433             if self._is_schema_base_object(res[0]):
1434                 self.assertTrue("msDS-IntId" not in res[0])
1435             else:
1436                 self.assertTrue("msDS-IntId" in res[0])
1437         else:
1438             self.assertTrue("msDS-IntId" not in res[0])
1439
1440         msg = Message()
1441         msg.dn = Dn(self.ldb, attr_dn)
1442         msg["msDS-IntId"] = MessageElement("-1993108831", FLAG_MOD_REPLACE, "msDS-IntId")
1443         try:
1444             self.ldb.modify(msg)
1445             self.fail("Modifying msDS-IntId should return error")
1446         except LdbError as e26:
1447             (num, _) = e26.args
1448             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
1449
1450         # 2. Create attribute with systemFlags = FLAG_SCHEMA_BASE_OBJECT
1451         # msDS-IntId should be created if forest functional
1452         # level is >= DS_DOMAIN_FUNCTION_2003
1453         # and missing otherwise
1454         (attr_name, attr_ldap_name, attr_dn) = self._make_obj_names("msDS-IntId-Attr-2-")
1455         ldif = self._make_attr_ldif(attr_name, attr_dn)
1456         ldif += "systemFlags: 16\n"
1457
1458         # try to add msDS-IntId during Attribute creation
1459         ldif_fail = ldif + "msDS-IntId: -1993108831\n"
1460         try:
1461             self.ldb.add_ldif(ldif_fail)
1462             self.fail("Adding attribute with preset msDS-IntId should fail")
1463         except LdbError as e27:
1464             (num, _) = e27.args
1465             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
1466
1467         # add the new attribute and update schema
1468         self.ldb.add_ldif(ldif)
1469         self._ldap_schemaUpdateNow()
1470
1471         # Search for created attribute
1472         res = []
1473         res = self.ldb.search(attr_dn, scope=SCOPE_BASE,
1474                               attrs=["lDAPDisplayName", "msDS-IntId"])
1475         self.assertEquals(len(res), 1)
1476         self.assertEquals(res[0]["lDAPDisplayName"][0], attr_ldap_name)
1477         if self.forest_level >= DS_DOMAIN_FUNCTION_2003:
1478             if self._is_schema_base_object(res[0]):
1479                 self.assertTrue("msDS-IntId" not in res[0])
1480             else:
1481                 self.assertTrue("msDS-IntId" in res[0])
1482         else:
1483             self.assertTrue("msDS-IntId" not in res[0])
1484
1485         msg = Message()
1486         msg.dn = Dn(self.ldb, attr_dn)
1487         msg["msDS-IntId"] = MessageElement("-1993108831", FLAG_MOD_REPLACE, "msDS-IntId")
1488         try:
1489             self.ldb.modify(msg)
1490             self.fail("Modifying msDS-IntId should return error")
1491         except LdbError as e28:
1492             (num, _) = e28.args
1493             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
1494
1495
1496     def _make_class_ldif(self, class_dn, class_name, sub_oid):
1497         ldif = """
1498 dn: """ + class_dn + """
1499 objectClass: top
1500 objectClass: classSchema
1501 adminDescription: """ + class_name + """
1502 adminDisplayName: """ + class_name + """
1503 cn: """ + class_name + """
1504 governsId: 1.3.6.1.4.1.7165.4.6.2.6.%d.""" % sub_oid + str(random.randint(1,100000)) + """
1505 instanceType: 4
1506 objectClassCategory: 1
1507 subClassOf: organizationalPerson
1508 rDNAttID: cn
1509 systemMustContain: cn
1510 systemOnly: FALSE
1511 """
1512         return ldif
1513
1514     def test_msDS_IntId_on_class(self):
1515         """Testing msDs-IntId creation for Class
1516            Reference: MS-ADTS - 3.1.1.2.4.8 Class classSchema"""
1517
1518         # 1. Create Class without systemFlags
1519         # msDS-IntId should be created if forest functional
1520         # level is >= DS_DOMAIN_FUNCTION_2003
1521         # and missing otherwise
1522         (class_name, class_ldap_name, class_dn) = self._make_obj_names("msDS-IntId-Class-1-")
1523         ldif = self._make_class_ldif(class_dn, class_name, 8)
1524
1525         # try to add msDS-IntId during Class creation
1526         ldif_add = ldif + "msDS-IntId: -1993108831\n"
1527         self.ldb.add_ldif(ldif_add)
1528         self._ldap_schemaUpdateNow()
1529
1530         res = self.ldb.search(class_dn, scope=SCOPE_BASE, attrs=["msDS-IntId"])
1531         self.assertEquals(len(res), 1)
1532         self.assertEquals(res[0]["msDS-IntId"][0], "-1993108831")
1533
1534         # add a new Class and update schema
1535         (class_name, class_ldap_name, class_dn) = self._make_obj_names("msDS-IntId-Class-2-")
1536         ldif = self._make_class_ldif(class_dn, class_name, 9)
1537
1538         self.ldb.add_ldif(ldif)
1539         self._ldap_schemaUpdateNow()
1540
1541         # Search for created Class
1542         res = self.ldb.search(class_dn, scope=SCOPE_BASE, attrs=["msDS-IntId"])
1543         self.assertEquals(len(res), 1)
1544         self.assertFalse("msDS-IntId" in res[0])
1545
1546         msg = Message()
1547         msg.dn = Dn(self.ldb, class_dn)
1548         msg["msDS-IntId"] = MessageElement("-1993108831", FLAG_MOD_REPLACE, "msDS-IntId")
1549         try:
1550             self.ldb.modify(msg)
1551             self.fail("Modifying msDS-IntId should return error")
1552         except LdbError as e29:
1553             (num, _) = e29.args
1554             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
1555
1556         # 2. Create Class with systemFlags = FLAG_SCHEMA_BASE_OBJECT
1557         # msDS-IntId should be created if forest functional
1558         # level is >= DS_DOMAIN_FUNCTION_2003
1559         # and missing otherwise
1560         (class_name, class_ldap_name, class_dn) = self._make_obj_names("msDS-IntId-Class-3-")
1561         ldif = self._make_class_ldif(class_dn, class_name, 10)
1562         ldif += "systemFlags: 16\n"
1563
1564         # try to add msDS-IntId during Class creation
1565         ldif_add = ldif + "msDS-IntId: -1993108831\n"
1566         self.ldb.add_ldif(ldif_add)
1567
1568         res = self.ldb.search(class_dn, scope=SCOPE_BASE, attrs=["msDS-IntId"])
1569         self.assertEquals(len(res), 1)
1570         self.assertEquals(res[0]["msDS-IntId"][0], "-1993108831")
1571
1572         # add the new Class and update schema
1573         (class_name, class_ldap_name, class_dn) = self._make_obj_names("msDS-IntId-Class-4-")
1574         ldif = self._make_class_ldif(class_dn, class_name, 11)
1575         ldif += "systemFlags: 16\n"
1576
1577         self.ldb.add_ldif(ldif)
1578         self._ldap_schemaUpdateNow()
1579
1580         # Search for created Class
1581         res = self.ldb.search(class_dn, scope=SCOPE_BASE, attrs=["msDS-IntId"])
1582         self.assertEquals(len(res), 1)
1583         self.assertFalse("msDS-IntId" in res[0])
1584
1585         msg = Message()
1586         msg.dn = Dn(self.ldb, class_dn)
1587         msg["msDS-IntId"] = MessageElement("-1993108831", FLAG_MOD_REPLACE, "msDS-IntId")
1588         try:
1589             self.ldb.modify(msg)
1590             self.fail("Modifying msDS-IntId should return error")
1591         except LdbError as e30:
1592             (num, _) = e30.args
1593             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
1594         res = self.ldb.search(class_dn, scope=SCOPE_BASE, attrs=["msDS-IntId"])
1595         self.assertEquals(len(res), 1)
1596         self.assertFalse("msDS-IntId" in res[0])
1597
1598
1599     def test_verify_msDS_IntId(self):
1600         """Verify msDS-IntId exists only on attributes without FLAG_SCHEMA_BASE_OBJECT flag set"""
1601         count = 0
1602         res = self.ldb.search(self.schema_dn, scope=SCOPE_ONELEVEL,
1603                               expression="objectClass=attributeSchema",
1604                               attrs=["systemFlags", "msDS-IntId", "attributeID", "cn"])
1605         self.assertTrue(len(res) > 1)
1606         for ldb_msg in res:
1607             if self.forest_level >= DS_DOMAIN_FUNCTION_2003:
1608                 if self._is_schema_base_object(ldb_msg):
1609                     self.assertTrue("msDS-IntId" not in ldb_msg)
1610                 else:
1611                     # don't assert here as there are plenty of
1612                     # attributes under w2k8 that are not part of
1613                     # Base Schema (SYSTEM_FLAG_SCHEMA_BASE_OBJECT flag not set)
1614                     # has not msDS-IntId attribute set
1615                     #self.assertTrue("msDS-IntId" in ldb_msg, "msDS-IntId expected on: %s" % ldb_msg.dn)
1616                     if "msDS-IntId" not in ldb_msg:
1617                         count = count + 1
1618                         print "%3d warning: msDS-IntId expected on: %-30s %s" % (count, ldb_msg["attributeID"], ldb_msg["cn"])
1619             else:
1620                 self.assertTrue("msDS-IntId" not in ldb_msg)
1621
1622
1623 class SchemaTests_msDS_isRODC(samba.tests.TestCase):
1624
1625     def setUp(self):
1626         super(SchemaTests_msDS_isRODC, self).setUp()
1627         self.ldb =  SamDB(host, credentials=creds,
1628             session_info=system_session(lp), lp=lp, options=ldb_options)
1629         res = self.ldb.search(base="", expression="", scope=SCOPE_BASE, attrs=["defaultNamingContext"])
1630         self.assertEquals(len(res), 1)
1631         self.base_dn = res[0]["defaultNamingContext"][0]
1632
1633     def test_objectClass_ntdsdsa(self):
1634         res = self.ldb.search(self.base_dn, expression="objectClass=nTDSDSA",
1635                               attrs=["msDS-isRODC"], controls=["search_options:1:2"])
1636         for ldb_msg in res:
1637             self.assertTrue("msDS-isRODC" in ldb_msg)
1638
1639     def test_objectClass_server(self):
1640         res = self.ldb.search(self.base_dn, expression="objectClass=server",
1641                               attrs=["msDS-isRODC"], controls=["search_options:1:2"])
1642         for ldb_msg in res:
1643             ntds_search_dn = "CN=NTDS Settings,%s" % ldb_msg['dn']
1644             try:
1645                 res_check = self.ldb.search(ntds_search_dn, attrs=["objectCategory"])
1646             except LdbError as e:
1647                 (num, _) = e.args
1648                 self.assertEquals(num, ERR_NO_SUCH_OBJECT)
1649                 print("Server entry %s doesn't have a NTDS settings object" % res[0]['dn'])
1650             else:
1651                 self.assertTrue("objectCategory" in res_check[0])
1652                 self.assertTrue("msDS-isRODC" in ldb_msg)
1653
1654     def test_objectClass_computer(self):
1655         res = self.ldb.search(self.base_dn, expression="objectClass=computer",
1656                               attrs=["serverReferenceBL","msDS-isRODC"], controls=["search_options:1:2"])
1657         for ldb_msg in res:
1658             if "serverReferenceBL" not in ldb_msg:
1659                 print("Computer entry %s doesn't have a serverReferenceBL attribute" % ldb_msg['dn'])
1660             else:
1661                 self.assertTrue("msDS-isRODC" in ldb_msg)
1662
1663 if not "://" in host:
1664     if os.path.isfile(host):
1665         host = "tdb://%s" % host
1666     else:
1667         host = "ldap://%s" % host
1668
1669 ldb_options = []
1670 if host.startswith("ldap://"):
1671     # user 'paged_search' module when connecting remotely
1672     ldb_options = ["modules:paged_searches"]
1673
1674 TestProgram(module=__name__, opts=subunitopts)