PY3: change shebang to python3 in source4/dsdb dir
[vlendec/samba-autobuild/.git] / source4 / dsdb / tests / python / ldap_schema.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 # 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 from __future__ import print_function
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(str(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(str(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, 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 classSchema
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(str(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     def test_duplicate_attributeID(self):
307         """Testing creating a duplicate attribute"""
308         rand = str(random.randint(1, 100000))
309         attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + "-" + rand
310         attr_ldap_display_name = attr_name.replace("-", "")
311         attributeID = "1.3.6.1.4.1.7165.4.6.1.6.2." + rand
312         ldif = """
313 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
314 objectClass: top
315 objectClass: attributeSchema
316 adminDescription: """ + attr_name + """
317 adminDisplayName: """ + attr_name + """
318 cn: """ + attr_name + """
319 attributeId: """ + attributeID + """
320 attributeSyntax: 2.5.5.12
321 omSyntax: 64
322 instanceType: 4
323 isSingleValued: TRUE
324 systemOnly: FALSE
325 """
326         self.ldb.add_ldif(ldif)
327
328         ldif = """
329 dn: CN=%s-dup,%s""" % (attr_name, self.schema_dn) + """
330 objectClass: top
331 objectClass: attributeSchema
332 adminDescription: """ + attr_name + """dup
333 adminDisplayName: """ + attr_name + """dup
334 cn: """ + attr_name + """-dup
335 attributeId: """ + attributeID + """
336 attributeSyntax: 2.5.5.12
337 omSyntax: 64
338 instanceType: 4
339 isSingleValued: TRUE
340 systemOnly: FALSE
341 """
342         try:
343             self.ldb.add_ldif(ldif)
344             self.fail("Should have failed to add duplicate attributeID value")
345         except LdbError as e2:
346             (enum, estr) = e2.args
347             self.assertEquals(enum, ERR_UNWILLING_TO_PERFORM)
348
349     def test_duplicate_attributeID_governsID(self):
350         """Testing creating a duplicate attribute and class"""
351         rand = str(random.randint(1, 100000))
352         attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + "-" + rand
353         attr_ldap_display_name = attr_name.replace("-", "")
354         attributeID = "1.3.6.1.4.1.7165.4.6.1.6.3." + rand
355         ldif = """
356 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
357 objectClass: top
358 objectClass: attributeSchema
359 adminDescription: """ + attr_name + """
360 adminDisplayName: """ + attr_name + """
361 cn: """ + attr_name + """
362 attributeId: """ + attributeID + """
363 attributeSyntax: 2.5.5.12
364 omSyntax: 64
365 instanceType: 4
366 isSingleValued: TRUE
367 systemOnly: FALSE
368 """
369         self.ldb.add_ldif(ldif)
370
371         ldif = """
372 dn: CN=%s-dup,%s""" % (attr_name, self.schema_dn) + """
373 objectClass: top
374 objectClass: classSchema
375 adminDescription: """ + attr_name + """dup
376 adminDisplayName: """ + attr_name + """dup
377 cn: """ + attr_name + """-dup
378 governsId: """ + attributeID + """
379 instanceType: 4
380 objectClassCategory: 1
381 subClassOf: organizationalPerson
382 rDNAttID: cn
383 systemMustContain: cn
384 systemOnly: FALSE
385 """
386         try:
387             self.ldb.add_ldif(ldif)
388             self.fail("Should have failed to add duplicate governsID conflicting with attributeID value")
389         except LdbError as e3:
390             (enum, estr) = e3.args
391             self.assertEquals(enum, ERR_UNWILLING_TO_PERFORM)
392
393     def test_duplicate_cn(self):
394         """Testing creating a duplicate attribute"""
395         rand = str(random.randint(1, 100000))
396         attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + "-" + rand
397         attr_ldap_display_name = attr_name.replace("-", "")
398         attributeID = "1.3.6.1.4.1.7165.4.6.1.6.4." + rand
399         ldif = """
400 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
401 objectClass: top
402 objectClass: attributeSchema
403 adminDescription: """ + attr_name + """
404 adminDisplayName: """ + attr_name + """
405 cn: """ + attr_name + """
406 attributeId: """ + attributeID + """
407 attributeSyntax: 2.5.5.12
408 omSyntax: 64
409 instanceType: 4
410 isSingleValued: TRUE
411 systemOnly: FALSE
412 """
413         self.ldb.add_ldif(ldif)
414
415         ldif = """
416 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
417 objectClass: top
418 objectClass: attributeSchema
419 adminDescription: """ + attr_name + """dup
420 adminDisplayName: """ + attr_name + """dup
421 cn: """ + attr_name + """
422 attributeId: """ + attributeID + """.1
423 attributeSyntax: 2.5.5.12
424 omSyntax: 64
425 instanceType: 4
426 isSingleValued: TRUE
427 systemOnly: FALSE
428 """
429         try:
430             self.ldb.add_ldif(ldif)
431             self.fail("Should have failed to add attribute with duplicate CN")
432         except LdbError as e4:
433             (enum, estr) = e4.args
434             self.assertEquals(enum, ERR_ENTRY_ALREADY_EXISTS)
435
436     def test_duplicate_implicit_ldapdisplayname(self):
437         """Testing creating a duplicate attribute ldapdisplayname"""
438         rand = str(random.randint(1, 100000))
439         attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + "-" + rand
440         attr_ldap_display_name = attr_name.replace("-", "")
441         attributeID = "1.3.6.1.4.1.7165.4.6.1.6.5." + rand
442         ldif = """
443 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
444 objectClass: top
445 objectClass: attributeSchema
446 adminDescription: """ + attr_name + """
447 adminDisplayName: """ + attr_name + """
448 cn: """ + attr_name + """
449 attributeId: """ + attributeID + """
450 attributeSyntax: 2.5.5.12
451 omSyntax: 64
452 instanceType: 4
453 isSingleValued: TRUE
454 systemOnly: FALSE
455 """
456         self.ldb.add_ldif(ldif)
457
458         ldif = """
459 dn: CN=%s-dup,%s""" % (attr_name, self.schema_dn) + """
460 objectClass: top
461 objectClass: attributeSchema
462 adminDescription: """ + attr_name + """dup
463 adminDisplayName: """ + attr_name + """dup
464 cn: """ + attr_name + """-dup
465 ldapDisplayName: """ + attr_ldap_display_name + """
466 attributeId: """ + attributeID + """.1
467 attributeSyntax: 2.5.5.12
468 omSyntax: 64
469 instanceType: 4
470 isSingleValued: TRUE
471 systemOnly: FALSE
472 """
473         try:
474             self.ldb.add_ldif(ldif)
475             self.fail("Should have failed to add attribute with duplicate of the implicit ldapDisplayName")
476         except LdbError as e5:
477             (enum, estr) = e5.args
478             self.assertEquals(enum, ERR_UNWILLING_TO_PERFORM)
479
480     def test_duplicate_explicit_ldapdisplayname(self):
481         """Testing creating a duplicate attribute ldapdisplayname"""
482         rand = str(random.randint(1, 100000))
483         attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + "-" + rand
484         attr_ldap_display_name = attr_name.replace("-", "")
485         attributeID = "1.3.6.1.4.1.7165.4.6.1.6.6." + rand
486         ldif = """
487 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
488 objectClass: top
489 objectClass: attributeSchema
490 adminDescription: """ + attr_name + """
491 adminDisplayName: """ + attr_name + """
492 cn: """ + attr_name + """
493 attributeId: """ + attributeID + """
494 attributeSyntax: 2.5.5.12
495 ldapDisplayName: """ + attr_ldap_display_name + """
496 omSyntax: 64
497 instanceType: 4
498 isSingleValued: TRUE
499 systemOnly: FALSE
500 """
501         self.ldb.add_ldif(ldif)
502
503         ldif = """
504 dn: CN=%s-dup,%s""" % (attr_name, self.schema_dn) + """
505 objectClass: top
506 objectClass: attributeSchema
507 adminDescription: """ + attr_name + """dup
508 adminDisplayName: """ + attr_name + """dup
509 cn: """ + attr_name + """-dup
510 ldapDisplayName: """ + attr_ldap_display_name + """
511 attributeId: """ + attributeID + """.1
512 attributeSyntax: 2.5.5.12
513 omSyntax: 64
514 instanceType: 4
515 isSingleValued: TRUE
516 systemOnly: FALSE
517 """
518         try:
519             self.ldb.add_ldif(ldif)
520             self.fail("Should have failed to add attribute with duplicate ldapDisplayName")
521         except LdbError as e6:
522             (enum, estr) = e6.args
523             self.assertEquals(enum, ERR_UNWILLING_TO_PERFORM)
524
525     def test_duplicate_explicit_ldapdisplayname_with_class(self):
526         """Testing creating a duplicate attribute ldapdisplayname between
527         and attribute and a class"""
528         rand = str(random.randint(1, 100000))
529         attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + "-" + rand
530         attr_ldap_display_name = attr_name.replace("-", "")
531         attributeID = "1.3.6.1.4.1.7165.4.6.1.6.7." + rand
532         governsID   = "1.3.6.1.4.1.7165.4.6.2.6.4." + rand
533         ldif = """
534 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
535 objectClass: top
536 objectClass: attributeSchema
537 adminDescription: """ + attr_name + """
538 adminDisplayName: """ + attr_name + """
539 cn: """ + attr_name + """
540 attributeId: """ + attributeID + """
541 attributeSyntax: 2.5.5.12
542 ldapDisplayName: """ + attr_ldap_display_name + """
543 omSyntax: 64
544 instanceType: 4
545 isSingleValued: TRUE
546 systemOnly: FALSE
547 """
548         self.ldb.add_ldif(ldif)
549
550         ldif = """
551 dn: CN=%s-dup,%s""" % (attr_name, self.schema_dn) + """
552 objectClass: top
553 objectClass: classSchema
554 adminDescription: """ + attr_name + """dup
555 adminDisplayName: """ + attr_name + """dup
556 cn: """ + attr_name + """-dup
557 ldapDisplayName: """ + attr_ldap_display_name + """
558 governsID: """ + governsID + """
559 instanceType: 4
560 objectClassCategory: 1
561 subClassOf: organizationalPerson
562 rDNAttID: cn
563 systemMustContain: cn
564 systemOnly: FALSE
565 """
566         try:
567             self.ldb.add_ldif(ldif)
568             self.fail("Should have failed to add class with duplicate ldapDisplayName")
569         except LdbError as e7:
570             (enum, estr) = e7.args
571             self.assertEquals(enum, ERR_UNWILLING_TO_PERFORM)
572
573     def test_duplicate_via_rename_ldapdisplayname(self):
574         """Testing creating a duplicate attribute ldapdisplayname"""
575         rand = str(random.randint(1, 100000))
576         attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + "-" + rand
577         attr_ldap_display_name = attr_name.replace("-", "")
578         attributeID = "1.3.6.1.4.1.7165.4.6.1.6.8." + rand
579         ldif = """
580 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
581 objectClass: top
582 objectClass: attributeSchema
583 adminDescription: """ + attr_name + """
584 adminDisplayName: """ + attr_name + """
585 cn: """ + attr_name + """
586 attributeId: """ + attributeID + """
587 attributeSyntax: 2.5.5.12
588 ldapDisplayName: """ + attr_ldap_display_name + """
589 omSyntax: 64
590 instanceType: 4
591 isSingleValued: TRUE
592 systemOnly: FALSE
593 """
594         self.ldb.add_ldif(ldif)
595
596         ldif = """
597 dn: CN=%s-dup,%s""" % (attr_name, self.schema_dn) + """
598 objectClass: top
599 objectClass: attributeSchema
600 adminDescription: """ + attr_name + """dup
601 adminDisplayName: """ + attr_name + """dup
602 cn: """ + attr_name + """-dup
603 ldapDisplayName: """ + attr_ldap_display_name + """dup
604 attributeId: """ + attributeID + """.1
605 attributeSyntax: 2.5.5.12
606 omSyntax: 64
607 instanceType: 4
608 isSingleValued: TRUE
609 systemOnly: FALSE
610 """
611         self.ldb.add_ldif(ldif)
612
613         ldif = """
614 dn: CN=%s-dup,%s""" % (attr_name, self.schema_dn) + """
615 changetype: modify
616 replace: ldapDisplayName
617 ldapDisplayName: """ + attr_ldap_display_name + """
618 -
619 """
620         try:
621             self.ldb.modify_ldif(ldif)
622             self.fail("Should have failed to modify schema to have attribute with duplicate ldapDisplayName")
623         except LdbError as e8:
624             (enum, estr) = e8.args
625             self.assertEquals(enum, ERR_UNWILLING_TO_PERFORM)
626
627     def test_duplicate_via_rename_attributeID(self):
628         """Testing creating a duplicate attributeID"""
629         rand = str(random.randint(1, 100000))
630         attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + "-" + rand
631         attr_ldap_display_name = attr_name.replace("-", "")
632         attributeID = "1.3.6.1.4.1.7165.4.6.1.6.9." + rand
633         ldif = """
634 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
635 objectClass: top
636 objectClass: attributeSchema
637 adminDescription: """ + attr_name + """
638 adminDisplayName: """ + attr_name + """
639 cn: """ + attr_name + """
640 attributeId: """ + attributeID + """
641 attributeSyntax: 2.5.5.12
642 ldapDisplayName: """ + attr_ldap_display_name + """
643 omSyntax: 64
644 instanceType: 4
645 isSingleValued: TRUE
646 systemOnly: FALSE
647 """
648         self.ldb.add_ldif(ldif)
649
650         ldif = """
651 dn: CN=%s-dup,%s""" % (attr_name, self.schema_dn) + """
652 objectClass: top
653 objectClass: attributeSchema
654 adminDescription: """ + attr_name + """dup
655 adminDisplayName: """ + attr_name + """dup
656 cn: """ + attr_name + """-dup
657 ldapDisplayName: """ + attr_ldap_display_name + """dup
658 attributeId: """ + attributeID + """.1
659 attributeSyntax: 2.5.5.12
660 omSyntax: 64
661 instanceType: 4
662 isSingleValued: TRUE
663 systemOnly: FALSE
664 """
665         self.ldb.add_ldif(ldif)
666
667         ldif = """
668 dn: CN=%s-dup,%s""" % (attr_name, self.schema_dn) + """
669 changetype: modify
670 replace: attributeId
671 attributeId: """ + attributeID + """
672 -
673 """
674         try:
675             self.ldb.modify_ldif(ldif)
676             self.fail("Should have failed to modify schema to have attribute with duplicate attributeID")
677         except LdbError as e9:
678             (enum, estr) = e9.args
679             self.assertEquals(enum, ERR_CONSTRAINT_VIOLATION)
680
681     def test_remove_ldapdisplayname(self):
682         """Testing removing the ldapdisplayname"""
683         rand = str(random.randint(1, 100000))
684         attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + "-" + rand
685         attr_ldap_display_name = attr_name.replace("-", "")
686         attributeID = "1.3.6.1.4.1.7165.4.6.1.6.10." + rand
687         ldif = """
688 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
689 objectClass: top
690 objectClass: attributeSchema
691 adminDescription: """ + attr_name + """
692 adminDisplayName: """ + attr_name + """
693 cn: """ + attr_name + """
694 attributeId: """ + attributeID + """
695 attributeSyntax: 2.5.5.12
696 ldapDisplayName: """ + attr_ldap_display_name + """
697 omSyntax: 64
698 instanceType: 4
699 isSingleValued: TRUE
700 systemOnly: FALSE
701 """
702         self.ldb.add_ldif(ldif)
703
704         ldif = """
705 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
706 changetype: modify
707 replace: ldapDisplayName
708 -
709 """
710         try:
711             self.ldb.modify_ldif(ldif)
712             self.fail("Should have failed to remove the ldapdisplayname")
713         except LdbError as e10:
714             (enum, estr) = e10.args
715             self.assertEquals(enum, ERR_OBJECT_CLASS_VIOLATION)
716
717     def test_rename_ldapdisplayname(self):
718         """Testing renaming ldapdisplayname"""
719         rand = str(random.randint(1, 100000))
720         attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + "-" + rand
721         attr_ldap_display_name = attr_name.replace("-", "")
722         attributeID = "1.3.6.1.4.1.7165.4.6.1.6.11." + rand
723         ldif = """
724 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
725 objectClass: top
726 objectClass: attributeSchema
727 adminDescription: """ + attr_name + """
728 adminDisplayName: """ + attr_name + """
729 cn: """ + attr_name + """
730 attributeId: """ + attributeID + """
731 attributeSyntax: 2.5.5.12
732 ldapDisplayName: """ + attr_ldap_display_name + """
733 omSyntax: 64
734 instanceType: 4
735 isSingleValued: TRUE
736 systemOnly: FALSE
737 """
738         self.ldb.add_ldif(ldif)
739
740         ldif = """
741 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
742 changetype: modify
743 replace: ldapDisplayName
744 ldapDisplayName: """ + attr_ldap_display_name + """2
745 -
746 """
747         self.ldb.modify_ldif(ldif)
748
749     def test_change_attributeID(self):
750         """Testing change the attributeID"""
751         rand = str(random.randint(1, 100000))
752         attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + "-" + rand
753         attr_ldap_display_name = attr_name.replace("-", "")
754         attributeID = "1.3.6.1.4.1.7165.4.6.1.6.12." + rand
755         ldif = """
756 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
757 objectClass: top
758 objectClass: attributeSchema
759 adminDescription: """ + attr_name + """
760 adminDisplayName: """ + attr_name + """
761 cn: """ + attr_name + """
762 attributeId: """ + attributeID + """
763 attributeSyntax: 2.5.5.12
764 ldapDisplayName: """ + attr_ldap_display_name + """
765 omSyntax: 64
766 instanceType: 4
767 isSingleValued: TRUE
768 systemOnly: FALSE
769 """
770         self.ldb.add_ldif(ldif)
771
772         ldif = """
773 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
774 changetype: modify
775 replace: attributeID
776 attributeId: """ + attributeID + """.1
777
778 """
779         try:
780             self.ldb.modify_ldif(ldif)
781             self.fail("Should have failed to modify schema to have different attributeID")
782         except LdbError as e11:
783             (enum, estr) = e11.args
784             self.assertEquals(enum, ERR_CONSTRAINT_VIOLATION)
785
786     def test_change_attributeID_same(self):
787         """Testing change the attributeID to the same value"""
788         rand = str(random.randint(1, 100000))
789         attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + "-" + rand
790         attr_ldap_display_name = attr_name.replace("-", "")
791         attributeID = "1.3.6.1.4.1.7165.4.6.1.6.13." + rand
792         ldif = """
793 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
794 objectClass: top
795 objectClass: attributeSchema
796 adminDescription: """ + attr_name + """
797 adminDisplayName: """ + attr_name + """
798 cn: """ + attr_name + """
799 attributeId: """ + attributeID + """
800 attributeSyntax: 2.5.5.12
801 ldapDisplayName: """ + attr_ldap_display_name + """
802 omSyntax: 64
803 instanceType: 4
804 isSingleValued: TRUE
805 systemOnly: FALSE
806 """
807         self.ldb.add_ldif(ldif)
808
809         ldif = """
810 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
811 changetype: modify
812 replace: attributeID
813 attributeId: """ + attributeID + """
814
815 """
816         try:
817             self.ldb.modify_ldif(ldif)
818             self.fail("Should have failed to modify schema to have the same attributeID")
819         except LdbError as e12:
820             (enum, estr) = e12.args
821             self.assertEquals(enum, ERR_CONSTRAINT_VIOLATION)
822
823     def test_generated_linkID(self):
824         """
825         Test that we automatically generate a linkID if the
826         OID "1.2.840.113556.1.2.50" is given as the linkID
827         of a new attribute, and that we don't get/can't add
828         duplicate linkIDs. Also test that we can add a backlink
829         by providing the attributeID or ldapDisplayName of
830         a forwards link in the linkID attribute.
831         """
832
833         # linkID generation isn't available before 2003
834         res = self.ldb.search(base="", expression="", scope=SCOPE_BASE,
835                               attrs=["domainControllerFunctionality"])
836         self.assertEquals(len(res), 1)
837         dc_level = int(res[0]["domainControllerFunctionality"][0])
838         if dc_level < DS_DOMAIN_FUNCTION_2003:
839             return
840
841         rand = str(random.randint(1, 100000))
842
843         attr_name_1 = "test-generated-linkID" + time.strftime("%s", time.gmtime()) + "-" + rand
844         attr_ldap_display_name_1 = attr_name_1.replace("-", "")
845         attributeID_1 = "1.3.6.1.4.1.7165.4.6.1.6.16." + rand
846         ldif = """
847 dn: CN=%s,%s""" % (attr_name_1, self.schema_dn) + """
848 objectClass: top
849 objectClass: attributeSchema
850 adminDescription: """ + attr_name_1 + """
851 adminDisplayName: """ + attr_name_1 + """
852 cn: """ + attr_name_1 + """
853 attributeId: """ + attributeID_1 + """
854 linkID: 1.2.840.113556.1.2.50
855 attributeSyntax: 2.5.5.1
856 ldapDisplayName: """ + attr_ldap_display_name_1 + """
857 omSyntax: 127
858 instanceType: 4
859 isSingleValued: TRUE
860 systemOnly: FALSE
861 """
862
863         try:
864             self.ldb.add_ldif(ldif)
865         except LdbError as e13:
866             (enum, estr) = e13.args
867             self.fail(estr)
868
869         attr_name_2 = "test-generated-linkID-2" + time.strftime("%s", time.gmtime()) + "-" + rand
870         attr_ldap_display_name_2 = attr_name_2.replace("-", "")
871         attributeID_2 = "1.3.6.1.4.1.7165.4.6.1.6.17." + rand
872         ldif = """
873 dn: CN=%s,%s""" % (attr_name_2, self.schema_dn) + """
874 objectClass: top
875 objectClass: attributeSchema
876 adminDescription: """ + attr_name_2 + """
877 adminDisplayName: """ + attr_name_2 + """
878 cn: """ + attr_name_2 + """
879 attributeId: """ + attributeID_2 + """
880 linkID: 1.2.840.113556.1.2.50
881 attributeSyntax: 2.5.5.1
882 ldapDisplayName: """ + attr_ldap_display_name_2 + """
883 omSyntax: 127
884 instanceType: 4
885 isSingleValued: TRUE
886 systemOnly: FALSE
887 """
888
889         try:
890             self.ldb.add_ldif(ldif)
891         except LdbError as e14:
892             (enum, estr) = e14.args
893             self.fail(estr)
894
895         res = self.ldb.search("CN=%s,%s" % (attr_name_1, self.schema_dn),
896                               scope=SCOPE_BASE,
897                               attrs=["linkID"])
898         self.assertEquals(len(res), 1)
899         linkID_1 = int(res[0]["linkID"][0])
900
901         res = self.ldb.search("CN=%s,%s" % (attr_name_2, self.schema_dn),
902                               scope=SCOPE_BASE,
903                               attrs=["linkID"])
904         self.assertEquals(len(res), 1)
905         linkID_2 = int(res[0]["linkID"][0])
906
907         # 0 should never be generated as a linkID
908         self.assertFalse(linkID_1 == 0)
909         self.assertFalse(linkID_2 == 0)
910
911         # The generated linkID should always be even, because
912         # it should assume we're adding a forward link.
913         self.assertTrue(linkID_1 % 2 == 0)
914         self.assertTrue(linkID_2 % 2 == 0)
915
916         self.assertFalse(linkID_1 == linkID_2)
917
918         # This is only necessary against Windows, since we depend
919         # on the previously added links in the next ones and Windows
920         # won't refresh the schema as we add them.
921         ldif = """
922 dn:
923 changetype: modify
924 replace: schemaupdatenow
925 schemaupdatenow: 1
926 """
927         self.ldb.modify_ldif(ldif)
928
929         # If we add a new link with the same linkID, it should fail
930         attr_name = "test-generated-linkID-duplicate" + time.strftime("%s", time.gmtime()) + "-" + rand
931         attr_ldap_display_name = attr_name.replace("-", "")
932         attributeID = "1.3.6.1.4.1.7165.4.6.1.6.18." + rand
933         ldif = """
934 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
935 objectClass: top
936 objectClass: attributeSchema
937 adminDescription: """ + attr_name + """
938 adminDisplayName: """ + attr_name + """
939 cn: """ + attr_name + """
940 attributeId: """ + attributeID + """
941 linkID: """ + str(linkID_1) + """
942 attributeSyntax: 2.5.5.1
943 ldapDisplayName: """ + attr_ldap_display_name + """
944 omSyntax: 127
945 instanceType: 4
946 isSingleValued: TRUE
947 systemOnly: FALSE
948 """
949
950         try:
951             self.ldb.add_ldif(ldif)
952             self.fail("Should have failed to add duplicate linkID value")
953         except LdbError as e15:
954             (enum, estr) = e15.args
955             self.assertEquals(enum, ERR_UNWILLING_TO_PERFORM)
956
957         # If we add another attribute with the attributeID or lDAPDisplayName
958         # of a forward link in its linkID field, it should add as a backlink
959
960         attr_name_3 = "test-generated-linkID-backlink" + time.strftime("%s", time.gmtime()) + "-" + rand
961         attr_ldap_display_name_3 = attr_name_3.replace("-", "")
962         attributeID_3 = "1.3.6.1.4.1.7165.4.6.1.6.19." + rand
963         ldif = """
964 dn: CN=%s,%s""" % (attr_name_3, self.schema_dn) + """
965 objectClass: top
966 objectClass: attributeSchema
967 adminDescription: """ + attr_name_3 + """
968 adminDisplayName: """ + attr_name_3 + """
969 cn: """ + attr_name_3 + """
970 attributeId: """ + attributeID_3 + """
971 linkID: """ + str(linkID_1 + 1) + """
972 attributeSyntax: 2.5.5.1
973 ldapDisplayName: """ + attr_ldap_display_name_3 + """
974 omSyntax: 127
975 instanceType: 4
976 isSingleValued: TRUE
977 systemOnly: FALSE
978 """
979
980         try:
981             self.ldb.add_ldif(ldif)
982         except LdbError as e16:
983             (enum, estr) = e16.args
984             self.fail(estr)
985
986         res = self.ldb.search("CN=%s,%s" % (attr_name_3, self.schema_dn),
987                               scope=SCOPE_BASE,
988                               attrs=["linkID"])
989         self.assertEquals(len(res), 1)
990         linkID = int(res[0]["linkID"][0])
991         self.assertEquals(linkID, linkID_1 + 1)
992
993         attr_name_4 = "test-generated-linkID-backlink-2" + time.strftime("%s", time.gmtime()) + "-" + rand
994         attr_ldap_display_name_4 = attr_name_4.replace("-", "")
995         attributeID_4 = "1.3.6.1.4.1.7165.4.6.1.6.20." + rand
996         ldif = """
997 dn: CN=%s,%s""" % (attr_name_4, self.schema_dn) + """
998 objectClass: top
999 objectClass: attributeSchema
1000 adminDescription: """ + attr_name_4 + """
1001 adminDisplayName: """ + attr_name_4 + """
1002 cn: """ + attr_name_4 + """
1003 attributeId: """ + attributeID_4 + """
1004 linkID: """ + attr_ldap_display_name_2 + """
1005 attributeSyntax: 2.5.5.1
1006 ldapDisplayName: """ + attr_ldap_display_name_4 + """
1007 omSyntax: 127
1008 instanceType: 4
1009 isSingleValued: TRUE
1010 systemOnly: FALSE
1011 """
1012
1013         try:
1014             self.ldb.add_ldif(ldif)
1015         except LdbError as e17:
1016             (enum, estr) = e17.args
1017             self.fail(estr)
1018
1019         res = self.ldb.search("CN=%s,%s" % (attr_name_4, self.schema_dn),
1020                               scope=SCOPE_BASE,
1021                               attrs=["linkID"])
1022         self.assertEquals(len(res), 1)
1023         linkID = int(res[0]["linkID"][0])
1024         self.assertEquals(linkID, linkID_2 + 1)
1025
1026         # If we then try to add another backlink in the same way
1027         # for the same forwards link, we should fail.
1028
1029         attr_name = "test-generated-linkID-backlink-duplicate" + time.strftime("%s", time.gmtime()) + "-" + rand
1030         attr_ldap_display_name = attr_name.replace("-", "")
1031         attributeID = "1.3.6.1.4.1.7165.4.6.1.6.21." + rand
1032         ldif = """
1033 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
1034 objectClass: top
1035 objectClass: attributeSchema
1036 adminDescription: """ + attr_name + """
1037 adminDisplayName: """ + attr_name + """
1038 cn: """ + attr_name + """
1039 attributeId: """ + attributeID + """
1040 linkID: """ + attributeID_1 + """
1041 attributeSyntax: 2.5.5.1
1042 ldapDisplayName: """ + attr_ldap_display_name + """
1043 omSyntax: 127
1044 instanceType: 4
1045 isSingleValued: TRUE
1046 systemOnly: FALSE
1047 """
1048
1049         try:
1050             self.ldb.add_ldif(ldif)
1051             self.fail("Should have failed to add duplicate backlink")
1052         except LdbError as e18:
1053             (enum, estr) = e18.args
1054             self.assertEquals(enum, ERR_UNWILLING_TO_PERFORM)
1055
1056         # If we try to supply the attributeID or ldapDisplayName
1057         # of an existing backlink in the linkID field of a new link,
1058         # it should fail.
1059
1060         attr_name = "test-generated-linkID-backlink-invalid" + time.strftime("%s", time.gmtime()) + "-" + rand
1061         attr_ldap_display_name = attr_name.replace("-", "")
1062         attributeID = "1.3.6.1.4.1.7165.4.6.1.6.22." + rand
1063         ldif = """
1064 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
1065 objectClass: top
1066 objectClass: attributeSchema
1067 adminDescription: """ + attr_name + """
1068 adminDisplayName: """ + attr_name + """
1069 cn: """ + attr_name + """
1070 attributeId: """ + attributeID + """
1071 linkID: """ + attributeID_3 + """
1072 attributeSyntax: 2.5.5.1
1073 ldapDisplayName: """ + attr_ldap_display_name + """
1074 omSyntax: 127
1075 instanceType: 4
1076 isSingleValued: TRUE
1077 systemOnly: FALSE
1078 """
1079
1080         try:
1081             self.ldb.add_ldif(ldif)
1082             self.fail("Should have failed to add backlink of backlink")
1083         except LdbError as e19:
1084             (enum, estr) = e19.args
1085             self.assertEquals(enum, ERR_UNWILLING_TO_PERFORM)
1086
1087         attr_name = "test-generated-linkID-backlink-invalid-2" + time.strftime("%s", time.gmtime()) + "-" + rand
1088         attr_ldap_display_name = attr_name.replace("-", "")
1089         attributeID = "1.3.6.1.4.1.7165.4.6.1.6.23." + rand
1090         ldif = """
1091 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
1092 objectClass: top
1093 objectClass: attributeSchema
1094 adminDescription: """ + attr_name + """
1095 adminDisplayName: """ + attr_name + """
1096 cn: """ + attr_name + """
1097 attributeId: """ + attributeID + """
1098 linkID: """ + attr_ldap_display_name_4 + """
1099 attributeSyntax: 2.5.5.1
1100 ldapDisplayName: """ + attr_ldap_display_name + """
1101 omSyntax: 127
1102 instanceType: 4
1103 isSingleValued: TRUE
1104 systemOnly: FALSE
1105 """
1106
1107         try:
1108             self.ldb.add_ldif(ldif)
1109             self.fail("Should have failed to add backlink of backlink")
1110         except LdbError as e20:
1111             (enum, estr) = e20.args
1112             self.assertEquals(enum, ERR_UNWILLING_TO_PERFORM)
1113
1114     def test_generated_mAPIID(self):
1115         """
1116         Test that we automatically generate a mAPIID if the
1117         OID "1.2.840.113556.1.2.49" is given as the mAPIID
1118         of a new attribute, and that we don't get/can't add
1119         duplicate mAPIIDs.
1120         """
1121
1122         rand = str(random.randint(1, 100000))
1123
1124         attr_name_1 = "test-generated-mAPIID" + time.strftime("%s", time.gmtime()) + "-" + rand
1125         attr_ldap_display_name_1 = attr_name_1.replace("-", "")
1126         attributeID_1 = "1.3.6.1.4.1.7165.4.6.1.6.24." + rand
1127         ldif = """
1128 dn: CN=%s,%s""" % (attr_name_1, self.schema_dn) + """
1129 objectClass: top
1130 objectClass: attributeSchema
1131 adminDescription: """ + attr_name_1 + """
1132 adminDisplayName: """ + attr_name_1 + """
1133 cn: """ + attr_name_1 + """
1134 attributeId: """ + attributeID_1 + """
1135 mAPIID: 1.2.840.113556.1.2.49
1136 attributeSyntax: 2.5.5.1
1137 ldapDisplayName: """ + attr_ldap_display_name_1 + """
1138 omSyntax: 127
1139 instanceType: 4
1140 isSingleValued: TRUE
1141 systemOnly: FALSE
1142 """
1143
1144         try:
1145             self.ldb.add_ldif(ldif)
1146         except LdbError as e21:
1147             (enum, estr) = e21.args
1148             self.fail(estr)
1149
1150         res = self.ldb.search("CN=%s,%s" % (attr_name_1, self.schema_dn),
1151                               scope=SCOPE_BASE,
1152                               attrs=["mAPIID"])
1153         self.assertEquals(len(res), 1)
1154         mAPIID_1 = int(res[0]["mAPIID"][0])
1155
1156         ldif = """
1157 dn:
1158 changetype: modify
1159 replace: schemaupdatenow
1160 schemaupdatenow: 1
1161 """
1162         self.ldb.modify_ldif(ldif)
1163
1164         # If we add a new attribute with the same mAPIID, it should fail
1165         attr_name = "test-generated-mAPIID-duplicate" + time.strftime("%s", time.gmtime()) + "-" + rand
1166         attr_ldap_display_name = attr_name.replace("-", "")
1167         attributeID = "1.3.6.1.4.1.7165.4.6.1.6.25." + rand
1168         ldif = """
1169 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
1170 objectClass: top
1171 objectClass: attributeSchema
1172 adminDescription: """ + attr_name + """
1173 adminDisplayName: """ + attr_name + """
1174 cn: """ + attr_name + """
1175 attributeId: """ + attributeID + """
1176 mAPIID: """ + str(mAPIID_1) + """
1177 attributeSyntax: 2.5.5.1
1178 ldapDisplayName: """ + attr_ldap_display_name + """
1179 omSyntax: 127
1180 instanceType: 4
1181 isSingleValued: TRUE
1182 systemOnly: FALSE
1183 """
1184
1185         try:
1186             self.ldb.add_ldif(ldif)
1187             self.fail("Should have failed to add duplicate mAPIID value")
1188         except LdbError as e22:
1189             (enum, estr) = e22.args
1190             self.assertEquals(enum, ERR_UNWILLING_TO_PERFORM)
1191
1192     def test_change_governsID(self):
1193         """Testing change the governsID"""
1194         rand = str(random.randint(1, 100000))
1195         class_name = "test-Class" + time.strftime("%s", time.gmtime()) + "-" + rand
1196         class_ldap_display_name = class_name.replace("-", "")
1197         governsID = "1.3.6.1.4.1.7165.4.6.2.6.5." + rand
1198         ldif = """
1199 dn: CN=%s,%s""" % (class_name, self.schema_dn) + """
1200 objectClass: top
1201 objectClass: classSchema
1202 adminDescription: """ + class_name + """
1203 adminDisplayName: """ + class_name + """
1204 cn: """ + class_name + """
1205 governsId: """ + governsID + """
1206 ldapDisplayName: """ + class_ldap_display_name + """
1207 instanceType: 4
1208 objectClassCategory: 1
1209 subClassOf: organizationalPerson
1210 rDNAttID: cn
1211 systemMustContain: cn
1212 systemOnly: FALSE
1213 """
1214         self.ldb.add_ldif(ldif)
1215
1216         ldif = """
1217 dn: CN=%s,%s""" % (class_name, self.schema_dn) + """
1218 changetype: modify
1219 replace: governsID
1220 governsId: """ + governsID + """.1
1221
1222 """
1223         try:
1224             self.ldb.modify_ldif(ldif)
1225             self.fail("Should have failed to modify schema to have different governsID")
1226         except LdbError as e23:
1227             (enum, estr) = e23.args
1228             self.assertEquals(enum, ERR_CONSTRAINT_VIOLATION)
1229
1230     def test_change_governsID_same(self):
1231         """Testing change the governsID"""
1232         rand = str(random.randint(1, 100000))
1233         class_name = "test-Class" + time.strftime("%s", time.gmtime()) + "-" + rand
1234         class_ldap_display_name = class_name.replace("-", "")
1235         governsID = "1.3.6.1.4.1.7165.4.6.2.6.6." + rand
1236         ldif = """
1237 dn: CN=%s,%s""" % (class_name, self.schema_dn) + """
1238 objectClass: top
1239 objectClass: classSchema
1240 adminDescription: """ + class_name + """
1241 adminDisplayName: """ + class_name + """
1242 cn: """ + class_name + """
1243 governsId: """ + governsID + """
1244 ldapDisplayName: """ + class_ldap_display_name + """
1245 instanceType: 4
1246 objectClassCategory: 1
1247 subClassOf: organizationalPerson
1248 rDNAttID: cn
1249 systemMustContain: cn
1250 systemOnly: FALSE
1251 """
1252         self.ldb.add_ldif(ldif)
1253
1254         ldif = """
1255 dn: CN=%s,%s""" % (class_name, self.schema_dn) + """
1256 changetype: modify
1257 replace: governsID
1258 governsId: """ + governsID + """.1
1259
1260 """
1261         try:
1262             self.ldb.modify_ldif(ldif)
1263             self.fail("Should have failed to modify schema to have the same governsID")
1264         except LdbError as e24:
1265             (enum, estr) = e24.args
1266             self.assertEquals(enum, ERR_CONSTRAINT_VIOLATION)
1267
1268
1269 class SchemaTests_msDS_IntId(samba.tests.TestCase):
1270
1271     def setUp(self):
1272         super(SchemaTests_msDS_IntId, self).setUp()
1273         self.ldb = SamDB(host, credentials=creds,
1274                          session_info=system_session(lp), lp=lp, options=ldb_options)
1275         res = self.ldb.search(base="", expression="", scope=SCOPE_BASE,
1276                               attrs=["schemaNamingContext", "defaultNamingContext",
1277                                      "forestFunctionality"])
1278         self.assertEquals(len(res), 1)
1279         self.schema_dn = res[0]["schemaNamingContext"][0]
1280         self.base_dn = res[0]["defaultNamingContext"][0]
1281         self.forest_level = int(res[0]["forestFunctionality"][0])
1282
1283     def _ldap_schemaUpdateNow(self):
1284         ldif = """
1285 dn:
1286 changetype: modify
1287 add: schemaUpdateNow
1288 schemaUpdateNow: 1
1289 """
1290         self.ldb.modify_ldif(ldif)
1291
1292     def _make_obj_names(self, prefix):
1293         class_name = prefix + time.strftime("%s", time.gmtime())
1294         class_ldap_name = class_name.replace("-", "")
1295         class_dn = "CN=%s,%s" % (class_name, self.schema_dn)
1296         return (class_name, class_ldap_name, class_dn)
1297
1298     def _is_schema_base_object(self, ldb_msg):
1299         """Test systemFlags for SYSTEM_FLAG_SCHEMA_BASE_OBJECT (16)"""
1300         systemFlags = 0
1301         if "systemFlags" in ldb_msg:
1302             systemFlags = int(ldb_msg["systemFlags"][0])
1303         return (systemFlags & 16) != 0
1304
1305     def _make_attr_ldif(self, attr_name, attr_dn):
1306         ldif = """
1307 dn: """ + attr_dn + """
1308 objectClass: top
1309 objectClass: attributeSchema
1310 adminDescription: """ + attr_name + """
1311 adminDisplayName: """ + attr_name + """
1312 cn: """ + attr_name + """
1313 attributeId: 1.3.6.1.4.1.7165.4.6.1.6.14.""" + str(random.randint(1, 100000)) + """
1314 attributeSyntax: 2.5.5.12
1315 omSyntax: 64
1316 instanceType: 4
1317 isSingleValued: TRUE
1318 systemOnly: FALSE
1319 """
1320         return ldif
1321
1322     def test_msDS_IntId_on_attr(self):
1323         """Testing msDs-IntId creation for Attributes.
1324         See MS-ADTS - 3.1.1.Attributes
1325
1326         This test should verify that:
1327         - Creating attribute with 'msDS-IntId' fails with ERR_UNWILLING_TO_PERFORM
1328         - Adding 'msDS-IntId' on existing attribute fails with ERR_CONSTRAINT_VIOLATION
1329         - Creating attribute with 'msDS-IntId' set and FLAG_SCHEMA_BASE_OBJECT flag
1330           set fails with ERR_UNWILLING_TO_PERFORM
1331         - Attributes created with FLAG_SCHEMA_BASE_OBJECT not set have
1332           'msDS-IntId' attribute added internally
1333         """
1334
1335         # 1. Create attribute without systemFlags
1336         # msDS-IntId should be created if forest functional
1337         # level is >= DS_DOMAIN_FUNCTION_2003
1338         # and missing otherwise
1339         (attr_name, attr_ldap_name, attr_dn) = self._make_obj_names("msDS-IntId-Attr-1-")
1340         ldif = self._make_attr_ldif(attr_name, attr_dn)
1341
1342         # try to add msDS-IntId during Attribute creation
1343         ldif_fail = ldif + "msDS-IntId: -1993108831\n"
1344         try:
1345             self.ldb.add_ldif(ldif_fail)
1346             self.fail("Adding attribute with preset msDS-IntId should fail")
1347         except LdbError as e25:
1348             (num, _) = e25.args
1349             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
1350
1351         # add the new attribute and update schema
1352         self.ldb.add_ldif(ldif)
1353         self._ldap_schemaUpdateNow()
1354
1355         # Search for created attribute
1356         res = []
1357         res = self.ldb.search(attr_dn, scope=SCOPE_BASE,
1358                               attrs=["lDAPDisplayName", "msDS-IntId", "systemFlags"])
1359         self.assertEquals(len(res), 1)
1360         self.assertEquals(str(res[0]["lDAPDisplayName"][0]), attr_ldap_name)
1361         if self.forest_level >= DS_DOMAIN_FUNCTION_2003:
1362             if self._is_schema_base_object(res[0]):
1363                 self.assertTrue("msDS-IntId" not in res[0])
1364             else:
1365                 self.assertTrue("msDS-IntId" in res[0])
1366         else:
1367             self.assertTrue("msDS-IntId" not in res[0])
1368
1369         msg = Message()
1370         msg.dn = Dn(self.ldb, attr_dn)
1371         msg["msDS-IntId"] = MessageElement("-1993108831", FLAG_MOD_REPLACE, "msDS-IntId")
1372         try:
1373             self.ldb.modify(msg)
1374             self.fail("Modifying msDS-IntId should return error")
1375         except LdbError as e26:
1376             (num, _) = e26.args
1377             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
1378
1379         # 2. Create attribute with systemFlags = FLAG_SCHEMA_BASE_OBJECT
1380         # msDS-IntId should be created if forest functional
1381         # level is >= DS_DOMAIN_FUNCTION_2003
1382         # and missing otherwise
1383         (attr_name, attr_ldap_name, attr_dn) = self._make_obj_names("msDS-IntId-Attr-2-")
1384         ldif = self._make_attr_ldif(attr_name, attr_dn)
1385         ldif += "systemFlags: 16\n"
1386
1387         # try to add msDS-IntId during Attribute creation
1388         ldif_fail = ldif + "msDS-IntId: -1993108831\n"
1389         try:
1390             self.ldb.add_ldif(ldif_fail)
1391             self.fail("Adding attribute with preset msDS-IntId should fail")
1392         except LdbError as e27:
1393             (num, _) = e27.args
1394             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
1395
1396         # add the new attribute and update schema
1397         self.ldb.add_ldif(ldif)
1398         self._ldap_schemaUpdateNow()
1399
1400         # Search for created attribute
1401         res = []
1402         res = self.ldb.search(attr_dn, scope=SCOPE_BASE,
1403                               attrs=["lDAPDisplayName", "msDS-IntId"])
1404         self.assertEquals(len(res), 1)
1405         self.assertEquals(str(res[0]["lDAPDisplayName"][0]), attr_ldap_name)
1406         if self.forest_level >= DS_DOMAIN_FUNCTION_2003:
1407             if self._is_schema_base_object(res[0]):
1408                 self.assertTrue("msDS-IntId" not in res[0])
1409             else:
1410                 self.assertTrue("msDS-IntId" in res[0])
1411         else:
1412             self.assertTrue("msDS-IntId" not in res[0])
1413
1414         msg = Message()
1415         msg.dn = Dn(self.ldb, attr_dn)
1416         msg["msDS-IntId"] = MessageElement("-1993108831", FLAG_MOD_REPLACE, "msDS-IntId")
1417         try:
1418             self.ldb.modify(msg)
1419             self.fail("Modifying msDS-IntId should return error")
1420         except LdbError as e28:
1421             (num, _) = e28.args
1422             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
1423
1424     def _make_class_ldif(self, class_dn, class_name, sub_oid):
1425         ldif = """
1426 dn: """ + class_dn + """
1427 objectClass: top
1428 objectClass: classSchema
1429 adminDescription: """ + class_name + """
1430 adminDisplayName: """ + class_name + """
1431 cn: """ + class_name + """
1432 governsId: 1.3.6.1.4.1.7165.4.6.2.6.%d.""" % sub_oid + str(random.randint(1, 100000)) + """
1433 instanceType: 4
1434 objectClassCategory: 1
1435 subClassOf: organizationalPerson
1436 rDNAttID: cn
1437 systemMustContain: cn
1438 systemOnly: FALSE
1439 """
1440         return ldif
1441
1442     def test_msDS_IntId_on_class(self):
1443         """Testing msDs-IntId creation for Class
1444            Reference: MS-ADTS - 3.1.1.2.4.8 Class classSchema"""
1445
1446         # 1. Create Class without systemFlags
1447         # msDS-IntId should be created if forest functional
1448         # level is >= DS_DOMAIN_FUNCTION_2003
1449         # and missing otherwise
1450         (class_name, class_ldap_name, class_dn) = self._make_obj_names("msDS-IntId-Class-1-")
1451         ldif = self._make_class_ldif(class_dn, class_name, 8)
1452
1453         # try to add msDS-IntId during Class creation
1454         ldif_add = ldif + "msDS-IntId: -1993108831\n"
1455         self.ldb.add_ldif(ldif_add)
1456         self._ldap_schemaUpdateNow()
1457
1458         res = self.ldb.search(class_dn, scope=SCOPE_BASE, attrs=["msDS-IntId"])
1459         self.assertEquals(len(res), 1)
1460         self.assertEquals(str(res[0]["msDS-IntId"][0]), "-1993108831")
1461
1462         # add a new Class and update schema
1463         (class_name, class_ldap_name, class_dn) = self._make_obj_names("msDS-IntId-Class-2-")
1464         ldif = self._make_class_ldif(class_dn, class_name, 9)
1465
1466         self.ldb.add_ldif(ldif)
1467         self._ldap_schemaUpdateNow()
1468
1469         # Search for created Class
1470         res = self.ldb.search(class_dn, scope=SCOPE_BASE, attrs=["msDS-IntId"])
1471         self.assertEquals(len(res), 1)
1472         self.assertFalse("msDS-IntId" in res[0])
1473
1474         msg = Message()
1475         msg.dn = Dn(self.ldb, class_dn)
1476         msg["msDS-IntId"] = MessageElement("-1993108831", FLAG_MOD_REPLACE, "msDS-IntId")
1477         try:
1478             self.ldb.modify(msg)
1479             self.fail("Modifying msDS-IntId should return error")
1480         except LdbError as e29:
1481             (num, _) = e29.args
1482             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
1483
1484         # 2. Create Class with systemFlags = FLAG_SCHEMA_BASE_OBJECT
1485         # msDS-IntId should be created if forest functional
1486         # level is >= DS_DOMAIN_FUNCTION_2003
1487         # and missing otherwise
1488         (class_name, class_ldap_name, class_dn) = self._make_obj_names("msDS-IntId-Class-3-")
1489         ldif = self._make_class_ldif(class_dn, class_name, 10)
1490         ldif += "systemFlags: 16\n"
1491
1492         # try to add msDS-IntId during Class creation
1493         ldif_add = ldif + "msDS-IntId: -1993108831\n"
1494         self.ldb.add_ldif(ldif_add)
1495
1496         res = self.ldb.search(class_dn, scope=SCOPE_BASE, attrs=["msDS-IntId"])
1497         self.assertEquals(len(res), 1)
1498         self.assertEquals(str(res[0]["msDS-IntId"][0]), "-1993108831")
1499
1500         # add the new Class and update schema
1501         (class_name, class_ldap_name, class_dn) = self._make_obj_names("msDS-IntId-Class-4-")
1502         ldif = self._make_class_ldif(class_dn, class_name, 11)
1503         ldif += "systemFlags: 16\n"
1504
1505         self.ldb.add_ldif(ldif)
1506         self._ldap_schemaUpdateNow()
1507
1508         # Search for created Class
1509         res = self.ldb.search(class_dn, scope=SCOPE_BASE, attrs=["msDS-IntId"])
1510         self.assertEquals(len(res), 1)
1511         self.assertFalse("msDS-IntId" in res[0])
1512
1513         msg = Message()
1514         msg.dn = Dn(self.ldb, class_dn)
1515         msg["msDS-IntId"] = MessageElement("-1993108831", FLAG_MOD_REPLACE, "msDS-IntId")
1516         try:
1517             self.ldb.modify(msg)
1518             self.fail("Modifying msDS-IntId should return error")
1519         except LdbError as e30:
1520             (num, _) = e30.args
1521             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
1522         res = self.ldb.search(class_dn, scope=SCOPE_BASE, attrs=["msDS-IntId"])
1523         self.assertEquals(len(res), 1)
1524         self.assertFalse("msDS-IntId" in res[0])
1525
1526     def test_verify_msDS_IntId(self):
1527         """Verify msDS-IntId exists only on attributes without FLAG_SCHEMA_BASE_OBJECT flag set"""
1528         count = 0
1529         res = self.ldb.search(self.schema_dn, scope=SCOPE_ONELEVEL,
1530                               expression="objectClass=attributeSchema",
1531                               attrs=["systemFlags", "msDS-IntId", "attributeID", "cn"])
1532         self.assertTrue(len(res) > 1)
1533         for ldb_msg in res:
1534             if self.forest_level >= DS_DOMAIN_FUNCTION_2003:
1535                 if self._is_schema_base_object(ldb_msg):
1536                     self.assertTrue("msDS-IntId" not in ldb_msg)
1537                 else:
1538                     # don't assert here as there are plenty of
1539                     # attributes under w2k8 that are not part of
1540                     # Base Schema (SYSTEM_FLAG_SCHEMA_BASE_OBJECT flag not set)
1541                     # has not msDS-IntId attribute set
1542                     #self.assertTrue("msDS-IntId" in ldb_msg, "msDS-IntId expected on: %s" % ldb_msg.dn)
1543                     if "msDS-IntId" not in ldb_msg:
1544                         count = count + 1
1545                         print("%3d warning: msDS-IntId expected on: %-30s %s" % (count, ldb_msg["attributeID"], ldb_msg["cn"]))
1546             else:
1547                 self.assertTrue("msDS-IntId" not in ldb_msg)
1548
1549
1550 class SchemaTests_msDS_isRODC(samba.tests.TestCase):
1551
1552     def setUp(self):
1553         super(SchemaTests_msDS_isRODC, self).setUp()
1554         self.ldb = SamDB(host, credentials=creds,
1555                          session_info=system_session(lp), lp=lp, options=ldb_options)
1556         res = self.ldb.search(base="", expression="", scope=SCOPE_BASE, attrs=["defaultNamingContext"])
1557         self.assertEquals(len(res), 1)
1558         self.base_dn = res[0]["defaultNamingContext"][0]
1559
1560     def test_objectClass_ntdsdsa(self):
1561         res = self.ldb.search(self.base_dn, expression="objectClass=nTDSDSA",
1562                               attrs=["msDS-isRODC"], controls=["search_options:1:2"])
1563         for ldb_msg in res:
1564             self.assertTrue("msDS-isRODC" in ldb_msg)
1565
1566     def test_objectClass_server(self):
1567         res = self.ldb.search(self.base_dn, expression="objectClass=server",
1568                               attrs=["msDS-isRODC"], controls=["search_options:1:2"])
1569         for ldb_msg in res:
1570             ntds_search_dn = "CN=NTDS Settings,%s" % ldb_msg['dn']
1571             try:
1572                 res_check = self.ldb.search(ntds_search_dn, attrs=["objectCategory"])
1573             except LdbError as e:
1574                 (num, _) = e.args
1575                 self.assertEquals(num, ERR_NO_SUCH_OBJECT)
1576                 print("Server entry %s doesn't have a NTDS settings object" % res[0]['dn'])
1577             else:
1578                 self.assertTrue("objectCategory" in res_check[0])
1579                 self.assertTrue("msDS-isRODC" in ldb_msg)
1580
1581     def test_objectClass_computer(self):
1582         res = self.ldb.search(self.base_dn, expression="objectClass=computer",
1583                               attrs=["serverReferenceBL", "msDS-isRODC"], controls=["search_options:1:2"])
1584         for ldb_msg in res:
1585             if "serverReferenceBL" not in ldb_msg:
1586                 print("Computer entry %s doesn't have a serverReferenceBL attribute" % ldb_msg['dn'])
1587             else:
1588                 self.assertTrue("msDS-isRODC" in ldb_msg)
1589
1590
1591 if "://" not in host:
1592     if os.path.isfile(host):
1593         host = "tdb://%s" % host
1594     else:
1595         host = "ldap://%s" % host
1596
1597 ldb_options = []
1598 if host.startswith("ldap://"):
1599     # user 'paged_search' module when connecting remotely
1600     ldb_options = ["modules:paged_searches"]
1601
1602 TestProgram(module=__name__, opts=subunitopts)