python/tests: add test for generated and duplicate linkIDs
[sfrench/samba-autobuild/.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, (num, _):
170                  self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
171
172         ldif = """
173 dn: CN=%s,%s""" % (class_name, self.schema_dn) + """
174 objectClass: top
175 objectClass: classSchema
176 adminDescription: """ + class_name + """
177 adminDisplayName: """ + class_name + """
178 cn: """ + class_name + """
179 governsId: 1.3.6.1.4.1.7165.4.6.2.6.2.""" + str(random.randint(1,100000)) + """
180 instanceType: 4
181 objectClassCategory: 1
182 subClassOf: organizationalPerson
183 systemFlags: 16
184 rDNAttID: cn
185 systemMustContain: cn
186 systemMustContain: """ + attr_ldap_display_name + """
187 systemOnly: FALSE
188 """
189         self.ldb.add_ldif(ldif)
190
191         # Search for created objectclass
192         res = []
193         res = self.ldb.search("cn=%s,%s" % (class_name, self.schema_dn), scope=SCOPE_BASE,
194                               attrs=["lDAPDisplayName", "defaultObjectCategory", "schemaIDGUID", "distinguishedName"])
195         self.assertEquals(len(res), 1)
196         self.assertEquals(res[0]["lDAPDisplayName"][0], class_ldap_display_name)
197         self.assertEquals(res[0]["defaultObjectCategory"][0], res[0]["distinguishedName"][0])
198         self.assertTrue("schemaIDGUID" in res[0])
199
200         ldif = """
201 dn:
202 changetype: modify
203 add: schemaUpdateNow
204 schemaUpdateNow: 1
205 """
206         self.ldb.modify_ldif(ldif)
207
208         object_name = "obj" + time.strftime("%s", time.gmtime())
209
210         ldif = """
211 dn: CN=%s,CN=Users,%s"""% (object_name, self.base_dn) + """
212 objectClass: organizationalPerson
213 objectClass: person
214 objectClass: """ + class_ldap_display_name + """
215 objectClass: top
216 cn: """ + object_name + """
217 instanceType: 4
218 objectCategory: CN=%s,%s"""% (class_name, self.schema_dn) + """
219 distinguishedName: CN=%s,CN=Users,%s"""% (object_name, self.base_dn) + """
220 name: """ + object_name + """
221 """ + attr_ldap_display_name + """: test
222 """
223         self.ldb.add_ldif(ldif)
224
225         # Search for created object
226         obj_res = self.ldb.search("cn=%s,cn=Users,%s" % (object_name, self.base_dn), scope=SCOPE_BASE, attrs=["replPropertyMetaData"])
227
228         self.assertEquals(len(obj_res), 1)
229         self.assertTrue("replPropertyMetaData" in obj_res[0])
230         val = obj_res[0]["replPropertyMetaData"][0]
231         repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob, str(val))
232         obj = repl.ctr
233
234         # Windows 2000 functional level won't have this.  It is too
235         # hard to work it out from the prefixmap however, so we skip
236         # this test in that case.
237         if msDS_IntId is not None:
238             found = False
239             for o in repl.ctr.array:
240                 if o.attid == msDS_IntId:
241                     found = True
242                     break
243             self.assertTrue(found, "Did not find 0x%08x in replPropertyMetaData" % msDS_IntId)
244         # Delete the object
245         delete_force(self.ldb, "cn=%s,cn=Users,%s" % (object_name, self.base_dn))
246
247     def test_subClassOf(self):
248         """ Testing usage of custom child schamaClass
249         """
250
251         class_name = "my-Class" + time.strftime("%s", time.gmtime())
252         class_ldap_display_name = class_name.replace("-", "")
253
254         ldif = """
255 dn: CN=%s,%s""" % (class_name, self.schema_dn) + """
256 objectClass: top
257 objectClass: classSchema
258 adminDescription: """ + class_name + """
259 adminDisplayName: """ + class_name + """
260 cn: """ + class_name + """
261 governsId: 1.3.6.1.4.1.7165.4.6.2.6.3.""" + str(random.randint(1,100000)) + """
262 instanceType: 4
263 objectClassCategory: 1
264 subClassOf: organizationalUnit
265 systemFlags: 16
266 systemOnly: FALSE
267 """
268         self.ldb.add_ldif(ldif)
269
270         # Search for created objectclass
271         res = []
272         res = self.ldb.search("cn=%s,%s" % (class_name, self.schema_dn), scope=SCOPE_BASE,
273                               attrs=["lDAPDisplayName", "defaultObjectCategory",
274                                      "schemaIDGUID", "distinguishedName"])
275         self.assertEquals(len(res), 1)
276         self.assertEquals(res[0]["lDAPDisplayName"][0], class_ldap_display_name)
277         self.assertEquals(res[0]["defaultObjectCategory"][0], res[0]["distinguishedName"][0])
278         self.assertTrue("schemaIDGUID" in res[0])
279
280         ldif = """
281 dn:
282 changetype: modify
283 add: schemaUpdateNow
284 schemaUpdateNow: 1
285 """
286         self.ldb.modify_ldif(ldif)
287
288         object_name = "org" + time.strftime("%s", time.gmtime())
289
290         ldif = """
291 dn: OU=%s,%s""" % (object_name, self.base_dn) + """
292 objectClass: """ + class_ldap_display_name + """
293 ou: """ + object_name + """
294 instanceType: 4
295 """
296         self.ldb.add_ldif(ldif)
297
298         # Search for created object
299         res = []
300         res = self.ldb.search("ou=%s,%s" % (object_name, self.base_dn), scope=SCOPE_BASE, attrs=["dn"])
301         self.assertEquals(len(res), 1)
302         # Delete the object
303         delete_force(self.ldb, "ou=%s,%s" % (object_name, self.base_dn))
304
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, (enum, estr):
346             self.assertEquals(enum, ERR_UNWILLING_TO_PERFORM)
347
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, (enum, estr):
390             self.assertEquals(enum, ERR_UNWILLING_TO_PERFORM)
391
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, (enum, estr):
433             self.assertEquals(enum, ERR_ENTRY_ALREADY_EXISTS)
434
435     def test_duplicate_implicit_ldapdisplayname(self):
436         """Testing creating a duplicate attribute ldapdisplayname"""
437         rand = str(random.randint(1,100000))
438         attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + "-" + rand
439         attr_ldap_display_name = attr_name.replace("-", "")
440         attributeID = "1.3.6.1.4.1.7165.4.6.1.6.5." + rand
441         ldif = """
442 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
443 objectClass: top
444 objectClass: attributeSchema
445 adminDescription: """ + attr_name + """
446 adminDisplayName: """ + attr_name + """
447 cn: """ + attr_name + """
448 attributeId: """ + attributeID + """
449 attributeSyntax: 2.5.5.12
450 omSyntax: 64
451 instanceType: 4
452 isSingleValued: TRUE
453 systemOnly: FALSE
454 """
455         self.ldb.add_ldif(ldif)
456
457         ldif = """
458 dn: CN=%s-dup,%s""" % (attr_name, self.schema_dn) + """
459 objectClass: top
460 objectClass: attributeSchema
461 adminDescription: """ + attr_name + """dup
462 adminDisplayName: """ + attr_name + """dup
463 cn: """ + attr_name + """-dup
464 ldapDisplayName: """ + attr_ldap_display_name + """
465 attributeId: """ + attributeID + """.1
466 attributeSyntax: 2.5.5.12
467 omSyntax: 64
468 instanceType: 4
469 isSingleValued: TRUE
470 systemOnly: FALSE
471 """
472         try:
473             self.ldb.add_ldif(ldif)
474             self.fail("Should have failed to add attribute with duplicate of the implicit ldapDisplayName")
475         except LdbError, (enum, estr):
476             self.assertEquals(enum, ERR_UNWILLING_TO_PERFORM)
477
478
479     def test_duplicate_explicit_ldapdisplayname(self):
480         """Testing creating a duplicate attribute ldapdisplayname"""
481         rand = str(random.randint(1,100000))
482         attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + "-" + rand
483         attr_ldap_display_name = attr_name.replace("-", "")
484         attributeID = "1.3.6.1.4.1.7165.4.6.1.6.6." + rand
485         ldif = """
486 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
487 objectClass: top
488 objectClass: attributeSchema
489 adminDescription: """ + attr_name + """
490 adminDisplayName: """ + attr_name + """
491 cn: """ + attr_name + """
492 attributeId: """ + attributeID + """
493 attributeSyntax: 2.5.5.12
494 ldapDisplayName: """ + attr_ldap_display_name + """
495 omSyntax: 64
496 instanceType: 4
497 isSingleValued: TRUE
498 systemOnly: FALSE
499 """
500         self.ldb.add_ldif(ldif)
501
502         ldif = """
503 dn: CN=%s-dup,%s""" % (attr_name, self.schema_dn) + """
504 objectClass: top
505 objectClass: attributeSchema
506 adminDescription: """ + attr_name + """dup
507 adminDisplayName: """ + attr_name + """dup
508 cn: """ + attr_name + """-dup
509 ldapDisplayName: """ + attr_ldap_display_name + """
510 attributeId: """ + attributeID + """.1
511 attributeSyntax: 2.5.5.12
512 omSyntax: 64
513 instanceType: 4
514 isSingleValued: TRUE
515 systemOnly: FALSE
516 """
517         try:
518             self.ldb.add_ldif(ldif)
519             self.fail("Should have failed to add attribute with duplicate ldapDisplayName")
520         except LdbError, (enum, estr):
521             self.assertEquals(enum, ERR_UNWILLING_TO_PERFORM)
522
523
524     def test_duplicate_explicit_ldapdisplayname_with_class(self):
525         """Testing creating a duplicate attribute ldapdisplayname between
526         and attribute and a class"""
527         rand = str(random.randint(1,100000))
528         attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + "-" + rand
529         attr_ldap_display_name = attr_name.replace("-", "")
530         attributeID = "1.3.6.1.4.1.7165.4.6.1.6.7." + rand
531         governsID   = "1.3.6.1.4.1.7165.4.6.2.6.4." + rand
532         ldif = """
533 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
534 objectClass: top
535 objectClass: attributeSchema
536 adminDescription: """ + attr_name + """
537 adminDisplayName: """ + attr_name + """
538 cn: """ + attr_name + """
539 attributeId: """ + attributeID + """
540 attributeSyntax: 2.5.5.12
541 ldapDisplayName: """ + attr_ldap_display_name + """
542 omSyntax: 64
543 instanceType: 4
544 isSingleValued: TRUE
545 systemOnly: FALSE
546 """
547         self.ldb.add_ldif(ldif)
548
549         ldif = """
550 dn: CN=%s-dup,%s""" % (attr_name, self.schema_dn) + """
551 objectClass: top
552 objectClass: classSchema
553 adminDescription: """ + attr_name + """dup
554 adminDisplayName: """ + attr_name + """dup
555 cn: """ + attr_name + """-dup
556 ldapDisplayName: """ + attr_ldap_display_name + """
557 governsID: """ + governsID + """
558 instanceType: 4
559 objectClassCategory: 1
560 subClassOf: organizationalPerson
561 rDNAttID: cn
562 systemMustContain: cn
563 systemOnly: FALSE
564 """
565         try:
566             self.ldb.add_ldif(ldif)
567             self.fail("Should have failed to add class with duplicate ldapDisplayName")
568         except LdbError, (enum, estr):
569             self.assertEquals(enum, ERR_UNWILLING_TO_PERFORM)
570
571
572     def test_duplicate_via_rename_ldapdisplayname(self):
573         """Testing creating a duplicate attribute ldapdisplayname"""
574         rand = str(random.randint(1,100000))
575         attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + "-" + rand
576         attr_ldap_display_name = attr_name.replace("-", "")
577         attributeID = "1.3.6.1.4.1.7165.4.6.1.6.8." + rand
578         ldif = """
579 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
580 objectClass: top
581 objectClass: attributeSchema
582 adminDescription: """ + attr_name + """
583 adminDisplayName: """ + attr_name + """
584 cn: """ + attr_name + """
585 attributeId: """ + attributeID + """
586 attributeSyntax: 2.5.5.12
587 ldapDisplayName: """ + attr_ldap_display_name + """
588 omSyntax: 64
589 instanceType: 4
590 isSingleValued: TRUE
591 systemOnly: FALSE
592 """
593         self.ldb.add_ldif(ldif)
594
595         ldif = """
596 dn: CN=%s-dup,%s""" % (attr_name, self.schema_dn) + """
597 objectClass: top
598 objectClass: attributeSchema
599 adminDescription: """ + attr_name + """dup
600 adminDisplayName: """ + attr_name + """dup
601 cn: """ + attr_name + """-dup
602 ldapDisplayName: """ + attr_ldap_display_name + """dup
603 attributeId: """ + attributeID + """.1
604 attributeSyntax: 2.5.5.12
605 omSyntax: 64
606 instanceType: 4
607 isSingleValued: TRUE
608 systemOnly: FALSE
609 """
610         self.ldb.add_ldif(ldif)
611
612         ldif = """
613 dn: CN=%s-dup,%s""" % (attr_name, self.schema_dn) + """
614 changetype: modify
615 replace: ldapDisplayName
616 ldapDisplayName: """ + attr_ldap_display_name + """
617 -
618 """
619         try:
620             self.ldb.modify_ldif(ldif)
621             self.fail("Should have failed to modify schema to have attribute with duplicate ldapDisplayName")
622         except LdbError, (enum, estr):
623             self.assertEquals(enum, ERR_UNWILLING_TO_PERFORM)
624
625
626     def test_duplicate_via_rename_attributeID(self):
627         """Testing creating a duplicate attributeID"""
628         rand = str(random.randint(1,100000))
629         attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + "-" + rand
630         attr_ldap_display_name = attr_name.replace("-", "")
631         attributeID = "1.3.6.1.4.1.7165.4.6.1.6.9." + rand
632         ldif = """
633 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
634 objectClass: top
635 objectClass: attributeSchema
636 adminDescription: """ + attr_name + """
637 adminDisplayName: """ + attr_name + """
638 cn: """ + attr_name + """
639 attributeId: """ + attributeID + """
640 attributeSyntax: 2.5.5.12
641 ldapDisplayName: """ + attr_ldap_display_name + """
642 omSyntax: 64
643 instanceType: 4
644 isSingleValued: TRUE
645 systemOnly: FALSE
646 """
647         self.ldb.add_ldif(ldif)
648
649         ldif = """
650 dn: CN=%s-dup,%s""" % (attr_name, self.schema_dn) + """
651 objectClass: top
652 objectClass: attributeSchema
653 adminDescription: """ + attr_name + """dup
654 adminDisplayName: """ + attr_name + """dup
655 cn: """ + attr_name + """-dup
656 ldapDisplayName: """ + attr_ldap_display_name + """dup
657 attributeId: """ + attributeID + """.1
658 attributeSyntax: 2.5.5.12
659 omSyntax: 64
660 instanceType: 4
661 isSingleValued: TRUE
662 systemOnly: FALSE
663 """
664         self.ldb.add_ldif(ldif)
665
666         ldif = """
667 dn: CN=%s-dup,%s""" % (attr_name, self.schema_dn) + """
668 changetype: modify
669 replace: attributeId
670 attributeId: """ + attributeID + """
671 -
672 """
673         try:
674             self.ldb.modify_ldif(ldif)
675             self.fail("Should have failed to modify schema to have attribute with duplicate attributeID")
676         except LdbError, (enum, estr):
677             self.assertEquals(enum, ERR_CONSTRAINT_VIOLATION)
678
679     def test_remove_ldapdisplayname(self):
680         """Testing removing the ldapdisplayname"""
681         rand = str(random.randint(1,100000))
682         attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + "-" + rand
683         attr_ldap_display_name = attr_name.replace("-", "")
684         attributeID = "1.3.6.1.4.1.7165.4.6.1.6.10." + rand
685         ldif = """
686 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
687 objectClass: top
688 objectClass: attributeSchema
689 adminDescription: """ + attr_name + """
690 adminDisplayName: """ + attr_name + """
691 cn: """ + attr_name + """
692 attributeId: """ + attributeID + """
693 attributeSyntax: 2.5.5.12
694 ldapDisplayName: """ + attr_ldap_display_name + """
695 omSyntax: 64
696 instanceType: 4
697 isSingleValued: TRUE
698 systemOnly: FALSE
699 """
700         self.ldb.add_ldif(ldif)
701
702         ldif = """
703 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
704 changetype: modify
705 replace: ldapDisplayName
706 -
707 """
708         try:
709             self.ldb.modify_ldif(ldif)
710             self.fail("Should have failed to remove the ldapdisplayname")
711         except LdbError, (enum, estr):
712             self.assertEquals(enum, ERR_OBJECT_CLASS_VIOLATION)
713
714     def test_rename_ldapdisplayname(self):
715         """Testing renaming ldapdisplayname"""
716         rand = str(random.randint(1,100000))
717         attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + "-" + rand
718         attr_ldap_display_name = attr_name.replace("-", "")
719         attributeID = "1.3.6.1.4.1.7165.4.6.1.6.11." + rand
720         ldif = """
721 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
722 objectClass: top
723 objectClass: attributeSchema
724 adminDescription: """ + attr_name + """
725 adminDisplayName: """ + attr_name + """
726 cn: """ + attr_name + """
727 attributeId: """ + attributeID + """
728 attributeSyntax: 2.5.5.12
729 ldapDisplayName: """ + attr_ldap_display_name + """
730 omSyntax: 64
731 instanceType: 4
732 isSingleValued: TRUE
733 systemOnly: FALSE
734 """
735         self.ldb.add_ldif(ldif)
736
737         ldif = """
738 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
739 changetype: modify
740 replace: ldapDisplayName
741 ldapDisplayName: """ + attr_ldap_display_name + """2
742 -
743 """
744         self.ldb.modify_ldif(ldif)
745
746
747     def test_change_attributeID(self):
748         """Testing change the attributeID"""
749         rand = str(random.randint(1,100000))
750         attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + "-" + rand
751         attr_ldap_display_name = attr_name.replace("-", "")
752         attributeID = "1.3.6.1.4.1.7165.4.6.1.6.12." + rand
753         ldif = """
754 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
755 objectClass: top
756 objectClass: attributeSchema
757 adminDescription: """ + attr_name + """
758 adminDisplayName: """ + attr_name + """
759 cn: """ + attr_name + """
760 attributeId: """ + attributeID + """
761 attributeSyntax: 2.5.5.12
762 ldapDisplayName: """ + attr_ldap_display_name + """
763 omSyntax: 64
764 instanceType: 4
765 isSingleValued: TRUE
766 systemOnly: FALSE
767 """
768         self.ldb.add_ldif(ldif)
769
770         ldif = """
771 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
772 changetype: modify
773 replace: attributeID
774 attributeId: """ + attributeID + """.1
775
776 """
777         try:
778             self.ldb.modify_ldif(ldif)
779             self.fail("Should have failed to modify schema to have different attributeID")
780         except LdbError, (enum, estr):
781             self.assertEquals(enum, ERR_CONSTRAINT_VIOLATION)
782
783
784     def test_change_attributeID_same(self):
785         """Testing change the attributeID to the same value"""
786         rand = str(random.randint(1,100000))
787         attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + "-" + rand
788         attr_ldap_display_name = attr_name.replace("-", "")
789         attributeID = "1.3.6.1.4.1.7165.4.6.1.6.13." + rand
790         ldif = """
791 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
792 objectClass: top
793 objectClass: attributeSchema
794 adminDescription: """ + attr_name + """
795 adminDisplayName: """ + attr_name + """
796 cn: """ + attr_name + """
797 attributeId: """ + attributeID + """
798 attributeSyntax: 2.5.5.12
799 ldapDisplayName: """ + attr_ldap_display_name + """
800 omSyntax: 64
801 instanceType: 4
802 isSingleValued: TRUE
803 systemOnly: FALSE
804 """
805         self.ldb.add_ldif(ldif)
806
807         ldif = """
808 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
809 changetype: modify
810 replace: attributeID
811 attributeId: """ + attributeID + """
812
813 """
814         try:
815             self.ldb.modify_ldif(ldif)
816             self.fail("Should have failed to modify schema to have the same attributeID")
817         except LdbError, (enum, estr):
818             self.assertEquals(enum, ERR_CONSTRAINT_VIOLATION)
819
820
821     def test_generated_linkID(self):
822         """
823         Test that we automatically generate a linkID if the
824         OID "1.2.840.113556.1.2.50" is given as the linkID
825         of a new attribute, and that we don't get/can't add
826         duplicate linkIDs. Also test that we can add a backlink
827         by providing the attributeID or ldapDisplayName of
828         a forwards link in the linkID attribute.
829         """
830
831         # linkID generation isn't available before 2003
832         res = self.ldb.search(base="", expression="", scope=SCOPE_BASE,
833                          attrs=["domainControllerFunctionality"])
834         self.assertEquals(len(res), 1)
835         dc_level = int(res[0]["domainControllerFunctionality"][0])
836         if dc_level < DS_DOMAIN_FUNCTION_2003:
837             return
838
839         rand = str(random.randint(1,100000))
840
841         attr_name_1 = "test-generated-linkID" + time.strftime("%s", time.gmtime()) + "-" + rand
842         attr_ldap_display_name_1 = attr_name_1.replace("-", "")
843         attributeID_1 = "1.3.6.1.4.1.7165.4.6.1.6.16." + rand
844         ldif = """
845 dn: CN=%s,%s""" % (attr_name_1, self.schema_dn) + """
846 objectClass: top
847 objectClass: attributeSchema
848 adminDescription: """ + attr_name_1 + """
849 adminDisplayName: """ + attr_name_1 + """
850 cn: """ + attr_name_1 + """
851 attributeId: """ + attributeID_1 + """
852 linkID: 1.2.840.113556.1.2.50
853 attributeSyntax: 2.5.5.1
854 ldapDisplayName: """ + attr_ldap_display_name_1 + """
855 omSyntax: 127
856 instanceType: 4
857 isSingleValued: TRUE
858 systemOnly: FALSE
859 """
860
861         try:
862             self.ldb.add_ldif(ldif)
863         except LdbError, (enum, estr):
864             self.fail(estr)
865
866         attr_name_2 = "test-generated-linkID-2" + time.strftime("%s", time.gmtime()) + "-" + rand
867         attr_ldap_display_name_2 = attr_name_2.replace("-", "")
868         attributeID_2 = "1.3.6.1.4.1.7165.4.6.1.6.17." + rand
869         ldif = """
870 dn: CN=%s,%s""" % (attr_name_2, self.schema_dn) + """
871 objectClass: top
872 objectClass: attributeSchema
873 adminDescription: """ + attr_name_2 + """
874 adminDisplayName: """ + attr_name_2 + """
875 cn: """ + attr_name_2 + """
876 attributeId: """ + attributeID_2 + """
877 linkID: 1.2.840.113556.1.2.50
878 attributeSyntax: 2.5.5.1
879 ldapDisplayName: """ + attr_ldap_display_name_2 + """
880 omSyntax: 127
881 instanceType: 4
882 isSingleValued: TRUE
883 systemOnly: FALSE
884 """
885
886         try:
887             self.ldb.add_ldif(ldif)
888         except LdbError, (enum, estr):
889             self.fail(estr)
890
891         res = self.ldb.search("CN=%s,%s" % (attr_name_1, self.schema_dn),
892                               scope=SCOPE_BASE,
893                               attrs=["linkID"])
894         self.assertEquals(len(res), 1)
895         linkID_1 = int(res[0]["linkID"][0])
896
897         res = self.ldb.search("CN=%s,%s" % (attr_name_2, self.schema_dn),
898                               scope=SCOPE_BASE,
899                               attrs=["linkID"])
900         self.assertEquals(len(res), 1)
901         linkID_2 = int(res[0]["linkID"][0])
902
903         # 0 should never be generated as a linkID
904         self.assertFalse(linkID_1 == 0)
905         self.assertFalse(linkID_2 == 0)
906
907         # The generated linkID should always be even, because
908         # it should assume we're adding a forward link.
909         self.assertTrue(linkID_1 % 2 == 0)
910         self.assertTrue(linkID_2 % 2 == 0)
911
912         self.assertFalse(linkID_1 == linkID_2)
913
914         # This is only necessary against Windows, since we depend
915         # on the previously added links in the next ones and Windows
916         # won't refresh the schema as we add them.
917         ldif = """
918 dn:
919 changetype: modify
920 replace: schemaupdatenow
921 schemaupdatenow: 1
922 """
923         self.ldb.modify_ldif(ldif)
924
925         # If we add a new link with the same linkID, it should fail
926         attr_name = "test-generated-linkID-duplicate" + time.strftime("%s", time.gmtime()) + "-" + rand
927         attr_ldap_display_name = attr_name.replace("-", "")
928         attributeID = "1.3.6.1.4.1.7165.4.6.1.6.18." + rand
929         ldif = """
930 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
931 objectClass: top
932 objectClass: attributeSchema
933 adminDescription: """ + attr_name + """
934 adminDisplayName: """ + attr_name + """
935 cn: """ + attr_name + """
936 attributeId: """ + attributeID + """
937 linkID: """ + str(linkID_1) + """
938 attributeSyntax: 2.5.5.1
939 ldapDisplayName: """ + attr_ldap_display_name + """
940 omSyntax: 127
941 instanceType: 4
942 isSingleValued: TRUE
943 systemOnly: FALSE
944 """
945
946         try:
947             self.ldb.add_ldif(ldif)
948             self.fail("Should have failed to add duplicate linkID value")
949         except LdbError, (enum, estr):
950             self.assertEquals(enum, ERR_UNWILLING_TO_PERFORM)
951
952         # If we add another attribute with the attributeID or lDAPDisplayName
953         # of a forward link in its linkID field, it should add as a backlink
954
955         attr_name_3 = "test-generated-linkID-backlink" + time.strftime("%s", time.gmtime()) + "-" + rand
956         attr_ldap_display_name_3 = attr_name_3.replace("-", "")
957         attributeID_3 = "1.3.6.1.4.1.7165.4.6.1.6.19." + rand
958         ldif = """
959 dn: CN=%s,%s""" % (attr_name_3, self.schema_dn) + """
960 objectClass: top
961 objectClass: attributeSchema
962 adminDescription: """ + attr_name_3 + """
963 adminDisplayName: """ + attr_name_3 + """
964 cn: """ + attr_name_3 + """
965 attributeId: """ + attributeID_3 + """
966 linkID: """ + str(linkID_1+1) + """
967 attributeSyntax: 2.5.5.1
968 ldapDisplayName: """ + attr_ldap_display_name_3 + """
969 omSyntax: 127
970 instanceType: 4
971 isSingleValued: TRUE
972 systemOnly: FALSE
973 """
974
975         try:
976             self.ldb.add_ldif(ldif)
977         except LdbError, (enum, estr):
978             self.fail(estr)
979
980         res = self.ldb.search("CN=%s,%s" % (attr_name_3, self.schema_dn),
981                               scope=SCOPE_BASE,
982                               attrs=["linkID"])
983         self.assertEquals(len(res), 1)
984         linkID = int(res[0]["linkID"][0])
985         self.assertEquals(linkID, linkID_1 + 1)
986
987         attr_name_4 = "test-generated-linkID-backlink-2" + time.strftime("%s", time.gmtime()) + "-" + rand
988         attr_ldap_display_name_4 = attr_name_4.replace("-", "")
989         attributeID_4 = "1.3.6.1.4.1.7165.4.6.1.6.20." + rand
990         ldif = """
991 dn: CN=%s,%s""" % (attr_name_4, self.schema_dn) + """
992 objectClass: top
993 objectClass: attributeSchema
994 adminDescription: """ + attr_name_4 + """
995 adminDisplayName: """ + attr_name_4 + """
996 cn: """ + attr_name_4 + """
997 attributeId: """ + attributeID_4 + """
998 linkID: """ + attr_ldap_display_name_2 + """
999 attributeSyntax: 2.5.5.1
1000 ldapDisplayName: """ + attr_ldap_display_name_4 + """
1001 omSyntax: 127
1002 instanceType: 4
1003 isSingleValued: TRUE
1004 systemOnly: FALSE
1005 """
1006
1007         try:
1008             self.ldb.add_ldif(ldif)
1009         except LdbError, (enum, estr):
1010             self.fail(estr)
1011
1012         res = self.ldb.search("CN=%s,%s" % (attr_name_4, self.schema_dn),
1013                               scope=SCOPE_BASE,
1014                               attrs=["linkID"])
1015         self.assertEquals(len(res), 1)
1016         linkID = int(res[0]["linkID"][0])
1017         self.assertEquals(linkID, linkID_2 + 1)
1018
1019         # If we then try to add another backlink in the same way
1020         # for the same forwards link, we should fail.
1021
1022         attr_name = "test-generated-linkID-backlink-duplicate" + time.strftime("%s", time.gmtime()) + "-" + rand
1023         attr_ldap_display_name = attr_name.replace("-", "")
1024         attributeID = "1.3.6.1.4.1.7165.4.6.1.6.21." + rand
1025         ldif = """
1026 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
1027 objectClass: top
1028 objectClass: attributeSchema
1029 adminDescription: """ + attr_name + """
1030 adminDisplayName: """ + attr_name + """
1031 cn: """ + attr_name + """
1032 attributeId: """ + attributeID + """
1033 linkID: """ + attributeID_1 + """
1034 attributeSyntax: 2.5.5.1
1035 ldapDisplayName: """ + attr_ldap_display_name + """
1036 omSyntax: 127
1037 instanceType: 4
1038 isSingleValued: TRUE
1039 systemOnly: FALSE
1040 """
1041
1042         try:
1043             self.ldb.add_ldif(ldif)
1044             self.fail("Should have failed to add duplicate backlink")
1045         except LdbError, (enum, estr):
1046             self.assertEquals(enum, ERR_UNWILLING_TO_PERFORM)
1047
1048         # If we try to supply the attributeID or ldapDisplayName
1049         # of an existing backlink in the linkID field of a new link,
1050         # it should fail.
1051
1052         attr_name = "test-generated-linkID-backlink-invalid" + time.strftime("%s", time.gmtime()) + "-" + rand
1053         attr_ldap_display_name = attr_name.replace("-", "")
1054         attributeID = "1.3.6.1.4.1.7165.4.6.1.6.22." + rand
1055         ldif = """
1056 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
1057 objectClass: top
1058 objectClass: attributeSchema
1059 adminDescription: """ + attr_name + """
1060 adminDisplayName: """ + attr_name + """
1061 cn: """ + attr_name + """
1062 attributeId: """ + attributeID + """
1063 linkID: """ + attributeID_3 + """
1064 attributeSyntax: 2.5.5.1
1065 ldapDisplayName: """ + attr_ldap_display_name + """
1066 omSyntax: 127
1067 instanceType: 4
1068 isSingleValued: TRUE
1069 systemOnly: FALSE
1070 """
1071
1072         try:
1073             self.ldb.add_ldif(ldif)
1074             self.fail("Should have failed to add backlink of backlink")
1075         except LdbError, (enum, estr):
1076             self.assertEquals(enum, ERR_UNWILLING_TO_PERFORM)
1077
1078         attr_name = "test-generated-linkID-backlink-invalid-2" + time.strftime("%s", time.gmtime()) + "-" + rand
1079         attr_ldap_display_name = attr_name.replace("-", "")
1080         attributeID = "1.3.6.1.4.1.7165.4.6.1.6.23." + rand
1081         ldif = """
1082 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
1083 objectClass: top
1084 objectClass: attributeSchema
1085 adminDescription: """ + attr_name + """
1086 adminDisplayName: """ + attr_name + """
1087 cn: """ + attr_name + """
1088 attributeId: """ + attributeID + """
1089 linkID: """ + attr_ldap_display_name_4 + """
1090 attributeSyntax: 2.5.5.1
1091 ldapDisplayName: """ + attr_ldap_display_name + """
1092 omSyntax: 127
1093 instanceType: 4
1094 isSingleValued: TRUE
1095 systemOnly: FALSE
1096 """
1097
1098         try:
1099             self.ldb.add_ldif(ldif)
1100             self.fail("Should have failed to add backlink of backlink")
1101         except LdbError, (enum, estr):
1102             self.assertEquals(enum, ERR_UNWILLING_TO_PERFORM)
1103
1104
1105     def test_change_governsID(self):
1106         """Testing change the governsID"""
1107         rand = str(random.randint(1,100000))
1108         class_name = "test-Class" + time.strftime("%s", time.gmtime()) + "-" + rand
1109         class_ldap_display_name = class_name.replace("-", "")
1110         governsID = "1.3.6.1.4.1.7165.4.6.2.6.5." + rand
1111         ldif = """
1112 dn: CN=%s,%s""" % (class_name, self.schema_dn) + """
1113 objectClass: top
1114 objectClass: classSchema
1115 adminDescription: """ + class_name + """
1116 adminDisplayName: """ + class_name + """
1117 cn: """ + class_name + """
1118 governsId: """ + governsID + """
1119 ldapDisplayName: """ + class_ldap_display_name + """
1120 instanceType: 4
1121 objectClassCategory: 1
1122 subClassOf: organizationalPerson
1123 rDNAttID: cn
1124 systemMustContain: cn
1125 systemOnly: FALSE
1126 """
1127         self.ldb.add_ldif(ldif)
1128
1129         ldif = """
1130 dn: CN=%s,%s""" % (class_name, self.schema_dn) + """
1131 changetype: modify
1132 replace: governsID
1133 governsId: """ + governsID + """.1
1134
1135 """
1136         try:
1137             self.ldb.modify_ldif(ldif)
1138             self.fail("Should have failed to modify schema to have different governsID")
1139         except LdbError, (enum, estr):
1140             self.assertEquals(enum, ERR_CONSTRAINT_VIOLATION)
1141
1142
1143     def test_change_governsID_same(self):
1144         """Testing change the governsID"""
1145         rand = str(random.randint(1,100000))
1146         class_name = "test-Class" + time.strftime("%s", time.gmtime()) + "-" + rand
1147         class_ldap_display_name = class_name.replace("-", "")
1148         governsID = "1.3.6.1.4.1.7165.4.6.2.6.6." + rand
1149         ldif = """
1150 dn: CN=%s,%s""" % (class_name, self.schema_dn) + """
1151 objectClass: top
1152 objectClass: classSchema
1153 adminDescription: """ + class_name + """
1154 adminDisplayName: """ + class_name + """
1155 cn: """ + class_name + """
1156 governsId: """ + governsID + """
1157 ldapDisplayName: """ + class_ldap_display_name + """
1158 instanceType: 4
1159 objectClassCategory: 1
1160 subClassOf: organizationalPerson
1161 rDNAttID: cn
1162 systemMustContain: cn
1163 systemOnly: FALSE
1164 """
1165         self.ldb.add_ldif(ldif)
1166
1167         ldif = """
1168 dn: CN=%s,%s""" % (class_name, self.schema_dn) + """
1169 changetype: modify
1170 replace: governsID
1171 governsId: """ + governsID + """.1
1172
1173 """
1174         try:
1175             self.ldb.modify_ldif(ldif)
1176             self.fail("Should have failed to modify schema to have the same governsID")
1177         except LdbError, (enum, estr):
1178             self.assertEquals(enum, ERR_CONSTRAINT_VIOLATION)
1179
1180
1181     def test_subClassOf(self):
1182         """ Testing usage of custom child classSchema
1183         """
1184
1185         class_name = "my-Class" + time.strftime("%s", time.gmtime())
1186         class_ldap_display_name = class_name.replace("-", "")
1187
1188         ldif = """
1189 dn: CN=%s,%s""" % (class_name, self.schema_dn) + """
1190 objectClass: top
1191 objectClass: classSchema
1192 adminDescription: """ + class_name + """
1193 adminDisplayName: """ + class_name + """
1194 cn: """ + class_name + """
1195 governsId: 1.3.6.1.4.1.7165.4.6.2.6.7.""" + str(random.randint(1,100000)) + """
1196 instanceType: 4
1197 objectClassCategory: 1
1198 subClassOf: organizationalUnit
1199 systemFlags: 16
1200 systemOnly: FALSE
1201 """
1202         self.ldb.add_ldif(ldif)
1203
1204         # Search for created objectclass
1205         res = []
1206         res = self.ldb.search("cn=%s,%s" % (class_name, self.schema_dn), scope=SCOPE_BASE,
1207                               attrs=["lDAPDisplayName", "defaultObjectCategory",
1208                                      "schemaIDGUID", "distinguishedName"])
1209         self.assertEquals(len(res), 1)
1210         self.assertEquals(res[0]["lDAPDisplayName"][0], class_ldap_display_name)
1211         self.assertEquals(res[0]["defaultObjectCategory"][0], res[0]["distinguishedName"][0])
1212         self.assertTrue("schemaIDGUID" in res[0])
1213
1214         ldif = """
1215 dn:
1216 changetype: modify
1217 add: schemaUpdateNow
1218 schemaUpdateNow: 1
1219 """
1220         self.ldb.modify_ldif(ldif)
1221
1222         object_name = "org" + time.strftime("%s", time.gmtime())
1223
1224         ldif = """
1225 dn: OU=%s,%s""" % (object_name, self.base_dn) + """
1226 objectClass: """ + class_ldap_display_name + """
1227 ou: """ + object_name + """
1228 instanceType: 4
1229 """
1230         self.ldb.add_ldif(ldif)
1231
1232         # Search for created object
1233         res = []
1234         res = self.ldb.search("ou=%s,%s" % (object_name, self.base_dn), scope=SCOPE_BASE, attrs=["dn"])
1235         self.assertEquals(len(res), 1)
1236         # Delete the object
1237         delete_force(self.ldb, "ou=%s,%s" % (object_name, self.base_dn))
1238
1239
1240 class SchemaTests_msDS_IntId(samba.tests.TestCase):
1241
1242     def setUp(self):
1243         super(SchemaTests_msDS_IntId, self).setUp()
1244         self.ldb = SamDB(host, credentials=creds,
1245             session_info=system_session(lp), lp=lp, options=ldb_options)
1246         res = self.ldb.search(base="", expression="", scope=SCOPE_BASE,
1247                          attrs=["schemaNamingContext", "defaultNamingContext",
1248                                 "forestFunctionality"])
1249         self.assertEquals(len(res), 1)
1250         self.schema_dn = res[0]["schemaNamingContext"][0]
1251         self.base_dn = res[0]["defaultNamingContext"][0]
1252         self.forest_level = int(res[0]["forestFunctionality"][0])
1253
1254     def _ldap_schemaUpdateNow(self):
1255         ldif = """
1256 dn:
1257 changetype: modify
1258 add: schemaUpdateNow
1259 schemaUpdateNow: 1
1260 """
1261         self.ldb.modify_ldif(ldif)
1262
1263     def _make_obj_names(self, prefix):
1264         class_name = prefix + time.strftime("%s", time.gmtime())
1265         class_ldap_name = class_name.replace("-", "")
1266         class_dn = "CN=%s,%s" % (class_name, self.schema_dn)
1267         return (class_name, class_ldap_name, class_dn)
1268
1269     def _is_schema_base_object(self, ldb_msg):
1270         """Test systemFlags for SYSTEM_FLAG_SCHEMA_BASE_OBJECT (16)"""
1271         systemFlags = 0
1272         if "systemFlags" in ldb_msg:
1273             systemFlags = int(ldb_msg["systemFlags"][0])
1274         return (systemFlags & 16) != 0
1275
1276     def _make_attr_ldif(self, attr_name, attr_dn):
1277         ldif = """
1278 dn: """ + attr_dn + """
1279 objectClass: top
1280 objectClass: attributeSchema
1281 adminDescription: """ + attr_name + """
1282 adminDisplayName: """ + attr_name + """
1283 cn: """ + attr_name + """
1284 attributeId: 1.3.6.1.4.1.7165.4.6.1.6.14.""" + str(random.randint(1,100000)) + """
1285 attributeSyntax: 2.5.5.12
1286 omSyntax: 64
1287 instanceType: 4
1288 isSingleValued: TRUE
1289 systemOnly: FALSE
1290 """
1291         return ldif
1292
1293     def test_msDS_IntId_on_attr(self):
1294         """Testing msDs-IntId creation for Attributes.
1295         See MS-ADTS - 3.1.1.Attributes
1296
1297         This test should verify that:
1298         - Creating attribute with 'msDS-IntId' fails with ERR_UNWILLING_TO_PERFORM
1299         - Adding 'msDS-IntId' on existing attribute fails with ERR_CONSTRAINT_VIOLATION
1300         - Creating attribute with 'msDS-IntId' set and FLAG_SCHEMA_BASE_OBJECT flag
1301           set fails with ERR_UNWILLING_TO_PERFORM
1302         - Attributes created with FLAG_SCHEMA_BASE_OBJECT not set have
1303           'msDS-IntId' attribute added internally
1304         """
1305
1306         # 1. Create attribute without systemFlags
1307         # msDS-IntId should be created if forest functional
1308         # level is >= DS_DOMAIN_FUNCTION_2003
1309         # and missing otherwise
1310         (attr_name, attr_ldap_name, attr_dn) = self._make_obj_names("msDS-IntId-Attr-1-")
1311         ldif = self._make_attr_ldif(attr_name, attr_dn)
1312
1313         # try to add msDS-IntId during Attribute creation
1314         ldif_fail = ldif + "msDS-IntId: -1993108831\n"
1315         try:
1316             self.ldb.add_ldif(ldif_fail)
1317             self.fail("Adding attribute with preset msDS-IntId should fail")
1318         except LdbError, (num, _):
1319             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
1320
1321         # add the new attribute and update schema
1322         self.ldb.add_ldif(ldif)
1323         self._ldap_schemaUpdateNow()
1324
1325         # Search for created attribute
1326         res = []
1327         res = self.ldb.search(attr_dn, scope=SCOPE_BASE,
1328                               attrs=["lDAPDisplayName", "msDS-IntId", "systemFlags"])
1329         self.assertEquals(len(res), 1)
1330         self.assertEquals(res[0]["lDAPDisplayName"][0], attr_ldap_name)
1331         if self.forest_level >= DS_DOMAIN_FUNCTION_2003:
1332             if self._is_schema_base_object(res[0]):
1333                 self.assertTrue("msDS-IntId" not in res[0])
1334             else:
1335                 self.assertTrue("msDS-IntId" in res[0])
1336         else:
1337             self.assertTrue("msDS-IntId" not in res[0])
1338
1339         msg = Message()
1340         msg.dn = Dn(self.ldb, attr_dn)
1341         msg["msDS-IntId"] = MessageElement("-1993108831", FLAG_MOD_REPLACE, "msDS-IntId")
1342         try:
1343             self.ldb.modify(msg)
1344             self.fail("Modifying msDS-IntId should return error")
1345         except LdbError, (num, _):
1346             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
1347
1348         # 2. Create attribute with systemFlags = FLAG_SCHEMA_BASE_OBJECT
1349         # msDS-IntId should be created if forest functional
1350         # level is >= DS_DOMAIN_FUNCTION_2003
1351         # and missing otherwise
1352         (attr_name, attr_ldap_name, attr_dn) = self._make_obj_names("msDS-IntId-Attr-2-")
1353         ldif = self._make_attr_ldif(attr_name, attr_dn)
1354         ldif += "systemFlags: 16\n"
1355
1356         # try to add msDS-IntId during Attribute creation
1357         ldif_fail = ldif + "msDS-IntId: -1993108831\n"
1358         try:
1359             self.ldb.add_ldif(ldif_fail)
1360             self.fail("Adding attribute with preset msDS-IntId should fail")
1361         except LdbError, (num, _):
1362             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
1363
1364         # add the new attribute and update schema
1365         self.ldb.add_ldif(ldif)
1366         self._ldap_schemaUpdateNow()
1367
1368         # Search for created attribute
1369         res = []
1370         res = self.ldb.search(attr_dn, scope=SCOPE_BASE,
1371                               attrs=["lDAPDisplayName", "msDS-IntId"])
1372         self.assertEquals(len(res), 1)
1373         self.assertEquals(res[0]["lDAPDisplayName"][0], attr_ldap_name)
1374         if self.forest_level >= DS_DOMAIN_FUNCTION_2003:
1375             if self._is_schema_base_object(res[0]):
1376                 self.assertTrue("msDS-IntId" not in res[0])
1377             else:
1378                 self.assertTrue("msDS-IntId" in res[0])
1379         else:
1380             self.assertTrue("msDS-IntId" not in res[0])
1381
1382         msg = Message()
1383         msg.dn = Dn(self.ldb, attr_dn)
1384         msg["msDS-IntId"] = MessageElement("-1993108831", FLAG_MOD_REPLACE, "msDS-IntId")
1385         try:
1386             self.ldb.modify(msg)
1387             self.fail("Modifying msDS-IntId should return error")
1388         except LdbError, (num, _):
1389             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
1390
1391
1392     def _make_class_ldif(self, class_dn, class_name, sub_oid):
1393         ldif = """
1394 dn: """ + class_dn + """
1395 objectClass: top
1396 objectClass: classSchema
1397 adminDescription: """ + class_name + """
1398 adminDisplayName: """ + class_name + """
1399 cn: """ + class_name + """
1400 governsId: 1.3.6.1.4.1.7165.4.6.2.6.%d.""" % sub_oid + str(random.randint(1,100000)) + """
1401 instanceType: 4
1402 objectClassCategory: 1
1403 subClassOf: organizationalPerson
1404 rDNAttID: cn
1405 systemMustContain: cn
1406 systemOnly: FALSE
1407 """
1408         return ldif
1409
1410     def test_msDS_IntId_on_class(self):
1411         """Testing msDs-IntId creation for Class
1412            Reference: MS-ADTS - 3.1.1.2.4.8 Class classSchema"""
1413
1414         # 1. Create Class without systemFlags
1415         # msDS-IntId should be created if forest functional
1416         # level is >= DS_DOMAIN_FUNCTION_2003
1417         # and missing otherwise
1418         (class_name, class_ldap_name, class_dn) = self._make_obj_names("msDS-IntId-Class-1-")
1419         ldif = self._make_class_ldif(class_dn, class_name, 8)
1420
1421         # try to add msDS-IntId during Class creation
1422         ldif_add = ldif + "msDS-IntId: -1993108831\n"
1423         self.ldb.add_ldif(ldif_add)
1424         self._ldap_schemaUpdateNow()
1425
1426         res = self.ldb.search(class_dn, scope=SCOPE_BASE, attrs=["msDS-IntId"])
1427         self.assertEquals(len(res), 1)
1428         self.assertEquals(res[0]["msDS-IntId"][0], "-1993108831")
1429
1430         # add a new Class and update schema
1431         (class_name, class_ldap_name, class_dn) = self._make_obj_names("msDS-IntId-Class-2-")
1432         ldif = self._make_class_ldif(class_dn, class_name, 9)
1433
1434         self.ldb.add_ldif(ldif)
1435         self._ldap_schemaUpdateNow()
1436
1437         # Search for created Class
1438         res = self.ldb.search(class_dn, scope=SCOPE_BASE, attrs=["msDS-IntId"])
1439         self.assertEquals(len(res), 1)
1440         self.assertFalse("msDS-IntId" in res[0])
1441
1442         msg = Message()
1443         msg.dn = Dn(self.ldb, class_dn)
1444         msg["msDS-IntId"] = MessageElement("-1993108831", FLAG_MOD_REPLACE, "msDS-IntId")
1445         try:
1446             self.ldb.modify(msg)
1447             self.fail("Modifying msDS-IntId should return error")
1448         except LdbError, (num, _):
1449             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
1450
1451         # 2. Create Class with systemFlags = FLAG_SCHEMA_BASE_OBJECT
1452         # msDS-IntId should be created if forest functional
1453         # level is >= DS_DOMAIN_FUNCTION_2003
1454         # and missing otherwise
1455         (class_name, class_ldap_name, class_dn) = self._make_obj_names("msDS-IntId-Class-3-")
1456         ldif = self._make_class_ldif(class_dn, class_name, 10)
1457         ldif += "systemFlags: 16\n"
1458
1459         # try to add msDS-IntId during Class creation
1460         ldif_add = ldif + "msDS-IntId: -1993108831\n"
1461         self.ldb.add_ldif(ldif_add)
1462
1463         res = self.ldb.search(class_dn, scope=SCOPE_BASE, attrs=["msDS-IntId"])
1464         self.assertEquals(len(res), 1)
1465         self.assertEquals(res[0]["msDS-IntId"][0], "-1993108831")
1466
1467         # add the new Class and update schema
1468         (class_name, class_ldap_name, class_dn) = self._make_obj_names("msDS-IntId-Class-4-")
1469         ldif = self._make_class_ldif(class_dn, class_name, 11)
1470         ldif += "systemFlags: 16\n"
1471
1472         self.ldb.add_ldif(ldif)
1473         self._ldap_schemaUpdateNow()
1474
1475         # Search for created Class
1476         res = self.ldb.search(class_dn, scope=SCOPE_BASE, attrs=["msDS-IntId"])
1477         self.assertEquals(len(res), 1)
1478         self.assertFalse("msDS-IntId" in res[0])
1479
1480         msg = Message()
1481         msg.dn = Dn(self.ldb, class_dn)
1482         msg["msDS-IntId"] = MessageElement("-1993108831", FLAG_MOD_REPLACE, "msDS-IntId")
1483         try:
1484             self.ldb.modify(msg)
1485             self.fail("Modifying msDS-IntId should return error")
1486         except LdbError, (num, _):
1487             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
1488         res = self.ldb.search(class_dn, scope=SCOPE_BASE, attrs=["msDS-IntId"])
1489         self.assertEquals(len(res), 1)
1490         self.assertFalse("msDS-IntId" in res[0])
1491
1492
1493     def test_verify_msDS_IntId(self):
1494         """Verify msDS-IntId exists only on attributes without FLAG_SCHEMA_BASE_OBJECT flag set"""
1495         count = 0
1496         res = self.ldb.search(self.schema_dn, scope=SCOPE_ONELEVEL,
1497                               expression="objectClass=attributeSchema",
1498                               attrs=["systemFlags", "msDS-IntId", "attributeID", "cn"])
1499         self.assertTrue(len(res) > 1)
1500         for ldb_msg in res:
1501             if self.forest_level >= DS_DOMAIN_FUNCTION_2003:
1502                 if self._is_schema_base_object(ldb_msg):
1503                     self.assertTrue("msDS-IntId" not in ldb_msg)
1504                 else:
1505                     # don't assert here as there are plenty of
1506                     # attributes under w2k8 that are not part of
1507                     # Base Schema (SYSTEM_FLAG_SCHEMA_BASE_OBJECT flag not set)
1508                     # has not msDS-IntId attribute set
1509                     #self.assertTrue("msDS-IntId" in ldb_msg, "msDS-IntId expected on: %s" % ldb_msg.dn)
1510                     if "msDS-IntId" not in ldb_msg:
1511                         count = count + 1
1512                         print "%3d warning: msDS-IntId expected on: %-30s %s" % (count, ldb_msg["attributeID"], ldb_msg["cn"])
1513             else:
1514                 self.assertTrue("msDS-IntId" not in ldb_msg)
1515
1516
1517 class SchemaTests_msDS_isRODC(samba.tests.TestCase):
1518
1519     def setUp(self):
1520         super(SchemaTests_msDS_isRODC, self).setUp()
1521         self.ldb =  SamDB(host, credentials=creds,
1522             session_info=system_session(lp), lp=lp, options=ldb_options)
1523         res = self.ldb.search(base="", expression="", scope=SCOPE_BASE, attrs=["defaultNamingContext"])
1524         self.assertEquals(len(res), 1)
1525         self.base_dn = res[0]["defaultNamingContext"][0]
1526
1527     def test_objectClass_ntdsdsa(self):
1528         res = self.ldb.search(self.base_dn, expression="objectClass=nTDSDSA",
1529                               attrs=["msDS-isRODC"], controls=["search_options:1:2"])
1530         for ldb_msg in res:
1531             self.assertTrue("msDS-isRODC" in ldb_msg)
1532
1533     def test_objectClass_server(self):
1534         res = self.ldb.search(self.base_dn, expression="objectClass=server",
1535                               attrs=["msDS-isRODC"], controls=["search_options:1:2"])
1536         for ldb_msg in res:
1537             ntds_search_dn = "CN=NTDS Settings,%s" % ldb_msg['dn']
1538             try:
1539                 res_check = self.ldb.search(ntds_search_dn, attrs=["objectCategory"])
1540             except LdbError, (num, _):
1541                 self.assertEquals(num, ERR_NO_SUCH_OBJECT)
1542                 print("Server entry %s doesn't have a NTDS settings object" % res[0]['dn'])
1543             else:
1544                 self.assertTrue("objectCategory" in res_check[0])
1545                 self.assertTrue("msDS-isRODC" in ldb_msg)
1546
1547     def test_objectClass_computer(self):
1548         res = self.ldb.search(self.base_dn, expression="objectClass=computer",
1549                               attrs=["serverReferenceBL","msDS-isRODC"], controls=["search_options:1:2"])
1550         for ldb_msg in res:
1551             if "serverReferenceBL" not in ldb_msg:
1552                 print("Computer entry %s doesn't have a serverReferenceBL attribute" % ldb_msg['dn'])
1553             else:
1554                 self.assertTrue("msDS-isRODC" in ldb_msg)
1555
1556 if not "://" in host:
1557     if os.path.isfile(host):
1558         host = "tdb://%s" % host
1559     else:
1560         host = "ldap://%s" % host
1561
1562 ldb_options = []
1563 if host.startswith("ldap://"):
1564     # user 'paged_search' module when connecting remotely
1565     ldb_options = ["modules:paged_searches"]
1566
1567 TestProgram(module=__name__, opts=subunitopts)