diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 17:47:29 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 17:47:29 +0000 |
commit | 4f5791ebd03eaec1c7da0865a383175b05102712 (patch) | |
tree | 8ce7b00f7a76baa386372422adebbe64510812d4 /source4/dsdb/tests/python/ldap_schema.py | |
parent | Initial commit. (diff) | |
download | samba-4f5791ebd03eaec1c7da0865a383175b05102712.tar.xz samba-4f5791ebd03eaec1c7da0865a383175b05102712.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-x | source4/dsdb/tests/python/ldap_schema.py | 1601 |
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) |