summaryrefslogtreecommitdiffstats
path: root/source4/dsdb/tests/python/ldap_schema.py
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 17:47:29 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 17:47:29 +0000
commit4f5791ebd03eaec1c7da0865a383175b05102712 (patch)
tree8ce7b00f7a76baa386372422adebbe64510812d4 /source4/dsdb/tests/python/ldap_schema.py
parentInitial commit. (diff)
downloadsamba-upstream.tar.xz
samba-upstream.zip
Adding upstream version 2:4.17.12+dfsg.upstream/2%4.17.12+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'source4/dsdb/tests/python/ldap_schema.py')
-rwxr-xr-xsource4/dsdb/tests/python/ldap_schema.py1601
1 files changed, 1601 insertions, 0 deletions
diff --git a/source4/dsdb/tests/python/ldap_schema.py b/source4/dsdb/tests/python/ldap_schema.py
new file mode 100755
index 0000000..b08aa7f
--- /dev/null
+++ b/source4/dsdb/tests/python/ldap_schema.py
@@ -0,0 +1,1601 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# This is a port of the original in testprogs/ejs/ldap.js
+
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2008-2011
+# Copyright (C) Catalyst.Net Ltd 2017
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+import optparse
+import sys
+import time
+import random
+import os
+
+sys.path.insert(0, "bin/python")
+import samba
+from samba.tests.subunitrun import TestProgram, SubunitOptions
+
+import samba.getopt as options
+
+from samba.auth import system_session
+from ldb import SCOPE_ONELEVEL, SCOPE_BASE, LdbError
+from ldb import ERR_NO_SUCH_OBJECT
+from ldb import ERR_UNWILLING_TO_PERFORM
+from ldb import ERR_ENTRY_ALREADY_EXISTS
+from ldb import ERR_CONSTRAINT_VIOLATION
+from ldb import ERR_OBJECT_CLASS_VIOLATION
+from ldb import Message, MessageElement, Dn
+from ldb import FLAG_MOD_REPLACE
+from samba.samdb import SamDB
+from samba.dsdb import DS_DOMAIN_FUNCTION_2003
+from samba.tests import delete_force
+from samba.ndr import ndr_unpack
+from samba.dcerpc import drsblobs
+
+parser = optparse.OptionParser("ldap_schema.py [options] <host>")
+sambaopts = options.SambaOptions(parser)
+parser.add_option_group(sambaopts)
+parser.add_option_group(options.VersionOptions(parser))
+# use command line creds if available
+credopts = options.CredentialsOptions(parser)
+parser.add_option_group(credopts)
+subunitopts = SubunitOptions(parser)
+parser.add_option_group(subunitopts)
+opts, args = parser.parse_args()
+
+if len(args) < 1:
+ parser.print_usage()
+ sys.exit(1)
+
+host = args[0]
+
+lp = sambaopts.get_loadparm()
+creds = credopts.get_credentials(lp)
+
+
+class SchemaTests(samba.tests.TestCase):
+
+ def setUp(self):
+ super(SchemaTests, self).setUp()
+ self.ldb = SamDB(host, credentials=creds,
+ session_info=system_session(lp), lp=lp, options=ldb_options)
+ self.base_dn = self.ldb.domain_dn()
+ self.schema_dn = self.ldb.get_schema_basedn().get_linearized()
+
+ def test_generated_schema(self):
+ """Testing we can read the generated schema via LDAP"""
+ res = self.ldb.search("cn=aggregate," + self.schema_dn, scope=SCOPE_BASE,
+ attrs=["objectClasses", "attributeTypes", "dITContentRules"])
+ self.assertEqual(len(res), 1)
+ self.assertTrue("dITContentRules" in res[0])
+ self.assertTrue("objectClasses" in res[0])
+ self.assertTrue("attributeTypes" in res[0])
+
+ def test_generated_schema_is_operational(self):
+ """Testing we don't get the generated schema via LDAP by default"""
+ # Must keep the "*" form
+ res = self.ldb.search("cn=aggregate," + self.schema_dn, scope=SCOPE_BASE,
+ attrs=["*"])
+ self.assertEqual(len(res), 1)
+ self.assertFalse("dITContentRules" in res[0])
+ self.assertFalse("objectClasses" in res[0])
+ self.assertFalse("attributeTypes" in res[0])
+
+ def test_schemaUpdateNow(self):
+ """Testing schemaUpdateNow"""
+ rand = str(random.randint(1, 100000))
+ attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + "-" + rand
+ attr_ldap_display_name = attr_name.replace("-", "")
+
+ ldif = """
+dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
+objectClass: top
+objectClass: attributeSchema
+adminDescription: """ + attr_name + """
+adminDisplayName: """ + attr_name + """
+cn: """ + attr_name + """
+attributeId: 1.3.6.1.4.1.7165.4.6.1.6.1.""" + rand + """
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+instanceType: 4
+isSingleValued: TRUE
+systemOnly: FALSE
+"""
+ self.ldb.add_ldif(ldif)
+ # We must do a schemaUpdateNow otherwise it's not 100% sure that the schema
+ # will contain the new attribute
+ ldif = """
+dn:
+changetype: modify
+add: schemaUpdateNow
+schemaUpdateNow: 1
+"""
+ self.ldb.modify_ldif(ldif)
+
+ # Search for created attribute
+ res = []
+ res = self.ldb.search("cn=%s,%s" % (attr_name, self.schema_dn), scope=SCOPE_BASE,
+ attrs=["lDAPDisplayName", "schemaIDGUID", "msDS-IntID"])
+ self.assertEqual(len(res), 1)
+ self.assertEqual(str(res[0]["lDAPDisplayName"][0]), attr_ldap_display_name)
+ self.assertTrue("schemaIDGUID" in res[0])
+ if "msDS-IntId" in res[0]:
+ msDS_IntId = int(res[0]["msDS-IntId"][0])
+ if msDS_IntId < 0:
+ msDS_IntId += (1 << 32)
+ else:
+ msDS_IntId = None
+
+ class_name = "test-Class" + time.strftime("%s", time.gmtime())
+ class_ldap_display_name = class_name.replace("-", "")
+
+ # First try to create a class with a wrong "defaultObjectCategory"
+ ldif = """
+dn: CN=%s,%s""" % (class_name, self.schema_dn) + """
+objectClass: top
+objectClass: classSchema
+defaultObjectCategory: CN=_
+adminDescription: """ + class_name + """
+adminDisplayName: """ + class_name + """
+cn: """ + class_name + """
+governsId: 1.3.6.1.4.1.7165.4.6.2.6.1.""" + str(random.randint(1, 100000)) + """
+instanceType: 4
+objectClassCategory: 1
+subClassOf: organizationalPerson
+systemFlags: 16
+rDNAttID: cn
+systemMustContain: cn
+systemMustContain: """ + attr_ldap_display_name + """
+systemOnly: FALSE
+"""
+ try:
+ self.ldb.add_ldif(ldif)
+ self.fail()
+ except LdbError as e1:
+ (num, _) = e1.args
+ self.assertEqual(num, ERR_CONSTRAINT_VIOLATION)
+
+ ldif = """
+dn: CN=%s,%s""" % (class_name, self.schema_dn) + """
+objectClass: top
+objectClass: classSchema
+adminDescription: """ + class_name + """
+adminDisplayName: """ + class_name + """
+cn: """ + class_name + """
+governsId: 1.3.6.1.4.1.7165.4.6.2.6.2.""" + str(random.randint(1, 100000)) + """
+instanceType: 4
+objectClassCategory: 1
+subClassOf: organizationalPerson
+systemFlags: 16
+rDNAttID: cn
+systemMustContain: cn
+systemMustContain: """ + attr_ldap_display_name + """
+systemOnly: FALSE
+"""
+ self.ldb.add_ldif(ldif)
+
+ # Search for created objectclass
+ res = []
+ res = self.ldb.search("cn=%s,%s" % (class_name, self.schema_dn), scope=SCOPE_BASE,
+ attrs=["lDAPDisplayName", "defaultObjectCategory", "schemaIDGUID", "distinguishedName"])
+ self.assertEqual(len(res), 1)
+ self.assertEqual(str(res[0]["lDAPDisplayName"][0]), class_ldap_display_name)
+ self.assertEqual(res[0]["defaultObjectCategory"][0], res[0]["distinguishedName"][0])
+ self.assertTrue("schemaIDGUID" in res[0])
+
+ ldif = """
+dn:
+changetype: modify
+add: schemaUpdateNow
+schemaUpdateNow: 1
+"""
+ self.ldb.modify_ldif(ldif)
+
+ object_name = "obj" + time.strftime("%s", time.gmtime())
+
+ ldif = """
+dn: CN=%s,CN=Users,%s""" % (object_name, self.base_dn) + """
+objectClass: organizationalPerson
+objectClass: person
+objectClass: """ + class_ldap_display_name + """
+objectClass: top
+cn: """ + object_name + """
+instanceType: 4
+objectCategory: CN=%s,%s""" % (class_name, self.schema_dn) + """
+distinguishedName: CN=%s,CN=Users,%s""" % (object_name, self.base_dn) + """
+name: """ + object_name + """
+""" + attr_ldap_display_name + """: test
+"""
+ self.ldb.add_ldif(ldif)
+
+ # Search for created object
+ obj_res = self.ldb.search("cn=%s,cn=Users,%s" % (object_name, self.base_dn), scope=SCOPE_BASE, attrs=["replPropertyMetaData"])
+
+ self.assertEqual(len(obj_res), 1)
+ self.assertTrue("replPropertyMetaData" in obj_res[0])
+ val = obj_res[0]["replPropertyMetaData"][0]
+ repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob, val)
+ obj = repl.ctr
+
+ # Windows 2000 functional level won't have this. It is too
+ # hard to work it out from the prefixmap however, so we skip
+ # this test in that case.
+ if msDS_IntId is not None:
+ found = False
+ for o in repl.ctr.array:
+ if o.attid == msDS_IntId:
+ found = True
+ break
+ self.assertTrue(found, "Did not find 0x%08x in replPropertyMetaData" % msDS_IntId)
+ # Delete the object
+ delete_force(self.ldb, "cn=%s,cn=Users,%s" % (object_name, self.base_dn))
+
+ def test_subClassOf(self):
+ """ Testing usage of custom child classSchema
+ """
+
+ class_name = "my-Class" + time.strftime("%s", time.gmtime())
+ class_ldap_display_name = class_name.replace("-", "")
+
+ ldif = """
+dn: CN=%s,%s""" % (class_name, self.schema_dn) + """
+objectClass: top
+objectClass: classSchema
+adminDescription: """ + class_name + """
+adminDisplayName: """ + class_name + """
+cn: """ + class_name + """
+governsId: 1.3.6.1.4.1.7165.4.6.2.6.3.""" + str(random.randint(1, 100000)) + """
+instanceType: 4
+objectClassCategory: 1
+subClassOf: organizationalUnit
+systemFlags: 16
+systemOnly: FALSE
+"""
+ self.ldb.add_ldif(ldif)
+
+ # Search for created objectclass
+ res = []
+ res = self.ldb.search("cn=%s,%s" % (class_name, self.schema_dn), scope=SCOPE_BASE,
+ attrs=["lDAPDisplayName", "defaultObjectCategory",
+ "schemaIDGUID", "distinguishedName"])
+ self.assertEqual(len(res), 1)
+ self.assertEqual(str(res[0]["lDAPDisplayName"][0]), class_ldap_display_name)
+ self.assertEqual(res[0]["defaultObjectCategory"][0], res[0]["distinguishedName"][0])
+ self.assertTrue("schemaIDGUID" in res[0])
+
+ ldif = """
+dn:
+changetype: modify
+add: schemaUpdateNow
+schemaUpdateNow: 1
+"""
+ self.ldb.modify_ldif(ldif)
+
+ object_name = "org" + time.strftime("%s", time.gmtime())
+
+ ldif = """
+dn: OU=%s,%s""" % (object_name, self.base_dn) + """
+objectClass: """ + class_ldap_display_name + """
+ou: """ + object_name + """
+instanceType: 4
+"""
+ self.ldb.add_ldif(ldif)
+
+ # Search for created object
+ res = []
+ res = self.ldb.search("ou=%s,%s" % (object_name, self.base_dn), scope=SCOPE_BASE, attrs=["dn"])
+ self.assertEqual(len(res), 1)
+ # Delete the object
+ delete_force(self.ldb, "ou=%s,%s" % (object_name, self.base_dn))
+
+ def test_duplicate_attributeID(self):
+ """Testing creating a duplicate attribute"""
+ rand = str(random.randint(1, 100000))
+ attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + "-" + rand
+ attr_ldap_display_name = attr_name.replace("-", "")
+ attributeID = "1.3.6.1.4.1.7165.4.6.1.6.2." + rand
+ ldif = """
+dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
+objectClass: top
+objectClass: attributeSchema
+adminDescription: """ + attr_name + """
+adminDisplayName: """ + attr_name + """
+cn: """ + attr_name + """
+attributeId: """ + attributeID + """
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+instanceType: 4
+isSingleValued: TRUE
+systemOnly: FALSE
+"""
+ self.ldb.add_ldif(ldif)
+
+ ldif = """
+dn: CN=%s-dup,%s""" % (attr_name, self.schema_dn) + """
+objectClass: top
+objectClass: attributeSchema
+adminDescription: """ + attr_name + """dup
+adminDisplayName: """ + attr_name + """dup
+cn: """ + attr_name + """-dup
+attributeId: """ + attributeID + """
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+instanceType: 4
+isSingleValued: TRUE
+systemOnly: FALSE
+"""
+ try:
+ self.ldb.add_ldif(ldif)
+ self.fail("Should have failed to add duplicate attributeID value")
+ except LdbError as e2:
+ (enum, estr) = e2.args
+ self.assertEqual(enum, ERR_UNWILLING_TO_PERFORM)
+
+ def test_duplicate_attributeID_governsID(self):
+ """Testing creating a duplicate attribute and class"""
+ rand = str(random.randint(1, 100000))
+ attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + "-" + rand
+ attr_ldap_display_name = attr_name.replace("-", "")
+ attributeID = "1.3.6.1.4.1.7165.4.6.1.6.3." + rand
+ ldif = """
+dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
+objectClass: top
+objectClass: attributeSchema
+adminDescription: """ + attr_name + """
+adminDisplayName: """ + attr_name + """
+cn: """ + attr_name + """
+attributeId: """ + attributeID + """
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+instanceType: 4
+isSingleValued: TRUE
+systemOnly: FALSE
+"""
+ self.ldb.add_ldif(ldif)
+
+ ldif = """
+dn: CN=%s-dup,%s""" % (attr_name, self.schema_dn) + """
+objectClass: top
+objectClass: classSchema
+adminDescription: """ + attr_name + """dup
+adminDisplayName: """ + attr_name + """dup
+cn: """ + attr_name + """-dup
+governsId: """ + attributeID + """
+instanceType: 4
+objectClassCategory: 1
+subClassOf: organizationalPerson
+rDNAttID: cn
+systemMustContain: cn
+systemOnly: FALSE
+"""
+ try:
+ self.ldb.add_ldif(ldif)
+ self.fail("Should have failed to add duplicate governsID conflicting with attributeID value")
+ except LdbError as e3:
+ (enum, estr) = e3.args
+ self.assertEqual(enum, ERR_UNWILLING_TO_PERFORM)
+
+ def test_duplicate_cn(self):
+ """Testing creating a duplicate attribute"""
+ rand = str(random.randint(1, 100000))
+ attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + "-" + rand
+ attr_ldap_display_name = attr_name.replace("-", "")
+ attributeID = "1.3.6.1.4.1.7165.4.6.1.6.4." + rand
+ ldif = """
+dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
+objectClass: top
+objectClass: attributeSchema
+adminDescription: """ + attr_name + """
+adminDisplayName: """ + attr_name + """
+cn: """ + attr_name + """
+attributeId: """ + attributeID + """
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+instanceType: 4
+isSingleValued: TRUE
+systemOnly: FALSE
+"""
+ self.ldb.add_ldif(ldif)
+
+ ldif = """
+dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
+objectClass: top
+objectClass: attributeSchema
+adminDescription: """ + attr_name + """dup
+adminDisplayName: """ + attr_name + """dup
+cn: """ + attr_name + """
+attributeId: """ + attributeID + """.1
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+instanceType: 4
+isSingleValued: TRUE
+systemOnly: FALSE
+"""
+ try:
+ self.ldb.add_ldif(ldif)
+ self.fail("Should have failed to add attribute with duplicate CN")
+ except LdbError as e4:
+ (enum, estr) = e4.args
+ self.assertEqual(enum, ERR_ENTRY_ALREADY_EXISTS)
+
+ def test_duplicate_implicit_ldapdisplayname(self):
+ """Testing creating a duplicate attribute ldapdisplayname"""
+ rand = str(random.randint(1, 100000))
+ attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + "-" + rand
+ attr_ldap_display_name = attr_name.replace("-", "")
+ attributeID = "1.3.6.1.4.1.7165.4.6.1.6.5." + rand
+ ldif = """
+dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
+objectClass: top
+objectClass: attributeSchema
+adminDescription: """ + attr_name + """
+adminDisplayName: """ + attr_name + """
+cn: """ + attr_name + """
+attributeId: """ + attributeID + """
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+instanceType: 4
+isSingleValued: TRUE
+systemOnly: FALSE
+"""
+ self.ldb.add_ldif(ldif)
+
+ ldif = """
+dn: CN=%s-dup,%s""" % (attr_name, self.schema_dn) + """
+objectClass: top
+objectClass: attributeSchema
+adminDescription: """ + attr_name + """dup
+adminDisplayName: """ + attr_name + """dup
+cn: """ + attr_name + """-dup
+ldapDisplayName: """ + attr_ldap_display_name + """
+attributeId: """ + attributeID + """.1
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+instanceType: 4
+isSingleValued: TRUE
+systemOnly: FALSE
+"""
+ try:
+ self.ldb.add_ldif(ldif)
+ self.fail("Should have failed to add attribute with duplicate of the implicit ldapDisplayName")
+ except LdbError as e5:
+ (enum, estr) = e5.args
+ self.assertEqual(enum, ERR_UNWILLING_TO_PERFORM)
+
+ def test_duplicate_explicit_ldapdisplayname(self):
+ """Testing creating a duplicate attribute ldapdisplayname"""
+ rand = str(random.randint(1, 100000))
+ attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + "-" + rand
+ attr_ldap_display_name = attr_name.replace("-", "")
+ attributeID = "1.3.6.1.4.1.7165.4.6.1.6.6." + rand
+ ldif = """
+dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
+objectClass: top
+objectClass: attributeSchema
+adminDescription: """ + attr_name + """
+adminDisplayName: """ + attr_name + """
+cn: """ + attr_name + """
+attributeId: """ + attributeID + """
+attributeSyntax: 2.5.5.12
+ldapDisplayName: """ + attr_ldap_display_name + """
+omSyntax: 64
+instanceType: 4
+isSingleValued: TRUE
+systemOnly: FALSE
+"""
+ self.ldb.add_ldif(ldif)
+
+ ldif = """
+dn: CN=%s-dup,%s""" % (attr_name, self.schema_dn) + """
+objectClass: top
+objectClass: attributeSchema
+adminDescription: """ + attr_name + """dup
+adminDisplayName: """ + attr_name + """dup
+cn: """ + attr_name + """-dup
+ldapDisplayName: """ + attr_ldap_display_name + """
+attributeId: """ + attributeID + """.1
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+instanceType: 4
+isSingleValued: TRUE
+systemOnly: FALSE
+"""
+ try:
+ self.ldb.add_ldif(ldif)
+ self.fail("Should have failed to add attribute with duplicate ldapDisplayName")
+ except LdbError as e6:
+ (enum, estr) = e6.args
+ self.assertEqual(enum, ERR_UNWILLING_TO_PERFORM)
+
+ def test_duplicate_explicit_ldapdisplayname_with_class(self):
+ """Testing creating a duplicate attribute ldapdisplayname between
+ and attribute and a class"""
+ rand = str(random.randint(1, 100000))
+ attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + "-" + rand
+ attr_ldap_display_name = attr_name.replace("-", "")
+ attributeID = "1.3.6.1.4.1.7165.4.6.1.6.7." + rand
+ governsID = "1.3.6.1.4.1.7165.4.6.2.6.4." + rand
+ ldif = """
+dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
+objectClass: top
+objectClass: attributeSchema
+adminDescription: """ + attr_name + """
+adminDisplayName: """ + attr_name + """
+cn: """ + attr_name + """
+attributeId: """ + attributeID + """
+attributeSyntax: 2.5.5.12
+ldapDisplayName: """ + attr_ldap_display_name + """
+omSyntax: 64
+instanceType: 4
+isSingleValued: TRUE
+systemOnly: FALSE
+"""
+ self.ldb.add_ldif(ldif)
+
+ ldif = """
+dn: CN=%s-dup,%s""" % (attr_name, self.schema_dn) + """
+objectClass: top
+objectClass: classSchema
+adminDescription: """ + attr_name + """dup
+adminDisplayName: """ + attr_name + """dup
+cn: """ + attr_name + """-dup
+ldapDisplayName: """ + attr_ldap_display_name + """
+governsID: """ + governsID + """
+instanceType: 4
+objectClassCategory: 1
+subClassOf: organizationalPerson
+rDNAttID: cn
+systemMustContain: cn
+systemOnly: FALSE
+"""
+ try:
+ self.ldb.add_ldif(ldif)
+ self.fail("Should have failed to add class with duplicate ldapDisplayName")
+ except LdbError as e7:
+ (enum, estr) = e7.args
+ self.assertEqual(enum, ERR_UNWILLING_TO_PERFORM)
+
+ def test_duplicate_via_rename_ldapdisplayname(self):
+ """Testing creating a duplicate attribute ldapdisplayname"""
+ rand = str(random.randint(1, 100000))
+ attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + "-" + rand
+ attr_ldap_display_name = attr_name.replace("-", "")
+ attributeID = "1.3.6.1.4.1.7165.4.6.1.6.8." + rand
+ ldif = """
+dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
+objectClass: top
+objectClass: attributeSchema
+adminDescription: """ + attr_name + """
+adminDisplayName: """ + attr_name + """
+cn: """ + attr_name + """
+attributeId: """ + attributeID + """
+attributeSyntax: 2.5.5.12
+ldapDisplayName: """ + attr_ldap_display_name + """
+omSyntax: 64
+instanceType: 4
+isSingleValued: TRUE
+systemOnly: FALSE
+"""
+ self.ldb.add_ldif(ldif)
+
+ ldif = """
+dn: CN=%s-dup,%s""" % (attr_name, self.schema_dn) + """
+objectClass: top
+objectClass: attributeSchema
+adminDescription: """ + attr_name + """dup
+adminDisplayName: """ + attr_name + """dup
+cn: """ + attr_name + """-dup
+ldapDisplayName: """ + attr_ldap_display_name + """dup
+attributeId: """ + attributeID + """.1
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+instanceType: 4
+isSingleValued: TRUE
+systemOnly: FALSE
+"""
+ self.ldb.add_ldif(ldif)
+
+ ldif = """
+dn: CN=%s-dup,%s""" % (attr_name, self.schema_dn) + """
+changetype: modify
+replace: ldapDisplayName
+ldapDisplayName: """ + attr_ldap_display_name + """
+-
+"""
+ try:
+ self.ldb.modify_ldif(ldif)
+ self.fail("Should have failed to modify schema to have attribute with duplicate ldapDisplayName")
+ except LdbError as e8:
+ (enum, estr) = e8.args
+ self.assertEqual(enum, ERR_UNWILLING_TO_PERFORM)
+
+ def test_duplicate_via_rename_attributeID(self):
+ """Testing creating a duplicate attributeID"""
+ rand = str(random.randint(1, 100000))
+ attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + "-" + rand
+ attr_ldap_display_name = attr_name.replace("-", "")
+ attributeID = "1.3.6.1.4.1.7165.4.6.1.6.9." + rand
+ ldif = """
+dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
+objectClass: top
+objectClass: attributeSchema
+adminDescription: """ + attr_name + """
+adminDisplayName: """ + attr_name + """
+cn: """ + attr_name + """
+attributeId: """ + attributeID + """
+attributeSyntax: 2.5.5.12
+ldapDisplayName: """ + attr_ldap_display_name + """
+omSyntax: 64
+instanceType: 4
+isSingleValued: TRUE
+systemOnly: FALSE
+"""
+ self.ldb.add_ldif(ldif)
+
+ ldif = """
+dn: CN=%s-dup,%s""" % (attr_name, self.schema_dn) + """
+objectClass: top
+objectClass: attributeSchema
+adminDescription: """ + attr_name + """dup
+adminDisplayName: """ + attr_name + """dup
+cn: """ + attr_name + """-dup
+ldapDisplayName: """ + attr_ldap_display_name + """dup
+attributeId: """ + attributeID + """.1
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+instanceType: 4
+isSingleValued: TRUE
+systemOnly: FALSE
+"""
+ self.ldb.add_ldif(ldif)
+
+ ldif = """
+dn: CN=%s-dup,%s""" % (attr_name, self.schema_dn) + """
+changetype: modify
+replace: attributeId
+attributeId: """ + attributeID + """
+-
+"""
+ try:
+ self.ldb.modify_ldif(ldif)
+ self.fail("Should have failed to modify schema to have attribute with duplicate attributeID")
+ except LdbError as e9:
+ (enum, estr) = e9.args
+ self.assertEqual(enum, ERR_CONSTRAINT_VIOLATION)
+
+ def test_remove_ldapdisplayname(self):
+ """Testing removing the ldapdisplayname"""
+ rand = str(random.randint(1, 100000))
+ attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + "-" + rand
+ attr_ldap_display_name = attr_name.replace("-", "")
+ attributeID = "1.3.6.1.4.1.7165.4.6.1.6.10." + rand
+ ldif = """
+dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
+objectClass: top
+objectClass: attributeSchema
+adminDescription: """ + attr_name + """
+adminDisplayName: """ + attr_name + """
+cn: """ + attr_name + """
+attributeId: """ + attributeID + """
+attributeSyntax: 2.5.5.12
+ldapDisplayName: """ + attr_ldap_display_name + """
+omSyntax: 64
+instanceType: 4
+isSingleValued: TRUE
+systemOnly: FALSE
+"""
+ self.ldb.add_ldif(ldif)
+
+ ldif = """
+dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
+changetype: modify
+replace: ldapDisplayName
+-
+"""
+ try:
+ self.ldb.modify_ldif(ldif)
+ self.fail("Should have failed to remove the ldapdisplayname")
+ except LdbError as e10:
+ (enum, estr) = e10.args
+ self.assertEqual(enum, ERR_OBJECT_CLASS_VIOLATION)
+
+ def test_rename_ldapdisplayname(self):
+ """Testing renaming ldapdisplayname"""
+ rand = str(random.randint(1, 100000))
+ attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + "-" + rand
+ attr_ldap_display_name = attr_name.replace("-", "")
+ attributeID = "1.3.6.1.4.1.7165.4.6.1.6.11." + rand
+ ldif = """
+dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
+objectClass: top
+objectClass: attributeSchema
+adminDescription: """ + attr_name + """
+adminDisplayName: """ + attr_name + """
+cn: """ + attr_name + """
+attributeId: """ + attributeID + """
+attributeSyntax: 2.5.5.12
+ldapDisplayName: """ + attr_ldap_display_name + """
+omSyntax: 64
+instanceType: 4
+isSingleValued: TRUE
+systemOnly: FALSE
+"""
+ self.ldb.add_ldif(ldif)
+
+ ldif = """
+dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
+changetype: modify
+replace: ldapDisplayName
+ldapDisplayName: """ + attr_ldap_display_name + """2
+-
+"""
+ self.ldb.modify_ldif(ldif)
+
+ def test_change_attributeID(self):
+ """Testing change the attributeID"""
+ rand = str(random.randint(1, 100000))
+ attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + "-" + rand
+ attr_ldap_display_name = attr_name.replace("-", "")
+ attributeID = "1.3.6.1.4.1.7165.4.6.1.6.12." + rand
+ ldif = """
+dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
+objectClass: top
+objectClass: attributeSchema
+adminDescription: """ + attr_name + """
+adminDisplayName: """ + attr_name + """
+cn: """ + attr_name + """
+attributeId: """ + attributeID + """
+attributeSyntax: 2.5.5.12
+ldapDisplayName: """ + attr_ldap_display_name + """
+omSyntax: 64
+instanceType: 4
+isSingleValued: TRUE
+systemOnly: FALSE
+"""
+ self.ldb.add_ldif(ldif)
+
+ ldif = """
+dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
+changetype: modify
+replace: attributeID
+attributeId: """ + attributeID + """.1
+
+"""
+ try:
+ self.ldb.modify_ldif(ldif)
+ self.fail("Should have failed to modify schema to have different attributeID")
+ except LdbError as e11:
+ (enum, estr) = e11.args
+ self.assertEqual(enum, ERR_CONSTRAINT_VIOLATION)
+
+ def test_change_attributeID_same(self):
+ """Testing change the attributeID to the same value"""
+ rand = str(random.randint(1, 100000))
+ attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + "-" + rand
+ attr_ldap_display_name = attr_name.replace("-", "")
+ attributeID = "1.3.6.1.4.1.7165.4.6.1.6.13." + rand
+ ldif = """
+dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
+objectClass: top
+objectClass: attributeSchema
+adminDescription: """ + attr_name + """
+adminDisplayName: """ + attr_name + """
+cn: """ + attr_name + """
+attributeId: """ + attributeID + """
+attributeSyntax: 2.5.5.12
+ldapDisplayName: """ + attr_ldap_display_name + """
+omSyntax: 64
+instanceType: 4
+isSingleValued: TRUE
+systemOnly: FALSE
+"""
+ self.ldb.add_ldif(ldif)
+
+ ldif = """
+dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
+changetype: modify
+replace: attributeID
+attributeId: """ + attributeID + """
+
+"""
+ try:
+ self.ldb.modify_ldif(ldif)
+ self.fail("Should have failed to modify schema to have the same attributeID")
+ except LdbError as e12:
+ (enum, estr) = e12.args
+ self.assertEqual(enum, ERR_CONSTRAINT_VIOLATION)
+
+ def test_generated_linkID(self):
+ """
+ Test that we automatically generate a linkID if the
+ OID "1.2.840.113556.1.2.50" is given as the linkID
+ of a new attribute, and that we don't get/can't add
+ duplicate linkIDs. Also test that we can add a backlink
+ by providing the attributeID or ldapDisplayName of
+ a forwards link in the linkID attribute.
+ """
+
+ # linkID generation isn't available before 2003
+ res = self.ldb.search(base="", expression="", scope=SCOPE_BASE,
+ attrs=["domainControllerFunctionality"])
+ self.assertEqual(len(res), 1)
+ dc_level = int(res[0]["domainControllerFunctionality"][0])
+ if dc_level < DS_DOMAIN_FUNCTION_2003:
+ return
+
+ rand = str(random.randint(1, 100000))
+
+ attr_name_1 = "test-generated-linkID" + time.strftime("%s", time.gmtime()) + "-" + rand
+ attr_ldap_display_name_1 = attr_name_1.replace("-", "")
+ attributeID_1 = "1.3.6.1.4.1.7165.4.6.1.6.16." + rand
+ ldif = """
+dn: CN=%s,%s""" % (attr_name_1, self.schema_dn) + """
+objectClass: top
+objectClass: attributeSchema
+adminDescription: """ + attr_name_1 + """
+adminDisplayName: """ + attr_name_1 + """
+cn: """ + attr_name_1 + """
+attributeId: """ + attributeID_1 + """
+linkID: 1.2.840.113556.1.2.50
+attributeSyntax: 2.5.5.1
+ldapDisplayName: """ + attr_ldap_display_name_1 + """
+omSyntax: 127
+instanceType: 4
+isSingleValued: TRUE
+systemOnly: FALSE
+"""
+
+ try:
+ self.ldb.add_ldif(ldif)
+ except LdbError as e13:
+ (enum, estr) = e13.args
+ self.fail(estr)
+
+ attr_name_2 = "test-generated-linkID-2" + time.strftime("%s", time.gmtime()) + "-" + rand
+ attr_ldap_display_name_2 = attr_name_2.replace("-", "")
+ attributeID_2 = "1.3.6.1.4.1.7165.4.6.1.6.17." + rand
+ ldif = """
+dn: CN=%s,%s""" % (attr_name_2, self.schema_dn) + """
+objectClass: top
+objectClass: attributeSchema
+adminDescription: """ + attr_name_2 + """
+adminDisplayName: """ + attr_name_2 + """
+cn: """ + attr_name_2 + """
+attributeId: """ + attributeID_2 + """
+linkID: 1.2.840.113556.1.2.50
+attributeSyntax: 2.5.5.1
+ldapDisplayName: """ + attr_ldap_display_name_2 + """
+omSyntax: 127
+instanceType: 4
+isSingleValued: TRUE
+systemOnly: FALSE
+"""
+
+ try:
+ self.ldb.add_ldif(ldif)
+ except LdbError as e14:
+ (enum, estr) = e14.args
+ self.fail(estr)
+
+ res = self.ldb.search("CN=%s,%s" % (attr_name_1, self.schema_dn),
+ scope=SCOPE_BASE,
+ attrs=["linkID"])
+ self.assertEqual(len(res), 1)
+ linkID_1 = int(res[0]["linkID"][0])
+
+ res = self.ldb.search("CN=%s,%s" % (attr_name_2, self.schema_dn),
+ scope=SCOPE_BASE,
+ attrs=["linkID"])
+ self.assertEqual(len(res), 1)
+ linkID_2 = int(res[0]["linkID"][0])
+
+ # 0 should never be generated as a linkID
+ self.assertFalse(linkID_1 == 0)
+ self.assertFalse(linkID_2 == 0)
+
+ # The generated linkID should always be even, because
+ # it should assume we're adding a forward link.
+ self.assertTrue(linkID_1 % 2 == 0)
+ self.assertTrue(linkID_2 % 2 == 0)
+
+ self.assertFalse(linkID_1 == linkID_2)
+
+ # This is only necessary against Windows, since we depend
+ # on the previously added links in the next ones and Windows
+ # won't refresh the schema as we add them.
+ ldif = """
+dn:
+changetype: modify
+replace: schemaupdatenow
+schemaupdatenow: 1
+"""
+ self.ldb.modify_ldif(ldif)
+
+ # If we add a new link with the same linkID, it should fail
+ attr_name = "test-generated-linkID-duplicate" + time.strftime("%s", time.gmtime()) + "-" + rand
+ attr_ldap_display_name = attr_name.replace("-", "")
+ attributeID = "1.3.6.1.4.1.7165.4.6.1.6.18." + rand
+ ldif = """
+dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
+objectClass: top
+objectClass: attributeSchema
+adminDescription: """ + attr_name + """
+adminDisplayName: """ + attr_name + """
+cn: """ + attr_name + """
+attributeId: """ + attributeID + """
+linkID: """ + str(linkID_1) + """
+attributeSyntax: 2.5.5.1
+ldapDisplayName: """ + attr_ldap_display_name + """
+omSyntax: 127
+instanceType: 4
+isSingleValued: TRUE
+systemOnly: FALSE
+"""
+
+ try:
+ self.ldb.add_ldif(ldif)
+ self.fail("Should have failed to add duplicate linkID value")
+ except LdbError as e15:
+ (enum, estr) = e15.args
+ self.assertEqual(enum, ERR_UNWILLING_TO_PERFORM)
+
+ # If we add another attribute with the attributeID or lDAPDisplayName
+ # of a forward link in its linkID field, it should add as a backlink
+
+ attr_name_3 = "test-generated-linkID-backlink" + time.strftime("%s", time.gmtime()) + "-" + rand
+ attr_ldap_display_name_3 = attr_name_3.replace("-", "")
+ attributeID_3 = "1.3.6.1.4.1.7165.4.6.1.6.19." + rand
+ ldif = """
+dn: CN=%s,%s""" % (attr_name_3, self.schema_dn) + """
+objectClass: top
+objectClass: attributeSchema
+adminDescription: """ + attr_name_3 + """
+adminDisplayName: """ + attr_name_3 + """
+cn: """ + attr_name_3 + """
+attributeId: """ + attributeID_3 + """
+linkID: """ + str(linkID_1 + 1) + """
+attributeSyntax: 2.5.5.1
+ldapDisplayName: """ + attr_ldap_display_name_3 + """
+omSyntax: 127
+instanceType: 4
+isSingleValued: TRUE
+systemOnly: FALSE
+"""
+
+ try:
+ self.ldb.add_ldif(ldif)
+ except LdbError as e16:
+ (enum, estr) = e16.args
+ self.fail(estr)
+
+ res = self.ldb.search("CN=%s,%s" % (attr_name_3, self.schema_dn),
+ scope=SCOPE_BASE,
+ attrs=["linkID"])
+ self.assertEqual(len(res), 1)
+ linkID = int(res[0]["linkID"][0])
+ self.assertEqual(linkID, linkID_1 + 1)
+
+ attr_name_4 = "test-generated-linkID-backlink-2" + time.strftime("%s", time.gmtime()) + "-" + rand
+ attr_ldap_display_name_4 = attr_name_4.replace("-", "")
+ attributeID_4 = "1.3.6.1.4.1.7165.4.6.1.6.20." + rand
+ ldif = """
+dn: CN=%s,%s""" % (attr_name_4, self.schema_dn) + """
+objectClass: top
+objectClass: attributeSchema
+adminDescription: """ + attr_name_4 + """
+adminDisplayName: """ + attr_name_4 + """
+cn: """ + attr_name_4 + """
+attributeId: """ + attributeID_4 + """
+linkID: """ + attr_ldap_display_name_2 + """
+attributeSyntax: 2.5.5.1
+ldapDisplayName: """ + attr_ldap_display_name_4 + """
+omSyntax: 127
+instanceType: 4
+isSingleValued: TRUE
+systemOnly: FALSE
+"""
+
+ try:
+ self.ldb.add_ldif(ldif)
+ except LdbError as e17:
+ (enum, estr) = e17.args
+ self.fail(estr)
+
+ res = self.ldb.search("CN=%s,%s" % (attr_name_4, self.schema_dn),
+ scope=SCOPE_BASE,
+ attrs=["linkID"])
+ self.assertEqual(len(res), 1)
+ linkID = int(res[0]["linkID"][0])
+ self.assertEqual(linkID, linkID_2 + 1)
+
+ # If we then try to add another backlink in the same way
+ # for the same forwards link, we should fail.
+
+ attr_name = "test-generated-linkID-backlink-duplicate" + time.strftime("%s", time.gmtime()) + "-" + rand
+ attr_ldap_display_name = attr_name.replace("-", "")
+ attributeID = "1.3.6.1.4.1.7165.4.6.1.6.21." + rand
+ ldif = """
+dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
+objectClass: top
+objectClass: attributeSchema
+adminDescription: """ + attr_name + """
+adminDisplayName: """ + attr_name + """
+cn: """ + attr_name + """
+attributeId: """ + attributeID + """
+linkID: """ + attributeID_1 + """
+attributeSyntax: 2.5.5.1
+ldapDisplayName: """ + attr_ldap_display_name + """
+omSyntax: 127
+instanceType: 4
+isSingleValued: TRUE
+systemOnly: FALSE
+"""
+
+ try:
+ self.ldb.add_ldif(ldif)
+ self.fail("Should have failed to add duplicate backlink")
+ except LdbError as e18:
+ (enum, estr) = e18.args
+ self.assertEqual(enum, ERR_UNWILLING_TO_PERFORM)
+
+ # If we try to supply the attributeID or ldapDisplayName
+ # of an existing backlink in the linkID field of a new link,
+ # it should fail.
+
+ attr_name = "test-generated-linkID-backlink-invalid" + time.strftime("%s", time.gmtime()) + "-" + rand
+ attr_ldap_display_name = attr_name.replace("-", "")
+ attributeID = "1.3.6.1.4.1.7165.4.6.1.6.22." + rand
+ ldif = """
+dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
+objectClass: top
+objectClass: attributeSchema
+adminDescription: """ + attr_name + """
+adminDisplayName: """ + attr_name + """
+cn: """ + attr_name + """
+attributeId: """ + attributeID + """
+linkID: """ + attributeID_3 + """
+attributeSyntax: 2.5.5.1
+ldapDisplayName: """ + attr_ldap_display_name + """
+omSyntax: 127
+instanceType: 4
+isSingleValued: TRUE
+systemOnly: FALSE
+"""
+
+ try:
+ self.ldb.add_ldif(ldif)
+ self.fail("Should have failed to add backlink of backlink")
+ except LdbError as e19:
+ (enum, estr) = e19.args
+ self.assertEqual(enum, ERR_UNWILLING_TO_PERFORM)
+
+ attr_name = "test-generated-linkID-backlink-invalid-2" + time.strftime("%s", time.gmtime()) + "-" + rand
+ attr_ldap_display_name = attr_name.replace("-", "")
+ attributeID = "1.3.6.1.4.1.7165.4.6.1.6.23." + rand
+ ldif = """
+dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
+objectClass: top
+objectClass: attributeSchema
+adminDescription: """ + attr_name + """
+adminDisplayName: """ + attr_name + """
+cn: """ + attr_name + """
+attributeId: """ + attributeID + """
+linkID: """ + attr_ldap_display_name_4 + """
+attributeSyntax: 2.5.5.1
+ldapDisplayName: """ + attr_ldap_display_name + """
+omSyntax: 127
+instanceType: 4
+isSingleValued: TRUE
+systemOnly: FALSE
+"""
+
+ try:
+ self.ldb.add_ldif(ldif)
+ self.fail("Should have failed to add backlink of backlink")
+ except LdbError as e20:
+ (enum, estr) = e20.args
+ self.assertEqual(enum, ERR_UNWILLING_TO_PERFORM)
+
+ def test_generated_mAPIID(self):
+ """
+ Test that we automatically generate a mAPIID if the
+ OID "1.2.840.113556.1.2.49" is given as the mAPIID
+ of a new attribute, and that we don't get/can't add
+ duplicate mAPIIDs.
+ """
+
+ rand = str(random.randint(1, 100000))
+
+ attr_name_1 = "test-generated-mAPIID" + time.strftime("%s", time.gmtime()) + "-" + rand
+ attr_ldap_display_name_1 = attr_name_1.replace("-", "")
+ attributeID_1 = "1.3.6.1.4.1.7165.4.6.1.6.24." + rand
+ ldif = """
+dn: CN=%s,%s""" % (attr_name_1, self.schema_dn) + """
+objectClass: top
+objectClass: attributeSchema
+adminDescription: """ + attr_name_1 + """
+adminDisplayName: """ + attr_name_1 + """
+cn: """ + attr_name_1 + """
+attributeId: """ + attributeID_1 + """
+mAPIID: 1.2.840.113556.1.2.49
+attributeSyntax: 2.5.5.1
+ldapDisplayName: """ + attr_ldap_display_name_1 + """
+omSyntax: 127
+instanceType: 4
+isSingleValued: TRUE
+systemOnly: FALSE
+"""
+
+ try:
+ self.ldb.add_ldif(ldif)
+ except LdbError as e21:
+ (enum, estr) = e21.args
+ self.fail(estr)
+
+ res = self.ldb.search("CN=%s,%s" % (attr_name_1, self.schema_dn),
+ scope=SCOPE_BASE,
+ attrs=["mAPIID"])
+ self.assertEqual(len(res), 1)
+ mAPIID_1 = int(res[0]["mAPIID"][0])
+
+ ldif = """
+dn:
+changetype: modify
+replace: schemaupdatenow
+schemaupdatenow: 1
+"""
+ self.ldb.modify_ldif(ldif)
+
+ # If we add a new attribute with the same mAPIID, it should fail
+ attr_name = "test-generated-mAPIID-duplicate" + time.strftime("%s", time.gmtime()) + "-" + rand
+ attr_ldap_display_name = attr_name.replace("-", "")
+ attributeID = "1.3.6.1.4.1.7165.4.6.1.6.25." + rand
+ ldif = """
+dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """
+objectClass: top
+objectClass: attributeSchema
+adminDescription: """ + attr_name + """
+adminDisplayName: """ + attr_name + """
+cn: """ + attr_name + """
+attributeId: """ + attributeID + """
+mAPIID: """ + str(mAPIID_1) + """
+attributeSyntax: 2.5.5.1
+ldapDisplayName: """ + attr_ldap_display_name + """
+omSyntax: 127
+instanceType: 4
+isSingleValued: TRUE
+systemOnly: FALSE
+"""
+
+ try:
+ self.ldb.add_ldif(ldif)
+ self.fail("Should have failed to add duplicate mAPIID value")
+ except LdbError as e22:
+ (enum, estr) = e22.args
+ self.assertEqual(enum, ERR_UNWILLING_TO_PERFORM)
+
+ def test_change_governsID(self):
+ """Testing change the governsID"""
+ rand = str(random.randint(1, 100000))
+ class_name = "test-Class" + time.strftime("%s", time.gmtime()) + "-" + rand
+ class_ldap_display_name = class_name.replace("-", "")
+ governsID = "1.3.6.1.4.1.7165.4.6.2.6.5." + rand
+ ldif = """
+dn: CN=%s,%s""" % (class_name, self.schema_dn) + """
+objectClass: top
+objectClass: classSchema
+adminDescription: """ + class_name + """
+adminDisplayName: """ + class_name + """
+cn: """ + class_name + """
+governsId: """ + governsID + """
+ldapDisplayName: """ + class_ldap_display_name + """
+instanceType: 4
+objectClassCategory: 1
+subClassOf: organizationalPerson
+rDNAttID: cn
+systemMustContain: cn
+systemOnly: FALSE
+"""
+ self.ldb.add_ldif(ldif)
+
+ ldif = """
+dn: CN=%s,%s""" % (class_name, self.schema_dn) + """
+changetype: modify
+replace: governsID
+governsId: """ + governsID + """.1
+
+"""
+ try:
+ self.ldb.modify_ldif(ldif)
+ self.fail("Should have failed to modify schema to have different governsID")
+ except LdbError as e23:
+ (enum, estr) = e23.args
+ self.assertEqual(enum, ERR_CONSTRAINT_VIOLATION)
+
+ def test_change_governsID_same(self):
+ """Testing change the governsID"""
+ rand = str(random.randint(1, 100000))
+ class_name = "test-Class" + time.strftime("%s", time.gmtime()) + "-" + rand
+ class_ldap_display_name = class_name.replace("-", "")
+ governsID = "1.3.6.1.4.1.7165.4.6.2.6.6." + rand
+ ldif = """
+dn: CN=%s,%s""" % (class_name, self.schema_dn) + """
+objectClass: top
+objectClass: classSchema
+adminDescription: """ + class_name + """
+adminDisplayName: """ + class_name + """
+cn: """ + class_name + """
+governsId: """ + governsID + """
+ldapDisplayName: """ + class_ldap_display_name + """
+instanceType: 4
+objectClassCategory: 1
+subClassOf: organizationalPerson
+rDNAttID: cn
+systemMustContain: cn
+systemOnly: FALSE
+"""
+ self.ldb.add_ldif(ldif)
+
+ ldif = """
+dn: CN=%s,%s""" % (class_name, self.schema_dn) + """
+changetype: modify
+replace: governsID
+governsId: """ + governsID + """.1
+
+"""
+ try:
+ self.ldb.modify_ldif(ldif)
+ self.fail("Should have failed to modify schema to have the same governsID")
+ except LdbError as e24:
+ (enum, estr) = e24.args
+ self.assertEqual(enum, ERR_CONSTRAINT_VIOLATION)
+
+
+class SchemaTests_msDS_IntId(samba.tests.TestCase):
+
+ def setUp(self):
+ super(SchemaTests_msDS_IntId, self).setUp()
+ self.ldb = SamDB(host, credentials=creds,
+ session_info=system_session(lp), lp=lp, options=ldb_options)
+ res = self.ldb.search(base="", expression="", scope=SCOPE_BASE,
+ attrs=["schemaNamingContext", "defaultNamingContext",
+ "forestFunctionality"])
+ self.assertEqual(len(res), 1)
+ self.schema_dn = res[0]["schemaNamingContext"][0]
+ self.base_dn = res[0]["defaultNamingContext"][0]
+ self.forest_level = int(res[0]["forestFunctionality"][0])
+
+ def _ldap_schemaUpdateNow(self):
+ ldif = """
+dn:
+changetype: modify
+add: schemaUpdateNow
+schemaUpdateNow: 1
+"""
+ self.ldb.modify_ldif(ldif)
+
+ def _make_obj_names(self, prefix):
+ class_name = prefix + time.strftime("%s", time.gmtime())
+ class_ldap_name = class_name.replace("-", "")
+ class_dn = "CN=%s,%s" % (class_name, self.schema_dn)
+ return (class_name, class_ldap_name, class_dn)
+
+ def _is_schema_base_object(self, ldb_msg):
+ """Test systemFlags for SYSTEM_FLAG_SCHEMA_BASE_OBJECT (16)"""
+ systemFlags = 0
+ if "systemFlags" in ldb_msg:
+ systemFlags = int(ldb_msg["systemFlags"][0])
+ return (systemFlags & 16) != 0
+
+ def _make_attr_ldif(self, attr_name, attr_dn):
+ ldif = """
+dn: """ + attr_dn + """
+objectClass: top
+objectClass: attributeSchema
+adminDescription: """ + attr_name + """
+adminDisplayName: """ + attr_name + """
+cn: """ + attr_name + """
+attributeId: 1.3.6.1.4.1.7165.4.6.1.6.14.""" + str(random.randint(1, 100000)) + """
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+instanceType: 4
+isSingleValued: TRUE
+systemOnly: FALSE
+"""
+ return ldif
+
+ def test_msDS_IntId_on_attr(self):
+ """Testing msDs-IntId creation for Attributes.
+ See MS-ADTS - 3.1.1.Attributes
+
+ This test should verify that:
+ - Creating attribute with 'msDS-IntId' fails with ERR_UNWILLING_TO_PERFORM
+ - Adding 'msDS-IntId' on existing attribute fails with ERR_CONSTRAINT_VIOLATION
+ - Creating attribute with 'msDS-IntId' set and FLAG_SCHEMA_BASE_OBJECT flag
+ set fails with ERR_UNWILLING_TO_PERFORM
+ - Attributes created with FLAG_SCHEMA_BASE_OBJECT not set have
+ 'msDS-IntId' attribute added internally
+ """
+
+ # 1. Create attribute without systemFlags
+ # msDS-IntId should be created if forest functional
+ # level is >= DS_DOMAIN_FUNCTION_2003
+ # and missing otherwise
+ (attr_name, attr_ldap_name, attr_dn) = self._make_obj_names("msDS-IntId-Attr-1-")
+ ldif = self._make_attr_ldif(attr_name, attr_dn)
+
+ # try to add msDS-IntId during Attribute creation
+ ldif_fail = ldif + "msDS-IntId: -1993108831\n"
+ try:
+ self.ldb.add_ldif(ldif_fail)
+ self.fail("Adding attribute with preset msDS-IntId should fail")
+ except LdbError as e25:
+ (num, _) = e25.args
+ self.assertEqual(num, ERR_UNWILLING_TO_PERFORM)
+
+ # add the new attribute and update schema
+ self.ldb.add_ldif(ldif)
+ self._ldap_schemaUpdateNow()
+
+ # Search for created attribute
+ res = []
+ res = self.ldb.search(attr_dn, scope=SCOPE_BASE,
+ attrs=["lDAPDisplayName", "msDS-IntId", "systemFlags"])
+ self.assertEqual(len(res), 1)
+ self.assertEqual(str(res[0]["lDAPDisplayName"][0]), attr_ldap_name)
+ if self.forest_level >= DS_DOMAIN_FUNCTION_2003:
+ if self._is_schema_base_object(res[0]):
+ self.assertTrue("msDS-IntId" not in res[0])
+ else:
+ self.assertTrue("msDS-IntId" in res[0])
+ else:
+ self.assertTrue("msDS-IntId" not in res[0])
+
+ msg = Message()
+ msg.dn = Dn(self.ldb, attr_dn)
+ msg["msDS-IntId"] = MessageElement("-1993108831", FLAG_MOD_REPLACE, "msDS-IntId")
+ try:
+ self.ldb.modify(msg)
+ self.fail("Modifying msDS-IntId should return error")
+ except LdbError as e26:
+ (num, _) = e26.args
+ self.assertEqual(num, ERR_CONSTRAINT_VIOLATION)
+
+ # 2. Create attribute with systemFlags = FLAG_SCHEMA_BASE_OBJECT
+ # msDS-IntId should be created if forest functional
+ # level is >= DS_DOMAIN_FUNCTION_2003
+ # and missing otherwise
+ (attr_name, attr_ldap_name, attr_dn) = self._make_obj_names("msDS-IntId-Attr-2-")
+ ldif = self._make_attr_ldif(attr_name, attr_dn)
+ ldif += "systemFlags: 16\n"
+
+ # try to add msDS-IntId during Attribute creation
+ ldif_fail = ldif + "msDS-IntId: -1993108831\n"
+ try:
+ self.ldb.add_ldif(ldif_fail)
+ self.fail("Adding attribute with preset msDS-IntId should fail")
+ except LdbError as e27:
+ (num, _) = e27.args
+ self.assertEqual(num, ERR_UNWILLING_TO_PERFORM)
+
+ # add the new attribute and update schema
+ self.ldb.add_ldif(ldif)
+ self._ldap_schemaUpdateNow()
+
+ # Search for created attribute
+ res = []
+ res = self.ldb.search(attr_dn, scope=SCOPE_BASE,
+ attrs=["lDAPDisplayName", "msDS-IntId"])
+ self.assertEqual(len(res), 1)
+ self.assertEqual(str(res[0]["lDAPDisplayName"][0]), attr_ldap_name)
+ if self.forest_level >= DS_DOMAIN_FUNCTION_2003:
+ if self._is_schema_base_object(res[0]):
+ self.assertTrue("msDS-IntId" not in res[0])
+ else:
+ self.assertTrue("msDS-IntId" in res[0])
+ else:
+ self.assertTrue("msDS-IntId" not in res[0])
+
+ msg = Message()
+ msg.dn = Dn(self.ldb, attr_dn)
+ msg["msDS-IntId"] = MessageElement("-1993108831", FLAG_MOD_REPLACE, "msDS-IntId")
+ try:
+ self.ldb.modify(msg)
+ self.fail("Modifying msDS-IntId should return error")
+ except LdbError as e28:
+ (num, _) = e28.args
+ self.assertEqual(num, ERR_CONSTRAINT_VIOLATION)
+
+ def _make_class_ldif(self, class_dn, class_name, sub_oid):
+ ldif = """
+dn: """ + class_dn + """
+objectClass: top
+objectClass: classSchema
+adminDescription: """ + class_name + """
+adminDisplayName: """ + class_name + """
+cn: """ + class_name + """
+governsId: 1.3.6.1.4.1.7165.4.6.2.6.%d.""" % sub_oid + str(random.randint(1, 100000)) + """
+instanceType: 4
+objectClassCategory: 1
+subClassOf: organizationalPerson
+rDNAttID: cn
+systemMustContain: cn
+systemOnly: FALSE
+"""
+ return ldif
+
+ def test_msDS_IntId_on_class(self):
+ """Testing msDs-IntId creation for Class
+ Reference: MS-ADTS - 3.1.1.2.4.8 Class classSchema"""
+
+ # 1. Create Class without systemFlags
+ # msDS-IntId should be created if forest functional
+ # level is >= DS_DOMAIN_FUNCTION_2003
+ # and missing otherwise
+ (class_name, class_ldap_name, class_dn) = self._make_obj_names("msDS-IntId-Class-1-")
+ ldif = self._make_class_ldif(class_dn, class_name, 8)
+
+ # try to add msDS-IntId during Class creation
+ ldif_add = ldif + "msDS-IntId: -1993108831\n"
+ self.ldb.add_ldif(ldif_add)
+ self._ldap_schemaUpdateNow()
+
+ res = self.ldb.search(class_dn, scope=SCOPE_BASE, attrs=["msDS-IntId"])
+ self.assertEqual(len(res), 1)
+ self.assertEqual(str(res[0]["msDS-IntId"][0]), "-1993108831")
+
+ # add a new Class and update schema
+ (class_name, class_ldap_name, class_dn) = self._make_obj_names("msDS-IntId-Class-2-")
+ ldif = self._make_class_ldif(class_dn, class_name, 9)
+
+ self.ldb.add_ldif(ldif)
+ self._ldap_schemaUpdateNow()
+
+ # Search for created Class
+ res = self.ldb.search(class_dn, scope=SCOPE_BASE, attrs=["msDS-IntId"])
+ self.assertEqual(len(res), 1)
+ self.assertFalse("msDS-IntId" in res[0])
+
+ msg = Message()
+ msg.dn = Dn(self.ldb, class_dn)
+ msg["msDS-IntId"] = MessageElement("-1993108831", FLAG_MOD_REPLACE, "msDS-IntId")
+ try:
+ self.ldb.modify(msg)
+ self.fail("Modifying msDS-IntId should return error")
+ except LdbError as e29:
+ (num, _) = e29.args
+ self.assertEqual(num, ERR_CONSTRAINT_VIOLATION)
+
+ # 2. Create Class with systemFlags = FLAG_SCHEMA_BASE_OBJECT
+ # msDS-IntId should be created if forest functional
+ # level is >= DS_DOMAIN_FUNCTION_2003
+ # and missing otherwise
+ (class_name, class_ldap_name, class_dn) = self._make_obj_names("msDS-IntId-Class-3-")
+ ldif = self._make_class_ldif(class_dn, class_name, 10)
+ ldif += "systemFlags: 16\n"
+
+ # try to add msDS-IntId during Class creation
+ ldif_add = ldif + "msDS-IntId: -1993108831\n"
+ self.ldb.add_ldif(ldif_add)
+
+ res = self.ldb.search(class_dn, scope=SCOPE_BASE, attrs=["msDS-IntId"])
+ self.assertEqual(len(res), 1)
+ self.assertEqual(str(res[0]["msDS-IntId"][0]), "-1993108831")
+
+ # add the new Class and update schema
+ (class_name, class_ldap_name, class_dn) = self._make_obj_names("msDS-IntId-Class-4-")
+ ldif = self._make_class_ldif(class_dn, class_name, 11)
+ ldif += "systemFlags: 16\n"
+
+ self.ldb.add_ldif(ldif)
+ self._ldap_schemaUpdateNow()
+
+ # Search for created Class
+ res = self.ldb.search(class_dn, scope=SCOPE_BASE, attrs=["msDS-IntId"])
+ self.assertEqual(len(res), 1)
+ self.assertFalse("msDS-IntId" in res[0])
+
+ msg = Message()
+ msg.dn = Dn(self.ldb, class_dn)
+ msg["msDS-IntId"] = MessageElement("-1993108831", FLAG_MOD_REPLACE, "msDS-IntId")
+ try:
+ self.ldb.modify(msg)
+ self.fail("Modifying msDS-IntId should return error")
+ except LdbError as e30:
+ (num, _) = e30.args
+ self.assertEqual(num, ERR_CONSTRAINT_VIOLATION)
+ res = self.ldb.search(class_dn, scope=SCOPE_BASE, attrs=["msDS-IntId"])
+ self.assertEqual(len(res), 1)
+ self.assertFalse("msDS-IntId" in res[0])
+
+ def test_verify_msDS_IntId(self):
+ """Verify msDS-IntId exists only on attributes without FLAG_SCHEMA_BASE_OBJECT flag set"""
+ count = 0
+ res = self.ldb.search(self.schema_dn, scope=SCOPE_ONELEVEL,
+ expression="objectClass=attributeSchema",
+ attrs=["systemFlags", "msDS-IntId", "attributeID", "cn"])
+ self.assertTrue(len(res) > 1)
+ for ldb_msg in res:
+ if self.forest_level >= DS_DOMAIN_FUNCTION_2003:
+ if self._is_schema_base_object(ldb_msg):
+ self.assertTrue("msDS-IntId" not in ldb_msg)
+ else:
+ # don't assert here as there are plenty of
+ # attributes under w2k8 that are not part of
+ # Base Schema (SYSTEM_FLAG_SCHEMA_BASE_OBJECT flag not set)
+ # has not msDS-IntId attribute set
+ #self.assertTrue("msDS-IntId" in ldb_msg, "msDS-IntId expected on: %s" % ldb_msg.dn)
+ if "msDS-IntId" not in ldb_msg:
+ count = count + 1
+ print("%3d warning: msDS-IntId expected on: %-30s %s" % (count, ldb_msg["attributeID"], ldb_msg["cn"]))
+ else:
+ self.assertTrue("msDS-IntId" not in ldb_msg)
+
+
+class SchemaTests_msDS_isRODC(samba.tests.TestCase):
+
+ def setUp(self):
+ super(SchemaTests_msDS_isRODC, self).setUp()
+ self.ldb = SamDB(host, credentials=creds,
+ session_info=system_session(lp), lp=lp, options=ldb_options)
+ res = self.ldb.search(base="", expression="", scope=SCOPE_BASE, attrs=["defaultNamingContext"])
+ self.assertEqual(len(res), 1)
+ self.base_dn = res[0]["defaultNamingContext"][0]
+
+ def test_objectClass_ntdsdsa(self):
+ res = self.ldb.search(self.base_dn, expression="objectClass=nTDSDSA",
+ attrs=["msDS-isRODC"], controls=["search_options:1:2"])
+ for ldb_msg in res:
+ self.assertTrue("msDS-isRODC" in ldb_msg)
+
+ def test_objectClass_server(self):
+ res = self.ldb.search(self.base_dn, expression="objectClass=server",
+ attrs=["msDS-isRODC"], controls=["search_options:1:2"])
+ for ldb_msg in res:
+ ntds_search_dn = "CN=NTDS Settings,%s" % ldb_msg['dn']
+ try:
+ res_check = self.ldb.search(ntds_search_dn, attrs=["objectCategory"])
+ except LdbError as e:
+ (num, _) = e.args
+ self.assertEqual(num, ERR_NO_SUCH_OBJECT)
+ print("Server entry %s doesn't have a NTDS settings object" % res[0]['dn'])
+ else:
+ self.assertTrue("objectCategory" in res_check[0])
+ self.assertTrue("msDS-isRODC" in ldb_msg)
+
+ def test_objectClass_computer(self):
+ res = self.ldb.search(self.base_dn, expression="objectClass=computer",
+ attrs=["serverReferenceBL", "msDS-isRODC"], controls=["search_options:1:2"])
+ for ldb_msg in res:
+ if "serverReferenceBL" not in ldb_msg:
+ print("Computer entry %s doesn't have a serverReferenceBL attribute" % ldb_msg['dn'])
+ else:
+ self.assertTrue("msDS-isRODC" in ldb_msg)
+
+
+if "://" not in host:
+ if os.path.isfile(host):
+ host = "tdb://%s" % host
+ else:
+ host = "ldap://%s" % host
+
+ldb_options = []
+if host.startswith("ldap://"):
+ # user 'paged_search' module when connecting remotely
+ ldb_options = ["modules:paged_searches"]
+
+TestProgram(module=__name__, opts=subunitopts)