#!/usr/bin/env python3 # -*- coding: utf-8 -*- # This is a port of the original in testprogs/ejs/ldap.js # Copyright (C) Jelmer Vernooij 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 . 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] ") 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)