c92029608260ef1c2b5e8cdd6e4d8ebf1904591b
[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 #
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
20
21
22 import optparse
23 import sys
24 import time
25 import random
26 import os
27
28 sys.path.insert(0, "bin/python")
29 import samba
30 from samba.tests.subunitrun import TestProgram, SubunitOptions
31
32 import samba.getopt as options
33
34 from samba.auth import system_session
35 from ldb import SCOPE_ONELEVEL, SCOPE_BASE, LdbError
36 from ldb import ERR_NO_SUCH_OBJECT
37 from ldb import ERR_UNWILLING_TO_PERFORM
38 from ldb import ERR_ENTRY_ALREADY_EXISTS
39 from ldb import ERR_CONSTRAINT_VIOLATION
40 from ldb import ERR_OBJECT_CLASS_VIOLATION
41 from ldb import Message, MessageElement, Dn
42 from ldb import FLAG_MOD_REPLACE
43 from samba.samdb import SamDB
44 from samba.dsdb import DS_DOMAIN_FUNCTION_2003
45 from samba.tests import delete_force
46 from samba.ndr import ndr_unpack
47 from samba.dcerpc import drsblobs
48
49 parser = optparse.OptionParser("ldap_schema.py [options] <host>")
50 sambaopts = options.SambaOptions(parser)
51 parser.add_option_group(sambaopts)
52 parser.add_option_group(options.VersionOptions(parser))
53 # use command line creds if available
54 credopts = options.CredentialsOptions(parser)
55 parser.add_option_group(credopts)
56 subunitopts = SubunitOptions(parser)
57 parser.add_option_group(subunitopts)
58 opts, args = parser.parse_args()
59
60 if len(args) < 1:
61     parser.print_usage()
62     sys.exit(1)
63
64 host = args[0]
65
66 lp = sambaopts.get_loadparm()
67 creds = credopts.get_credentials(lp)
68
69
70 class SchemaTests(samba.tests.TestCase):
71
72     def setUp(self):
73         super(SchemaTests, self).setUp()
74         self.ldb = SamDB(host, credentials=creds,
75             session_info=system_session(lp), lp=lp, options=ldb_options)
76         self.base_dn = self.ldb.domain_dn()
77         self.schema_dn = self.ldb.get_schema_basedn().get_linearized()
78
79     def test_generated_schema(self):
80         """Testing we can read the generated schema via LDAP"""
81         res = self.ldb.search("cn=aggregate,"+self.schema_dn, scope=SCOPE_BASE,
82                 attrs=["objectClasses", "attributeTypes", "dITContentRules"])
83         self.assertEquals(len(res), 1)
84         self.assertTrue("dITContentRules" in res[0])
85         self.assertTrue("objectClasses" in res[0])
86         self.assertTrue("attributeTypes" in res[0])
87
88     def test_generated_schema_is_operational(self):
89         """Testing we don't get the generated schema via LDAP by default"""
90         # Must keep the "*" form
91         res = self.ldb.search("cn=aggregate,"+self.schema_dn, scope=SCOPE_BASE,
92                               attrs=["*"])
93         self.assertEquals(len(res), 1)
94         self.assertFalse("dITContentRules" in res[0])
95         self.assertFalse("objectClasses" in res[0])
96         self.assertFalse("attributeTypes" in res[0])
97
98     def test_schemaUpdateNow(self):
99         """Testing schemaUpdateNow"""
100         rand = str(random.randint(1,100000))
101         attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + "-" + rand
102         attr_ldap_display_name = attr_name.replace("-", "")
103
104         ldif = """
105 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
106 objectClass: top
107 objectClass: attributeSchema
108 adminDescription: """ + attr_name + """
109 adminDisplayName: """ + attr_name + """
110 cn: """ + attr_name + """
111 attributeId: 1.3.6.1.4.1.7165.4.6.1.6.1.""" + rand + """
112 attributeSyntax: 2.5.5.12
113 omSyntax: 64
114 instanceType: 4
115 isSingleValued: TRUE
116 systemOnly: FALSE
117 """
118         self.ldb.add_ldif(ldif)
119         # We must do a schemaUpdateNow otherwise it's not 100% sure that the schema
120         # will contain the new attribute
121         ldif = """
122 dn:
123 changetype: modify
124 add: schemaUpdateNow
125 schemaUpdateNow: 1
126 """
127         self.ldb.modify_ldif(ldif)
128
129         # Search for created attribute
130         res = []
131         res = self.ldb.search("cn=%s,%s" % (attr_name, self.schema_dn), scope=SCOPE_BASE,
132                               attrs=["lDAPDisplayName","schemaIDGUID", "msDS-IntID"])
133         self.assertEquals(len(res), 1)
134         self.assertEquals(res[0]["lDAPDisplayName"][0], attr_ldap_display_name)
135         self.assertTrue("schemaIDGUID" in res[0])
136         if "msDS-IntId" in res[0]:
137             msDS_IntId = int(res[0]["msDS-IntId"][0])
138             if msDS_IntId < 0:
139                 msDS_IntId += (1 << 32)
140         else:
141             msDS_IntId = None
142
143         class_name = "test-Class" + time.strftime("%s", time.gmtime())
144         class_ldap_display_name = class_name.replace("-", "")
145
146         # First try to create a class with a wrong "defaultObjectCategory"
147         ldif = """
148 dn: CN=%s,%s""" % (class_name, self.schema_dn) + """
149 objectClass: top
150 objectClass: classSchema
151 defaultObjectCategory: CN=_
152 adminDescription: """ + class_name + """
153 adminDisplayName: """ + class_name + """
154 cn: """ + class_name + """
155 governsId: 1.3.6.1.4.1.7165.4.6.2.6.1.""" + str(random.randint(1,100000)) + """
156 instanceType: 4
157 objectClassCategory: 1
158 subClassOf: organizationalPerson
159 systemFlags: 16
160 rDNAttID: cn
161 systemMustContain: cn
162 systemMustContain: """ + attr_ldap_display_name + """
163 systemOnly: FALSE
164 """
165         try:
166                  self.ldb.add_ldif(ldif)
167                  self.fail()
168         except LdbError, (num, _):
169                  self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
170
171         ldif = """
172 dn: CN=%s,%s""" % (class_name, self.schema_dn) + """
173 objectClass: top
174 objectClass: classSchema
175 adminDescription: """ + class_name + """
176 adminDisplayName: """ + class_name + """
177 cn: """ + class_name + """
178 governsId: 1.3.6.1.4.1.7165.4.6.2.6.2.""" + str(random.randint(1,100000)) + """
179 instanceType: 4
180 objectClassCategory: 1
181 subClassOf: organizationalPerson
182 systemFlags: 16
183 rDNAttID: cn
184 systemMustContain: cn
185 systemMustContain: """ + attr_ldap_display_name + """
186 systemOnly: FALSE
187 """
188         self.ldb.add_ldif(ldif)
189
190         # Search for created objectclass
191         res = []
192         res = self.ldb.search("cn=%s,%s" % (class_name, self.schema_dn), scope=SCOPE_BASE,
193                               attrs=["lDAPDisplayName", "defaultObjectCategory", "schemaIDGUID", "distinguishedName"])
194         self.assertEquals(len(res), 1)
195         self.assertEquals(res[0]["lDAPDisplayName"][0], class_ldap_display_name)
196         self.assertEquals(res[0]["defaultObjectCategory"][0], res[0]["distinguishedName"][0])
197         self.assertTrue("schemaIDGUID" in res[0])
198
199         ldif = """
200 dn:
201 changetype: modify
202 add: schemaUpdateNow
203 schemaUpdateNow: 1
204 """
205         self.ldb.modify_ldif(ldif)
206
207         object_name = "obj" + time.strftime("%s", time.gmtime())
208
209         ldif = """
210 dn: CN=%s,CN=Users,%s"""% (object_name, self.base_dn) + """
211 objectClass: organizationalPerson
212 objectClass: person
213 objectClass: """ + class_ldap_display_name + """
214 objectClass: top
215 cn: """ + object_name + """
216 instanceType: 4
217 objectCategory: CN=%s,%s"""% (class_name, self.schema_dn) + """
218 distinguishedName: CN=%s,CN=Users,%s"""% (object_name, self.base_dn) + """
219 name: """ + object_name + """
220 """ + attr_ldap_display_name + """: test
221 """
222         self.ldb.add_ldif(ldif)
223
224         # Search for created object
225         obj_res = self.ldb.search("cn=%s,cn=Users,%s" % (object_name, self.base_dn), scope=SCOPE_BASE, attrs=["replPropertyMetaData"])
226
227         self.assertEquals(len(obj_res), 1)
228         self.assertTrue("replPropertyMetaData" in obj_res[0])
229         val = obj_res[0]["replPropertyMetaData"][0]
230         repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob, str(val))
231         obj = repl.ctr
232
233         # Windows 2000 functional level won't have this.  It is too
234         # hard to work it out from the prefixmap however, so we skip
235         # this test in that case.
236         if msDS_IntId is not None:
237             found = False
238             for o in repl.ctr.array:
239                 if o.attid == msDS_IntId:
240                     found = True
241                     break
242             self.assertTrue(found, "Did not find 0x%08x in replPropertyMetaData" % msDS_IntId)
243         # Delete the object
244         delete_force(self.ldb, "cn=%s,cn=Users,%s" % (object_name, self.base_dn))
245
246     def test_subClassOf(self):
247         """ Testing usage of custom child schamaClass
248         """
249
250         class_name = "my-Class" + time.strftime("%s", time.gmtime())
251         class_ldap_display_name = class_name.replace("-", "")
252
253         ldif = """
254 dn: CN=%s,%s""" % (class_name, self.schema_dn) + """
255 objectClass: top
256 objectClass: classSchema
257 adminDescription: """ + class_name + """
258 adminDisplayName: """ + class_name + """
259 cn: """ + class_name + """
260 governsId: 1.3.6.1.4.1.7165.4.6.2.6.3.""" + str(random.randint(1,100000)) + """
261 instanceType: 4
262 objectClassCategory: 1
263 subClassOf: organizationalUnit
264 systemFlags: 16
265 systemOnly: FALSE
266 """
267         self.ldb.add_ldif(ldif)
268
269         # Search for created objectclass
270         res = []
271         res = self.ldb.search("cn=%s,%s" % (class_name, self.schema_dn), scope=SCOPE_BASE,
272                               attrs=["lDAPDisplayName", "defaultObjectCategory",
273                                      "schemaIDGUID", "distinguishedName"])
274         self.assertEquals(len(res), 1)
275         self.assertEquals(res[0]["lDAPDisplayName"][0], class_ldap_display_name)
276         self.assertEquals(res[0]["defaultObjectCategory"][0], res[0]["distinguishedName"][0])
277         self.assertTrue("schemaIDGUID" in res[0])
278
279         ldif = """
280 dn:
281 changetype: modify
282 add: schemaUpdateNow
283 schemaUpdateNow: 1
284 """
285         self.ldb.modify_ldif(ldif)
286
287         object_name = "org" + time.strftime("%s", time.gmtime())
288
289         ldif = """
290 dn: OU=%s,%s""" % (object_name, self.base_dn) + """
291 objectClass: """ + class_ldap_display_name + """
292 ou: """ + object_name + """
293 instanceType: 4
294 """
295         self.ldb.add_ldif(ldif)
296
297         # Search for created object
298         res = []
299         res = self.ldb.search("ou=%s,%s" % (object_name, self.base_dn), scope=SCOPE_BASE, attrs=["dn"])
300         self.assertEquals(len(res), 1)
301         # Delete the object
302         delete_force(self.ldb, "ou=%s,%s" % (object_name, self.base_dn))
303
304
305     def test_duplicate_attributeID(self):
306         """Testing creating a duplicate attribute"""
307         rand = str(random.randint(1,100000))
308         attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + "-" + rand
309         attr_ldap_display_name = attr_name.replace("-", "")
310         attributeID = "1.3.6.1.4.1.7165.4.6.1.6.2." + rand
311         ldif = """
312 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
313 objectClass: top
314 objectClass: attributeSchema
315 adminDescription: """ + attr_name + """
316 adminDisplayName: """ + attr_name + """
317 cn: """ + attr_name + """
318 attributeId: """ + attributeID + """
319 attributeSyntax: 2.5.5.12
320 omSyntax: 64
321 instanceType: 4
322 isSingleValued: TRUE
323 systemOnly: FALSE
324 """
325         self.ldb.add_ldif(ldif)
326
327         ldif = """
328 dn: CN=%s-dup,%s""" % (attr_name, self.schema_dn) + """
329 objectClass: top
330 objectClass: attributeSchema
331 adminDescription: """ + attr_name + """dup
332 adminDisplayName: """ + attr_name + """dup
333 cn: """ + attr_name + """-dup
334 attributeId: """ + attributeID + """
335 attributeSyntax: 2.5.5.12
336 omSyntax: 64
337 instanceType: 4
338 isSingleValued: TRUE
339 systemOnly: FALSE
340 """
341         try:
342             self.ldb.add_ldif(ldif)
343             self.fail("Should have failed to add duplicate attributeID value")
344         except LdbError, (enum, estr):
345             self.assertEquals(enum, ERR_UNWILLING_TO_PERFORM)
346
347
348     def test_duplicate_attributeID_governsID(self):
349         """Testing creating a duplicate attribute and class"""
350         rand = str(random.randint(1,100000))
351         attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + "-" + rand
352         attr_ldap_display_name = attr_name.replace("-", "")
353         attributeID = "1.3.6.1.4.1.7165.4.6.1.6.3." + rand
354         ldif = """
355 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
356 objectClass: top
357 objectClass: attributeSchema
358 adminDescription: """ + attr_name + """
359 adminDisplayName: """ + attr_name + """
360 cn: """ + attr_name + """
361 attributeId: """ + attributeID + """
362 attributeSyntax: 2.5.5.12
363 omSyntax: 64
364 instanceType: 4
365 isSingleValued: TRUE
366 systemOnly: FALSE
367 """
368         self.ldb.add_ldif(ldif)
369
370         ldif = """
371 dn: CN=%s-dup,%s""" % (attr_name, self.schema_dn) + """
372 objectClass: top
373 objectClass: classSchema
374 adminDescription: """ + attr_name + """dup
375 adminDisplayName: """ + attr_name + """dup
376 cn: """ + attr_name + """-dup
377 governsId: """ + attributeID + """
378 instanceType: 4
379 objectClassCategory: 1
380 subClassOf: organizationalPerson
381 rDNAttID: cn
382 systemMustContain: cn
383 systemOnly: FALSE
384 """
385         try:
386             self.ldb.add_ldif(ldif)
387             self.fail("Should have failed to add duplicate governsID conflicting with attributeID value")
388         except LdbError, (enum, estr):
389             self.assertEquals(enum, ERR_UNWILLING_TO_PERFORM)
390
391
392     def test_duplicate_cn(self):
393         """Testing creating a duplicate attribute"""
394         rand = str(random.randint(1,100000))
395         attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + "-" + rand
396         attr_ldap_display_name = attr_name.replace("-", "")
397         attributeID = "1.3.6.1.4.1.7165.4.6.1.6.4." + rand
398         ldif = """
399 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
400 objectClass: top
401 objectClass: attributeSchema
402 adminDescription: """ + attr_name + """
403 adminDisplayName: """ + attr_name + """
404 cn: """ + attr_name + """
405 attributeId: """ + attributeID + """
406 attributeSyntax: 2.5.5.12
407 omSyntax: 64
408 instanceType: 4
409 isSingleValued: TRUE
410 systemOnly: FALSE
411 """
412         self.ldb.add_ldif(ldif)
413
414         ldif = """
415 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
416 objectClass: top
417 objectClass: attributeSchema
418 adminDescription: """ + attr_name + """dup
419 adminDisplayName: """ + attr_name + """dup
420 cn: """ + attr_name + """
421 attributeId: """ + attributeID + """.1
422 attributeSyntax: 2.5.5.12
423 omSyntax: 64
424 instanceType: 4
425 isSingleValued: TRUE
426 systemOnly: FALSE
427 """
428         try:
429             self.ldb.add_ldif(ldif)
430             self.fail("Should have failed to add attribute with duplicate CN")
431         except LdbError, (enum, estr):
432             self.assertEquals(enum, ERR_ENTRY_ALREADY_EXISTS)
433
434     def test_duplicate_implicit_ldapdisplayname(self):
435         """Testing creating a duplicate attribute ldapdisplayname"""
436         rand = str(random.randint(1,100000))
437         attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + "-" + rand
438         attr_ldap_display_name = attr_name.replace("-", "")
439         attributeID = "1.3.6.1.4.1.7165.4.6.1.6.5." + rand
440         ldif = """
441 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
442 objectClass: top
443 objectClass: attributeSchema
444 adminDescription: """ + attr_name + """
445 adminDisplayName: """ + attr_name + """
446 cn: """ + attr_name + """
447 attributeId: """ + attributeID + """
448 attributeSyntax: 2.5.5.12
449 omSyntax: 64
450 instanceType: 4
451 isSingleValued: TRUE
452 systemOnly: FALSE
453 """
454         self.ldb.add_ldif(ldif)
455
456         ldif = """
457 dn: CN=%s-dup,%s""" % (attr_name, self.schema_dn) + """
458 objectClass: top
459 objectClass: attributeSchema
460 adminDescription: """ + attr_name + """dup
461 adminDisplayName: """ + attr_name + """dup
462 cn: """ + attr_name + """-dup
463 ldapDisplayName: """ + attr_ldap_display_name + """
464 attributeId: """ + attributeID + """.1
465 attributeSyntax: 2.5.5.12
466 omSyntax: 64
467 instanceType: 4
468 isSingleValued: TRUE
469 systemOnly: FALSE
470 """
471         try:
472             self.ldb.add_ldif(ldif)
473             self.fail("Should have failed to add attribute with duplicate of the implicit ldapDisplayName")
474         except LdbError, (enum, estr):
475             self.assertEquals(enum, ERR_UNWILLING_TO_PERFORM)
476
477
478     def test_duplicate_explicit_ldapdisplayname(self):
479         """Testing creating a duplicate attribute ldapdisplayname"""
480         rand = str(random.randint(1,100000))
481         attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + "-" + rand
482         attr_ldap_display_name = attr_name.replace("-", "")
483         attributeID = "1.3.6.1.4.1.7165.4.6.1.6.6." + rand
484         ldif = """
485 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
486 objectClass: top
487 objectClass: attributeSchema
488 adminDescription: """ + attr_name + """
489 adminDisplayName: """ + attr_name + """
490 cn: """ + attr_name + """
491 attributeId: """ + attributeID + """
492 attributeSyntax: 2.5.5.12
493 ldapDisplayName: """ + attr_ldap_display_name + """
494 omSyntax: 64
495 instanceType: 4
496 isSingleValued: TRUE
497 systemOnly: FALSE
498 """
499         self.ldb.add_ldif(ldif)
500
501         ldif = """
502 dn: CN=%s-dup,%s""" % (attr_name, self.schema_dn) + """
503 objectClass: top
504 objectClass: attributeSchema
505 adminDescription: """ + attr_name + """dup
506 adminDisplayName: """ + attr_name + """dup
507 cn: """ + attr_name + """-dup
508 ldapDisplayName: """ + attr_ldap_display_name + """
509 attributeId: """ + attributeID + """.1
510 attributeSyntax: 2.5.5.12
511 omSyntax: 64
512 instanceType: 4
513 isSingleValued: TRUE
514 systemOnly: FALSE
515 """
516         try:
517             self.ldb.add_ldif(ldif)
518             self.fail("Should have failed to add attribute with duplicate ldapDisplayName")
519         except LdbError, (enum, estr):
520             self.assertEquals(enum, ERR_UNWILLING_TO_PERFORM)
521
522
523     def test_duplicate_explicit_ldapdisplayname_with_class(self):
524         """Testing creating a duplicate attribute ldapdisplayname between
525         and attribute and a class"""
526         rand = str(random.randint(1,100000))
527         attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + "-" + rand
528         attr_ldap_display_name = attr_name.replace("-", "")
529         attributeID = "1.3.6.1.4.1.7165.4.6.1.6.7." + rand
530         governsID   = "1.3.6.1.4.1.7165.4.6.2.6.4." + rand
531         ldif = """
532 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
533 objectClass: top
534 objectClass: attributeSchema
535 adminDescription: """ + attr_name + """
536 adminDisplayName: """ + attr_name + """
537 cn: """ + attr_name + """
538 attributeId: """ + attributeID + """
539 attributeSyntax: 2.5.5.12
540 ldapDisplayName: """ + attr_ldap_display_name + """
541 omSyntax: 64
542 instanceType: 4
543 isSingleValued: TRUE
544 systemOnly: FALSE
545 """
546         self.ldb.add_ldif(ldif)
547
548         ldif = """
549 dn: CN=%s-dup,%s""" % (attr_name, self.schema_dn) + """
550 objectClass: top
551 objectClass: classSchema
552 adminDescription: """ + attr_name + """dup
553 adminDisplayName: """ + attr_name + """dup
554 cn: """ + attr_name + """-dup
555 ldapDisplayName: """ + attr_ldap_display_name + """
556 governsID: """ + governsID + """
557 instanceType: 4
558 objectClassCategory: 1
559 subClassOf: organizationalPerson
560 rDNAttID: cn
561 systemMustContain: cn
562 systemOnly: FALSE
563 """
564         try:
565             self.ldb.add_ldif(ldif)
566             self.fail("Should have failed to add class with duplicate ldapDisplayName")
567         except LdbError, (enum, estr):
568             self.assertEquals(enum, ERR_UNWILLING_TO_PERFORM)
569
570
571     def test_duplicate_via_rename_ldapdisplayname(self):
572         """Testing creating a duplicate attribute ldapdisplayname"""
573         rand = str(random.randint(1,100000))
574         attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + "-" + rand
575         attr_ldap_display_name = attr_name.replace("-", "")
576         attributeID = "1.3.6.1.4.1.7165.4.6.1.6.8." + rand
577         ldif = """
578 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
579 objectClass: top
580 objectClass: attributeSchema
581 adminDescription: """ + attr_name + """
582 adminDisplayName: """ + attr_name + """
583 cn: """ + attr_name + """
584 attributeId: """ + attributeID + """
585 attributeSyntax: 2.5.5.12
586 ldapDisplayName: """ + attr_ldap_display_name + """
587 omSyntax: 64
588 instanceType: 4
589 isSingleValued: TRUE
590 systemOnly: FALSE
591 """
592         self.ldb.add_ldif(ldif)
593
594         ldif = """
595 dn: CN=%s-dup,%s""" % (attr_name, self.schema_dn) + """
596 objectClass: top
597 objectClass: attributeSchema
598 adminDescription: """ + attr_name + """dup
599 adminDisplayName: """ + attr_name + """dup
600 cn: """ + attr_name + """-dup
601 ldapDisplayName: """ + attr_ldap_display_name + """dup
602 attributeId: """ + attributeID + """.1
603 attributeSyntax: 2.5.5.12
604 omSyntax: 64
605 instanceType: 4
606 isSingleValued: TRUE
607 systemOnly: FALSE
608 """
609         self.ldb.add_ldif(ldif)
610
611         ldif = """
612 dn: CN=%s-dup,%s""" % (attr_name, self.schema_dn) + """
613 changetype: modify
614 replace: ldapDisplayName
615 ldapDisplayName: """ + attr_ldap_display_name + """
616 -
617 """
618         try:
619             self.ldb.modify_ldif(ldif)
620             self.fail("Should have failed to modify schema to have attribute with duplicate ldapDisplayName")
621         except LdbError, (enum, estr):
622             self.assertEquals(enum, ERR_UNWILLING_TO_PERFORM)
623
624
625     def test_duplicate_via_rename_attributeID(self):
626         """Testing creating a duplicate attributeID"""
627         rand = str(random.randint(1,100000))
628         attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + "-" + rand
629         attr_ldap_display_name = attr_name.replace("-", "")
630         attributeID = "1.3.6.1.4.1.7165.4.6.1.6.9." + rand
631         ldif = """
632 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
633 objectClass: top
634 objectClass: attributeSchema
635 adminDescription: """ + attr_name + """
636 adminDisplayName: """ + attr_name + """
637 cn: """ + attr_name + """
638 attributeId: """ + attributeID + """
639 attributeSyntax: 2.5.5.12
640 ldapDisplayName: """ + attr_ldap_display_name + """
641 omSyntax: 64
642 instanceType: 4
643 isSingleValued: TRUE
644 systemOnly: FALSE
645 """
646         self.ldb.add_ldif(ldif)
647
648         ldif = """
649 dn: CN=%s-dup,%s""" % (attr_name, self.schema_dn) + """
650 objectClass: top
651 objectClass: attributeSchema
652 adminDescription: """ + attr_name + """dup
653 adminDisplayName: """ + attr_name + """dup
654 cn: """ + attr_name + """-dup
655 ldapDisplayName: """ + attr_ldap_display_name + """dup
656 attributeId: """ + attributeID + """.1
657 attributeSyntax: 2.5.5.12
658 omSyntax: 64
659 instanceType: 4
660 isSingleValued: TRUE
661 systemOnly: FALSE
662 """
663         self.ldb.add_ldif(ldif)
664
665         ldif = """
666 dn: CN=%s-dup,%s""" % (attr_name, self.schema_dn) + """
667 changetype: modify
668 replace: attributeId
669 attributeId: """ + attributeID + """
670 -
671 """
672         try:
673             self.ldb.modify_ldif(ldif)
674             self.fail("Should have failed to modify schema to have attribute with duplicate attributeID")
675         except LdbError, (enum, estr):
676             self.assertEquals(enum, ERR_CONSTRAINT_VIOLATION)
677
678     def test_remove_ldapdisplayname(self):
679         """Testing removing the ldapdisplayname"""
680         rand = str(random.randint(1,100000))
681         attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + "-" + rand
682         attr_ldap_display_name = attr_name.replace("-", "")
683         attributeID = "1.3.6.1.4.1.7165.4.6.1.6.10." + rand
684         ldif = """
685 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
686 objectClass: top
687 objectClass: attributeSchema
688 adminDescription: """ + attr_name + """
689 adminDisplayName: """ + attr_name + """
690 cn: """ + attr_name + """
691 attributeId: """ + attributeID + """
692 attributeSyntax: 2.5.5.12
693 ldapDisplayName: """ + attr_ldap_display_name + """
694 omSyntax: 64
695 instanceType: 4
696 isSingleValued: TRUE
697 systemOnly: FALSE
698 """
699         self.ldb.add_ldif(ldif)
700
701         ldif = """
702 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
703 changetype: modify
704 replace: ldapDisplayName
705 -
706 """
707         try:
708             self.ldb.modify_ldif(ldif)
709             self.fail("Should have failed to remove the ldapdisplayname")
710         except LdbError, (enum, estr):
711             self.assertEquals(enum, ERR_OBJECT_CLASS_VIOLATION)
712
713     def test_rename_ldapdisplayname(self):
714         """Testing renaming ldapdisplayname"""
715         rand = str(random.randint(1,100000))
716         attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + "-" + rand
717         attr_ldap_display_name = attr_name.replace("-", "")
718         attributeID = "1.3.6.1.4.1.7165.4.6.1.6.11." + rand
719         ldif = """
720 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
721 objectClass: top
722 objectClass: attributeSchema
723 adminDescription: """ + attr_name + """
724 adminDisplayName: """ + attr_name + """
725 cn: """ + attr_name + """
726 attributeId: """ + attributeID + """
727 attributeSyntax: 2.5.5.12
728 ldapDisplayName: """ + attr_ldap_display_name + """
729 omSyntax: 64
730 instanceType: 4
731 isSingleValued: TRUE
732 systemOnly: FALSE
733 """
734         self.ldb.add_ldif(ldif)
735
736         ldif = """
737 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
738 changetype: modify
739 replace: ldapDisplayName
740 ldapDisplayName: """ + attr_ldap_display_name + """2
741 -
742 """
743         self.ldb.modify_ldif(ldif)
744
745
746     def test_change_attributeID(self):
747         """Testing change the attributeID"""
748         rand = str(random.randint(1,100000))
749         attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + "-" + rand
750         attr_ldap_display_name = attr_name.replace("-", "")
751         attributeID = "1.3.6.1.4.1.7165.4.6.1.6.12." + rand
752         ldif = """
753 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
754 objectClass: top
755 objectClass: attributeSchema
756 adminDescription: """ + attr_name + """
757 adminDisplayName: """ + attr_name + """
758 cn: """ + attr_name + """
759 attributeId: """ + attributeID + """
760 attributeSyntax: 2.5.5.12
761 ldapDisplayName: """ + attr_ldap_display_name + """
762 omSyntax: 64
763 instanceType: 4
764 isSingleValued: TRUE
765 systemOnly: FALSE
766 """
767         self.ldb.add_ldif(ldif)
768
769         ldif = """
770 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
771 changetype: modify
772 replace: attributeID
773 attributeId: """ + attributeID + """.1
774
775 """
776         try:
777             self.ldb.modify_ldif(ldif)
778             self.fail("Should have failed to modify schema to have different attributeID")
779         except LdbError, (enum, estr):
780             self.assertEquals(enum, ERR_CONSTRAINT_VIOLATION)
781
782
783     def test_change_attributeID_same(self):
784         """Testing change the attributeID to the same value"""
785         rand = str(random.randint(1,100000))
786         attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + "-" + rand
787         attr_ldap_display_name = attr_name.replace("-", "")
788         attributeID = "1.3.6.1.4.1.7165.4.6.1.6.13." + rand
789         ldif = """
790 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
791 objectClass: top
792 objectClass: attributeSchema
793 adminDescription: """ + attr_name + """
794 adminDisplayName: """ + attr_name + """
795 cn: """ + attr_name + """
796 attributeId: """ + attributeID + """
797 attributeSyntax: 2.5.5.12
798 ldapDisplayName: """ + attr_ldap_display_name + """
799 omSyntax: 64
800 instanceType: 4
801 isSingleValued: TRUE
802 systemOnly: FALSE
803 """
804         self.ldb.add_ldif(ldif)
805
806         ldif = """
807 dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
808 changetype: modify
809 replace: attributeID
810 attributeId: """ + attributeID + """
811
812 """
813         try:
814             self.ldb.modify_ldif(ldif)
815             self.fail("Should have failed to modify schema to have the same attributeID")
816         except LdbError, (enum, estr):
817             self.assertEquals(enum, ERR_CONSTRAINT_VIOLATION)
818
819
820     def test_change_governsID(self):
821         """Testing change the governsID"""
822         rand = str(random.randint(1,100000))
823         class_name = "test-Class" + time.strftime("%s", time.gmtime()) + "-" + rand
824         class_ldap_display_name = class_name.replace("-", "")
825         governsID = "1.3.6.1.4.1.7165.4.6.2.6.5." + rand
826         ldif = """
827 dn: CN=%s,%s""" % (class_name, self.schema_dn) + """
828 objectClass: top
829 objectClass: classSchema
830 adminDescription: """ + class_name + """
831 adminDisplayName: """ + class_name + """
832 cn: """ + class_name + """
833 governsId: """ + governsID + """
834 ldapDisplayName: """ + class_ldap_display_name + """
835 instanceType: 4
836 objectClassCategory: 1
837 subClassOf: organizationalPerson
838 rDNAttID: cn
839 systemMustContain: cn
840 systemOnly: FALSE
841 """
842         self.ldb.add_ldif(ldif)
843
844         ldif = """
845 dn: CN=%s,%s""" % (class_name, self.schema_dn) + """
846 changetype: modify
847 replace: governsID
848 governsId: """ + governsID + """.1
849
850 """
851         try:
852             self.ldb.modify_ldif(ldif)
853             self.fail("Should have failed to modify schema to have different governsID")
854         except LdbError, (enum, estr):
855             self.assertEquals(enum, ERR_CONSTRAINT_VIOLATION)
856
857
858     def test_change_governsID_same(self):
859         """Testing change the governsID"""
860         rand = str(random.randint(1,100000))
861         class_name = "test-Class" + time.strftime("%s", time.gmtime()) + "-" + rand
862         class_ldap_display_name = class_name.replace("-", "")
863         governsID = "1.3.6.1.4.1.7165.4.6.2.6.6." + rand
864         ldif = """
865 dn: CN=%s,%s""" % (class_name, self.schema_dn) + """
866 objectClass: top
867 objectClass: classSchema
868 adminDescription: """ + class_name + """
869 adminDisplayName: """ + class_name + """
870 cn: """ + class_name + """
871 governsId: """ + governsID + """
872 ldapDisplayName: """ + class_ldap_display_name + """
873 instanceType: 4
874 objectClassCategory: 1
875 subClassOf: organizationalPerson
876 rDNAttID: cn
877 systemMustContain: cn
878 systemOnly: FALSE
879 """
880         self.ldb.add_ldif(ldif)
881
882         ldif = """
883 dn: CN=%s,%s""" % (class_name, self.schema_dn) + """
884 changetype: modify
885 replace: governsID
886 governsId: """ + governsID + """.1
887
888 """
889         try:
890             self.ldb.modify_ldif(ldif)
891             self.fail("Should have failed to modify schema to have the same governsID")
892         except LdbError, (enum, estr):
893             self.assertEquals(enum, ERR_CONSTRAINT_VIOLATION)
894
895
896     def test_subClassOf(self):
897         """ Testing usage of custom child classSchema
898         """
899
900         class_name = "my-Class" + time.strftime("%s", time.gmtime())
901         class_ldap_display_name = class_name.replace("-", "")
902
903         ldif = """
904 dn: CN=%s,%s""" % (class_name, self.schema_dn) + """
905 objectClass: top
906 objectClass: classSchema
907 adminDescription: """ + class_name + """
908 adminDisplayName: """ + class_name + """
909 cn: """ + class_name + """
910 governsId: 1.3.6.1.4.1.7165.4.6.2.6.7.""" + str(random.randint(1,100000)) + """
911 instanceType: 4
912 objectClassCategory: 1
913 subClassOf: organizationalUnit
914 systemFlags: 16
915 systemOnly: FALSE
916 """
917         self.ldb.add_ldif(ldif)
918
919         # Search for created objectclass
920         res = []
921         res = self.ldb.search("cn=%s,%s" % (class_name, self.schema_dn), scope=SCOPE_BASE,
922                               attrs=["lDAPDisplayName", "defaultObjectCategory",
923                                      "schemaIDGUID", "distinguishedName"])
924         self.assertEquals(len(res), 1)
925         self.assertEquals(res[0]["lDAPDisplayName"][0], class_ldap_display_name)
926         self.assertEquals(res[0]["defaultObjectCategory"][0], res[0]["distinguishedName"][0])
927         self.assertTrue("schemaIDGUID" in res[0])
928
929         ldif = """
930 dn:
931 changetype: modify
932 add: schemaUpdateNow
933 schemaUpdateNow: 1
934 """
935         self.ldb.modify_ldif(ldif)
936
937         object_name = "org" + time.strftime("%s", time.gmtime())
938
939         ldif = """
940 dn: OU=%s,%s""" % (object_name, self.base_dn) + """
941 objectClass: """ + class_ldap_display_name + """
942 ou: """ + object_name + """
943 instanceType: 4
944 """
945         self.ldb.add_ldif(ldif)
946
947         # Search for created object
948         res = []
949         res = self.ldb.search("ou=%s,%s" % (object_name, self.base_dn), scope=SCOPE_BASE, attrs=["dn"])
950         self.assertEquals(len(res), 1)
951         # Delete the object
952         delete_force(self.ldb, "ou=%s,%s" % (object_name, self.base_dn))
953
954
955 class SchemaTests_msDS_IntId(samba.tests.TestCase):
956
957     def setUp(self):
958         super(SchemaTests_msDS_IntId, self).setUp()
959         self.ldb = SamDB(host, credentials=creds,
960             session_info=system_session(lp), lp=lp, options=ldb_options)
961         res = self.ldb.search(base="", expression="", scope=SCOPE_BASE,
962                          attrs=["schemaNamingContext", "defaultNamingContext",
963                                 "forestFunctionality"])
964         self.assertEquals(len(res), 1)
965         self.schema_dn = res[0]["schemaNamingContext"][0]
966         self.base_dn = res[0]["defaultNamingContext"][0]
967         self.forest_level = int(res[0]["forestFunctionality"][0])
968
969     def _ldap_schemaUpdateNow(self):
970         ldif = """
971 dn:
972 changetype: modify
973 add: schemaUpdateNow
974 schemaUpdateNow: 1
975 """
976         self.ldb.modify_ldif(ldif)
977
978     def _make_obj_names(self, prefix):
979         class_name = prefix + time.strftime("%s", time.gmtime())
980         class_ldap_name = class_name.replace("-", "")
981         class_dn = "CN=%s,%s" % (class_name, self.schema_dn)
982         return (class_name, class_ldap_name, class_dn)
983
984     def _is_schema_base_object(self, ldb_msg):
985         """Test systemFlags for SYSTEM_FLAG_SCHEMA_BASE_OBJECT (16)"""
986         systemFlags = 0
987         if "systemFlags" in ldb_msg:
988             systemFlags = int(ldb_msg["systemFlags"][0])
989         return (systemFlags & 16) != 0
990
991     def _make_attr_ldif(self, attr_name, attr_dn):
992         ldif = """
993 dn: """ + attr_dn + """
994 objectClass: top
995 objectClass: attributeSchema
996 adminDescription: """ + attr_name + """
997 adminDisplayName: """ + attr_name + """
998 cn: """ + attr_name + """
999 attributeId: 1.3.6.1.4.1.7165.4.6.1.6.14.""" + str(random.randint(1,100000)) + """
1000 attributeSyntax: 2.5.5.12
1001 omSyntax: 64
1002 instanceType: 4
1003 isSingleValued: TRUE
1004 systemOnly: FALSE
1005 """
1006         return ldif
1007
1008     def test_msDS_IntId_on_attr(self):
1009         """Testing msDs-IntId creation for Attributes.
1010         See MS-ADTS - 3.1.1.Attributes
1011
1012         This test should verify that:
1013         - Creating attribute with 'msDS-IntId' fails with ERR_UNWILLING_TO_PERFORM
1014         - Adding 'msDS-IntId' on existing attribute fails with ERR_CONSTRAINT_VIOLATION
1015         - Creating attribute with 'msDS-IntId' set and FLAG_SCHEMA_BASE_OBJECT flag
1016           set fails with ERR_UNWILLING_TO_PERFORM
1017         - Attributes created with FLAG_SCHEMA_BASE_OBJECT not set have
1018           'msDS-IntId' attribute added internally
1019         """
1020
1021         # 1. Create attribute without systemFlags
1022         # msDS-IntId should be created if forest functional
1023         # level is >= DS_DOMAIN_FUNCTION_2003
1024         # and missing otherwise
1025         (attr_name, attr_ldap_name, attr_dn) = self._make_obj_names("msDS-IntId-Attr-1-")
1026         ldif = self._make_attr_ldif(attr_name, attr_dn)
1027
1028         # try to add msDS-IntId during Attribute creation
1029         ldif_fail = ldif + "msDS-IntId: -1993108831\n"
1030         try:
1031             self.ldb.add_ldif(ldif_fail)
1032             self.fail("Adding attribute with preset msDS-IntId should fail")
1033         except LdbError, (num, _):
1034             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
1035
1036         # add the new attribute and update schema
1037         self.ldb.add_ldif(ldif)
1038         self._ldap_schemaUpdateNow()
1039
1040         # Search for created attribute
1041         res = []
1042         res = self.ldb.search(attr_dn, scope=SCOPE_BASE,
1043                               attrs=["lDAPDisplayName", "msDS-IntId", "systemFlags"])
1044         self.assertEquals(len(res), 1)
1045         self.assertEquals(res[0]["lDAPDisplayName"][0], attr_ldap_name)
1046         if self.forest_level >= DS_DOMAIN_FUNCTION_2003:
1047             if self._is_schema_base_object(res[0]):
1048                 self.assertTrue("msDS-IntId" not in res[0])
1049             else:
1050                 self.assertTrue("msDS-IntId" in res[0])
1051         else:
1052             self.assertTrue("msDS-IntId" not in res[0])
1053
1054         msg = Message()
1055         msg.dn = Dn(self.ldb, attr_dn)
1056         msg["msDS-IntId"] = MessageElement("-1993108831", FLAG_MOD_REPLACE, "msDS-IntId")
1057         try:
1058             self.ldb.modify(msg)
1059             self.fail("Modifying msDS-IntId should return error")
1060         except LdbError, (num, _):
1061             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
1062
1063         # 2. Create attribute with systemFlags = FLAG_SCHEMA_BASE_OBJECT
1064         # msDS-IntId should be created if forest functional
1065         # level is >= DS_DOMAIN_FUNCTION_2003
1066         # and missing otherwise
1067         (attr_name, attr_ldap_name, attr_dn) = self._make_obj_names("msDS-IntId-Attr-2-")
1068         ldif = self._make_attr_ldif(attr_name, attr_dn)
1069         ldif += "systemFlags: 16\n"
1070
1071         # try to add msDS-IntId during Attribute creation
1072         ldif_fail = ldif + "msDS-IntId: -1993108831\n"
1073         try:
1074             self.ldb.add_ldif(ldif_fail)
1075             self.fail("Adding attribute with preset msDS-IntId should fail")
1076         except LdbError, (num, _):
1077             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
1078
1079         # add the new attribute and update schema
1080         self.ldb.add_ldif(ldif)
1081         self._ldap_schemaUpdateNow()
1082
1083         # Search for created attribute
1084         res = []
1085         res = self.ldb.search(attr_dn, scope=SCOPE_BASE,
1086                               attrs=["lDAPDisplayName", "msDS-IntId"])
1087         self.assertEquals(len(res), 1)
1088         self.assertEquals(res[0]["lDAPDisplayName"][0], attr_ldap_name)
1089         if self.forest_level >= DS_DOMAIN_FUNCTION_2003:
1090             if self._is_schema_base_object(res[0]):
1091                 self.assertTrue("msDS-IntId" not in res[0])
1092             else:
1093                 self.assertTrue("msDS-IntId" in res[0])
1094         else:
1095             self.assertTrue("msDS-IntId" not in res[0])
1096
1097         msg = Message()
1098         msg.dn = Dn(self.ldb, attr_dn)
1099         msg["msDS-IntId"] = MessageElement("-1993108831", FLAG_MOD_REPLACE, "msDS-IntId")
1100         try:
1101             self.ldb.modify(msg)
1102             self.fail("Modifying msDS-IntId should return error")
1103         except LdbError, (num, _):
1104             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
1105
1106
1107     def _make_class_ldif(self, class_dn, class_name, sub_oid):
1108         ldif = """
1109 dn: """ + class_dn + """
1110 objectClass: top
1111 objectClass: classSchema
1112 adminDescription: """ + class_name + """
1113 adminDisplayName: """ + class_name + """
1114 cn: """ + class_name + """
1115 governsId: 1.3.6.1.4.1.7165.4.6.2.6.%d.""" % sub_oid + str(random.randint(1,100000)) + """
1116 instanceType: 4
1117 objectClassCategory: 1
1118 subClassOf: organizationalPerson
1119 rDNAttID: cn
1120 systemMustContain: cn
1121 systemOnly: FALSE
1122 """
1123         return ldif
1124
1125     def test_msDS_IntId_on_class(self):
1126         """Testing msDs-IntId creation for Class
1127            Reference: MS-ADTS - 3.1.1.2.4.8 Class classSchema"""
1128
1129         # 1. Create Class without systemFlags
1130         # msDS-IntId should be created if forest functional
1131         # level is >= DS_DOMAIN_FUNCTION_2003
1132         # and missing otherwise
1133         (class_name, class_ldap_name, class_dn) = self._make_obj_names("msDS-IntId-Class-1-")
1134         ldif = self._make_class_ldif(class_dn, class_name, 8)
1135
1136         # try to add msDS-IntId during Class creation
1137         ldif_add = ldif + "msDS-IntId: -1993108831\n"
1138         self.ldb.add_ldif(ldif_add)
1139         self._ldap_schemaUpdateNow()
1140
1141         res = self.ldb.search(class_dn, scope=SCOPE_BASE, attrs=["msDS-IntId"])
1142         self.assertEquals(len(res), 1)
1143         self.assertEquals(res[0]["msDS-IntId"][0], "-1993108831")
1144
1145         # add a new Class and update schema
1146         (class_name, class_ldap_name, class_dn) = self._make_obj_names("msDS-IntId-Class-2-")
1147         ldif = self._make_class_ldif(class_dn, class_name, 9)
1148
1149         self.ldb.add_ldif(ldif)
1150         self._ldap_schemaUpdateNow()
1151
1152         # Search for created Class
1153         res = self.ldb.search(class_dn, scope=SCOPE_BASE, attrs=["msDS-IntId"])
1154         self.assertEquals(len(res), 1)
1155         self.assertFalse("msDS-IntId" in res[0])
1156
1157         msg = Message()
1158         msg.dn = Dn(self.ldb, class_dn)
1159         msg["msDS-IntId"] = MessageElement("-1993108831", FLAG_MOD_REPLACE, "msDS-IntId")
1160         try:
1161             self.ldb.modify(msg)
1162             self.fail("Modifying msDS-IntId should return error")
1163         except LdbError, (num, _):
1164             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
1165
1166         # 2. Create Class with systemFlags = FLAG_SCHEMA_BASE_OBJECT
1167         # msDS-IntId should be created if forest functional
1168         # level is >= DS_DOMAIN_FUNCTION_2003
1169         # and missing otherwise
1170         (class_name, class_ldap_name, class_dn) = self._make_obj_names("msDS-IntId-Class-3-")
1171         ldif = self._make_class_ldif(class_dn, class_name, 10)
1172         ldif += "systemFlags: 16\n"
1173
1174         # try to add msDS-IntId during Class creation
1175         ldif_add = ldif + "msDS-IntId: -1993108831\n"
1176         self.ldb.add_ldif(ldif_add)
1177
1178         res = self.ldb.search(class_dn, scope=SCOPE_BASE, attrs=["msDS-IntId"])
1179         self.assertEquals(len(res), 1)
1180         self.assertEquals(res[0]["msDS-IntId"][0], "-1993108831")
1181
1182         # add the new Class and update schema
1183         (class_name, class_ldap_name, class_dn) = self._make_obj_names("msDS-IntId-Class-4-")
1184         ldif = self._make_class_ldif(class_dn, class_name, 11)
1185         ldif += "systemFlags: 16\n"
1186
1187         self.ldb.add_ldif(ldif)
1188         self._ldap_schemaUpdateNow()
1189
1190         # Search for created Class
1191         res = self.ldb.search(class_dn, scope=SCOPE_BASE, attrs=["msDS-IntId"])
1192         self.assertEquals(len(res), 1)
1193         self.assertFalse("msDS-IntId" in res[0])
1194
1195         msg = Message()
1196         msg.dn = Dn(self.ldb, class_dn)
1197         msg["msDS-IntId"] = MessageElement("-1993108831", FLAG_MOD_REPLACE, "msDS-IntId")
1198         try:
1199             self.ldb.modify(msg)
1200             self.fail("Modifying msDS-IntId should return error")
1201         except LdbError, (num, _):
1202             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
1203         res = self.ldb.search(class_dn, scope=SCOPE_BASE, attrs=["msDS-IntId"])
1204         self.assertEquals(len(res), 1)
1205         self.assertFalse("msDS-IntId" in res[0])
1206
1207
1208     def test_verify_msDS_IntId(self):
1209         """Verify msDS-IntId exists only on attributes without FLAG_SCHEMA_BASE_OBJECT flag set"""
1210         count = 0
1211         res = self.ldb.search(self.schema_dn, scope=SCOPE_ONELEVEL,
1212                               expression="objectClass=attributeSchema",
1213                               attrs=["systemFlags", "msDS-IntId", "attributeID", "cn"])
1214         self.assertTrue(len(res) > 1)
1215         for ldb_msg in res:
1216             if self.forest_level >= DS_DOMAIN_FUNCTION_2003:
1217                 if self._is_schema_base_object(ldb_msg):
1218                     self.assertTrue("msDS-IntId" not in ldb_msg)
1219                 else:
1220                     # don't assert here as there are plenty of
1221                     # attributes under w2k8 that are not part of
1222                     # Base Schema (SYSTEM_FLAG_SCHEMA_BASE_OBJECT flag not set)
1223                     # has not msDS-IntId attribute set
1224                     #self.assertTrue("msDS-IntId" in ldb_msg, "msDS-IntId expected on: %s" % ldb_msg.dn)
1225                     if "msDS-IntId" not in ldb_msg:
1226                         count = count + 1
1227                         print "%3d warning: msDS-IntId expected on: %-30s %s" % (count, ldb_msg["attributeID"], ldb_msg["cn"])
1228             else:
1229                 self.assertTrue("msDS-IntId" not in ldb_msg)
1230
1231
1232 class SchemaTests_msDS_isRODC(samba.tests.TestCase):
1233
1234     def setUp(self):
1235         super(SchemaTests_msDS_isRODC, self).setUp()
1236         self.ldb =  SamDB(host, credentials=creds,
1237             session_info=system_session(lp), lp=lp, options=ldb_options)
1238         res = self.ldb.search(base="", expression="", scope=SCOPE_BASE, attrs=["defaultNamingContext"])
1239         self.assertEquals(len(res), 1)
1240         self.base_dn = res[0]["defaultNamingContext"][0]
1241
1242     def test_objectClass_ntdsdsa(self):
1243         res = self.ldb.search(self.base_dn, expression="objectClass=nTDSDSA",
1244                               attrs=["msDS-isRODC"], controls=["search_options:1:2"])
1245         for ldb_msg in res:
1246             self.assertTrue("msDS-isRODC" in ldb_msg)
1247
1248     def test_objectClass_server(self):
1249         res = self.ldb.search(self.base_dn, expression="objectClass=server",
1250                               attrs=["msDS-isRODC"], controls=["search_options:1:2"])
1251         for ldb_msg in res:
1252             ntds_search_dn = "CN=NTDS Settings,%s" % ldb_msg['dn']
1253             try:
1254                 res_check = self.ldb.search(ntds_search_dn, attrs=["objectCategory"])
1255             except LdbError, (num, _):
1256                 self.assertEquals(num, ERR_NO_SUCH_OBJECT)
1257                 print("Server entry %s doesn't have a NTDS settings object" % res[0]['dn'])
1258             else:
1259                 self.assertTrue("objectCategory" in res_check[0])
1260                 self.assertTrue("msDS-isRODC" in ldb_msg)
1261
1262     def test_objectClass_computer(self):
1263         res = self.ldb.search(self.base_dn, expression="objectClass=computer",
1264                               attrs=["serverReferenceBL","msDS-isRODC"], controls=["search_options:1:2"])
1265         for ldb_msg in res:
1266             if "serverReferenceBL" not in ldb_msg:
1267                 print("Computer entry %s doesn't have a serverReferenceBL attribute" % ldb_msg['dn'])
1268             else:
1269                 self.assertTrue("msDS-isRODC" in ldb_msg)
1270
1271 if not "://" in host:
1272     if os.path.isfile(host):
1273         host = "tdb://%s" % host
1274     else:
1275         host = "ldap://%s" % host
1276
1277 ldb_options = []
1278 if host.startswith("ldap://"):
1279     # user 'paged_search' module when connecting remotely
1280     ldb_options = ["modules:paged_searches"]
1281
1282 TestProgram(module=__name__, opts=subunitopts)