s4-tort: Tests for "msDS-IntId" attribute implemented
authorKamen Mazdrashki <kamen.mazdrashki@postpath.com>
Thu, 17 Dec 2009 17:27:47 +0000 (19:27 +0200)
committerAndrew Tridgell <tridge@samba.org>
Mon, 21 Dec 2009 12:44:13 +0000 (23:44 +1100)
Signed-off-by: Andrew Tridgell <tridge@samba.org>
source4/lib/ldb/tests/python/ldap_schema.py

index f6a6f38ec130528d4e1464fcab65d5fe90b49e6c..f13a4fbc52b90e810286fddc7251e263f57dfc78 100755 (executable)
@@ -43,6 +43,7 @@ from samba import ATYPE_SECURITY_UNIVERSAL_GROUP
 from samba import ATYPE_DISTRIBUTION_GLOBAL_GROUP
 from samba import ATYPE_DISTRIBUTION_LOCAL_GROUP
 from samba import ATYPE_DISTRIBUTION_UNIVERSAL_GROUP
+from samba import DS_DC_FUNCTION_2003
 
 from subunit import SubunitTestRunner
 import unittest
@@ -110,7 +111,6 @@ class SchemaTests(unittest.TestCase):
         self.assertFalse("objectClasses" in res[0])
         self.assertFalse("attributeTypes" in res[0])
 
-
     def test_schemaUpdateNow(self):
         """Testing schemaUpdateNow"""
         attr_name = "test-Attr" + time.strftime("%s", time.gmtime())
@@ -201,13 +201,290 @@ name: """ + object_name + """
         # Delete the object
         self.delete_force(self.ldb, "cn=%s,cn=Users,%s" % (object_name, self.base_dn))
 
+
+class SchemaTests_msDS_IntId(unittest.TestCase):
+
+    def setUp(self):
+        self.ldb = ldb
+        res = ldb.search(base="", expression="", scope=SCOPE_BASE, attrs=["*"])
+        self.assertEquals(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.2.840.""" + str(random.randint(1,100000)) + """.1.5.9940
+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_DC_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, (num, _):
+            self.assertEquals(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=["*"])
+        self.assertEquals(len(res), 1)
+        self.assertEquals(res[0]["lDAPDisplayName"][0], attr_ldap_name)
+        if self.forest_level >= DS_DC_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, (num, _):
+            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
+
+        # 2. Create attribute with systemFlags = FLAG_SCHEMA_BASE_OBJECT
+        # msDS-IntId should be created if forest functional
+        # level is >= DS_DC_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, (num, _):
+            self.assertEquals(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=["*"])
+        self.assertEquals(len(res), 1)
+        self.assertEquals(res[0]["lDAPDisplayName"][0], attr_ldap_name)
+        if self.forest_level >= DS_DC_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, (num, _):
+            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
+
+
+    def _make_class_ldif(self, class_dn, class_name):
+        ldif = """
+dn: """ + class_dn + """
+objectClass: top
+objectClass: classSchema
+adminDescription: """ + class_name + """
+adminDisplayName: """ + class_name + """
+cn: """ + class_name + """
+governsId: 1.2.840.""" + str(random.randint(1,100000)) + """.1.5.9939
+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_DC_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)
+
+        # 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=["*"])
+        self.assertEquals(len(res), 1)
+        self.assertEquals(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)
+
+        self.ldb.add_ldif(ldif)
+        self._ldap_schemaUpdateNow()
+
+        # Search for created Class
+        res = self.ldb.search(class_dn, scope=SCOPE_BASE, attrs=["*"])
+        self.assertEquals(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, (num, _):
+            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
+
+        # 2. Create Class with systemFlags = FLAG_SCHEMA_BASE_OBJECT
+        # msDS-IntId should be created if forest functional
+        # level is >= DS_DC_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)
+        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=["*"])
+        self.assertEquals(len(res), 1)
+        self.assertEquals(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)
+        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=["*"])
+        self.assertEquals(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, (num, _):
+            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
+        res = self.ldb.search(class_dn, scope=SCOPE_BASE, attrs=["*"])
+        self.assertEquals(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_DC_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)
+
+
 if not "://" in host:
     if os.path.isfile(host):
         host = "tdb://%s" % host
     else:
         host = "ldap://%s" % host
 
-ldb = Ldb(host, credentials=creds, session_info=system_session(), lp=lp)
+ldb_options = []
+if host.startswith("ldap://"):
+    # user 'paged_search' module when connecting remotely
+    ldb_options = ["modules:paged_searches"]
+
+ldb = Ldb(host, credentials=creds, session_info=system_session(), lp=lp, options=ldb_options)
 if not "tdb://" in host:
     gc_ldb = Ldb("%s:3268" % host, credentials=creds,
                  session_info=system_session(), lp=lp)
@@ -218,4 +495,6 @@ runner = SubunitTestRunner()
 rc = 0
 if not runner.run(unittest.makeSuite(SchemaTests)).wasSuccessful():
     rc = 1
+if not runner.run(unittest.makeSuite(SchemaTests_msDS_IntId)).wasSuccessful():
+    rc = 1
 sys.exit(rc)