s4-dsdb: Tests for dSHeuristics attribute
[nivanova/samba.git] / source4 / dsdb / tests / python / ldap.py
index 7cf02ea97d4621a2a8554d22bab731cef1421198..5c7ea8dae55bb6ca07575f00d17b2316aedb96fc 100755 (executable)
@@ -20,16 +20,22 @@ from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError
 from ldb import ERR_NO_SUCH_OBJECT, ERR_ATTRIBUTE_OR_VALUE_EXISTS
 from ldb import ERR_ENTRY_ALREADY_EXISTS, ERR_UNWILLING_TO_PERFORM
 from ldb import ERR_NOT_ALLOWED_ON_NON_LEAF, ERR_OTHER, ERR_INVALID_DN_SYNTAX
-from ldb import ERR_NO_SUCH_ATTRIBUTE
+from ldb import ERR_NO_SUCH_ATTRIBUTE, ERR_INVALID_ATTRIBUTE_SYNTAX
 from ldb import ERR_OBJECT_CLASS_VIOLATION, ERR_NOT_ALLOWED_ON_RDN
 from ldb import ERR_NAMING_VIOLATION, ERR_CONSTRAINT_VIOLATION
 from ldb import ERR_UNDEFINED_ATTRIBUTE_TYPE
 from ldb import Message, MessageElement, Dn
 from ldb import FLAG_MOD_ADD, FLAG_MOD_REPLACE, FLAG_MOD_DELETE
 from samba import Ldb
-from samba.dsdb import (UF_NORMAL_ACCOUNT, UF_WORKSTATION_TRUST_ACCOUNT, 
+from samba.dsdb import (UF_NORMAL_ACCOUNT, UF_INTERDOMAIN_TRUST_ACCOUNT,
+    UF_WORKSTATION_TRUST_ACCOUNT, UF_SERVER_TRUST_ACCOUNT,
+    UF_PARTIAL_SECRETS_ACCOUNT,
     UF_PASSWD_NOTREQD, UF_ACCOUNTDISABLE, ATYPE_NORMAL_ACCOUNT,
-    ATYPE_WORKSTATION_TRUST, SYSTEM_FLAG_DOMAIN_DISALLOW_MOVE)
+    ATYPE_WORKSTATION_TRUST, SYSTEM_FLAG_DOMAIN_DISALLOW_MOVE,
+    SYSTEM_FLAG_CONFIG_ALLOW_RENAME, SYSTEM_FLAG_CONFIG_ALLOW_MOVE,
+    SYSTEM_FLAG_CONFIG_ALLOW_LIMITED_MOVE)
+from samba.dcerpc.security import (DOMAIN_RID_USERS, DOMAIN_RID_DOMAIN_MEMBERS,
+    DOMAIN_RID_DCS, DOMAIN_RID_READONLY_DCS)
 
 from subunit.run import SubunitTestRunner
 import unittest
@@ -37,7 +43,7 @@ import unittest
 from samba.ndr import ndr_pack, ndr_unpack
 from samba.dcerpc import security
 
-parser = optparse.OptionParser("ldap [options] <host>")
+parser = optparse.OptionParser("ldap.py [options] <host>")
 sambaopts = options.SambaOptions(parser)
 parser.add_option_group(sambaopts)
 parser.add_option_group(options.VersionOptions(parser))
@@ -83,6 +89,17 @@ class BasicTests(unittest.TestCase):
         res = self.ldb.search(base=self.base_dn, expression="(objectClass=*)", scope=SCOPE_BASE)
         return ndr_unpack( security.dom_sid,res[0]["objectSid"][0])
 
+    def set_dsheuristics(self, dsheuristics):
+        m = Message()
+        m.dn = Dn(self.ldb, "CN=Directory Service, CN=Windows NT, CN=Services, "
+                  + self.configuration_dn)
+        if dsheuristics is not None:
+            m["dSHeuristics"] = MessageElement(dsheuristics, FLAG_MOD_REPLACE,
+                                               "dSHeuristics")
+        else:
+            m["dSHeuristics"] = MessageElement([], FLAG_MOD_DELETE, "dsHeuristics")
+        self.ldb.modify(m)
+
     def setUp(self):
         super(BasicTests, self).setUp()
         self.ldb = ldb
@@ -108,8 +125,6 @@ class BasicTests(unittest.TestCase):
         self.delete_force(self.ldb, "cn=ldaptestcomputer3,cn=computers," + self.base_dn)
         self.delete_force(self.ldb, "cn=ldaptestutf8user èùéìòà,cn=users," + self.base_dn)
         self.delete_force(self.ldb, "cn=ldaptestutf8user2  èùéìòà,cn=users," + self.base_dn)
-        self.delete_force(self.ldb, "cn=entry1,cn=ldaptestcontainer," + self.base_dn)
-        self.delete_force(self.ldb, "cn=entry2,cn=ldaptestcontainer," + self.base_dn)
         self.delete_force(self.ldb, "cn=ldaptestcontainer," + self.base_dn)
         self.delete_force(self.ldb, "cn=ldaptestcontainer2," + self.base_dn)
         self.delete_force(self.ldb, "cn=parentguidtest,cn=users," + self.base_dn)
@@ -171,6 +186,19 @@ class BasicTests(unittest.TestCase):
         except LdbError, (num, _):
             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
 
+        # Test allowed system flags
+        self.ldb.add({
+             "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
+             "objectClass": "person",
+             "systemFlags": str(~(SYSTEM_FLAG_CONFIG_ALLOW_RENAME | SYSTEM_FLAG_CONFIG_ALLOW_MOVE | SYSTEM_FLAG_CONFIG_ALLOW_LIMITED_MOVE)) })
+
+        res = ldb.search("cn=ldaptestuser,cn=users," + self.base_dn,
+                         scope=SCOPE_BASE, attrs=["systemFlags"])
+        self.assertTrue(len(res) == 1)
+        self.assertEquals(res[0]["systemFlags"][0], "0")
+
+        self.delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
+
         self.ldb.add({
              "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
              "objectClass": "person" })
@@ -337,7 +365,7 @@ class BasicTests(unittest.TestCase):
           "objectClass")
         ldb.modify(m)
 
-        self.delete_force(self.ldb, "cn=ldaptestuser2,cn=users," + self.base_dn)
+        self.delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
 
     def test_system_only(self):
         """Test systemOnly objects"""
@@ -351,9 +379,56 @@ class BasicTests(unittest.TestCase):
         except LdbError, (num, _):
             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
 
+        try:
+            self.ldb.add({
+                "dn": "cn=testsecret,cn=system," + self.base_dn,
+                "objectclass": "secret"})
+            self.fail()
+        except LdbError, (num, _):
+            self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
+
         self.delete_force(self.ldb, "cn=ldaptestobject," + self.base_dn)
         self.delete_force(self.ldb, "cn=testsecret,cn=system," + self.base_dn)
 
+        try:
+            self.ldb.add({
+                "dn": "cn=ldaptestcontainer," + self.base_dn,
+                "objectclass": "container",
+                "isCriticalSystemObject": "TRUE"})
+            self.fail()
+        except LdbError, (num, _):
+            self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
+
+        self.ldb.add({
+            "dn": "cn=ldaptestcontainer," + self.base_dn,
+            "objectclass": "container"})
+
+        m = Message()
+        m.dn = Dn(ldb, "cn=ldaptestcontainer," + self.base_dn)
+        m["isCriticalSystemObject"] = MessageElement("TRUE", FLAG_MOD_REPLACE,
+          "isCriticalSystemObject")
+        try:
+            ldb.modify(m)
+            self.fail()
+        except LdbError, (num, _):
+            self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
+
+        self.delete_force(self.ldb, "cn=ldaptestcontainer," + self.base_dn)
+
+        # Proof if DC SAM object has "isCriticalSystemObject" set
+        res = self.ldb.search("", scope=SCOPE_BASE, attrs=["serverName"])
+        self.assertTrue(len(res) == 1)
+        self.assertTrue("serverName" in res[0])
+        res = self.ldb.search(res[0]["serverName"][0], scope=SCOPE_BASE,
+                              attrs=["serverReference"])
+        self.assertTrue(len(res) == 1)
+        self.assertTrue("serverReference" in res[0])
+        res = self.ldb.search(res[0]["serverReference"][0], scope=SCOPE_BASE,
+                              attrs=["isCriticalSystemObject"])
+        self.assertTrue(len(res) == 1)
+        self.assertTrue("isCriticalSystemObject" in res[0])
+        self.assertEquals(res[0]["isCriticalSystemObject"][0], "TRUE")
+
     def test_invalid_parent(self):
         """Test adding an object with invalid parent"""
         print "Test adding an object with invalid parent"""
@@ -527,14 +602,9 @@ class BasicTests(unittest.TestCase):
 
         self.delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
 
-    def test_multi_valued_attributes(self):
-        """Test multi-valued attributes"""
-        print "Test multi-valued attributes"""
-
-# TODO: In this test I added some special tests where I got very unusual
-# results back from a real AD. s4 doesn't match them and I've no idea how to
-# implement those error cases (maybe there exists a special trigger for
-# "description" attributes which handle them)
+    def test_description_attribute(self):
+        """Test description attribute"""
+        print "Test description attribute"""
 
         self.ldb.add({
             "dn": "cn=ldaptestgroup,cn=users," + self.base_dn,
@@ -542,6 +612,13 @@ class BasicTests(unittest.TestCase):
             "objectclass": "group",
             "description": "desc1"})
 
+        res = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn,
+                         scope=SCOPE_BASE, attrs=["description"])
+        self.assertTrue(len(res) == 1)
+        self.assertTrue("description" in res[0])
+        self.assertTrue(len(res[0]["description"]) == 1)
+        self.assertEquals(res[0]["description"][0], "desc1")
+
         self.delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
 
         self.ldb.add({
@@ -549,15 +626,37 @@ class BasicTests(unittest.TestCase):
             "objectclass": "group",
             "description": ["desc1", "desc2"]})
 
-#        m = Message()
-#        m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
-#        m["description"] = MessageElement(["desc1","desc2"], FLAG_MOD_REPLACE,
-#          "description")
-#        try:
-#            ldb.modify(m)
-#            self.fail()
-#        except LdbError, (num, _):
-#            self.assertEquals(num, ERR_ATTRIBUTE_OR_VALUE_EXISTS)
+        res = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn,
+                         scope=SCOPE_BASE, attrs=["description"])
+        self.assertTrue(len(res) == 1)
+        self.assertTrue("description" in res[0])
+        self.assertTrue(len(res[0]["description"]) == 2)
+        self.assertTrue(res[0]["description"][0] == "desc1" or
+                        res[0]["description"][1] == "desc1")
+        self.assertTrue(res[0]["description"][0] == "desc2" or
+                        res[0]["description"][1] == "desc2")
+
+        m = Message()
+        m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
+        m["description"] = MessageElement(["desc1","desc2"], FLAG_MOD_REPLACE,
+          "description")
+        try:
+            ldb.modify(m)
+            self.fail()
+        except LdbError, (num, _):
+            self.assertEquals(num, ERR_ATTRIBUTE_OR_VALUE_EXISTS)
+
+        m = Message()
+        m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
+        m["description"] = MessageElement(["desc1","desc2"], FLAG_MOD_DELETE,
+          "description")
+        ldb.modify(m)
+
+        self.delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
+
+        self.ldb.add({
+            "dn": "cn=ldaptestgroup,cn=users," + self.base_dn,
+            "objectclass": "group" })
 
         m = Message()
         m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
@@ -565,15 +664,42 @@ class BasicTests(unittest.TestCase):
           "description")
         ldb.modify(m)
 
-#        m = Message()
-#        m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
-#        m["description"] = MessageElement("desc3", FLAG_MOD_ADD,
-#          "description")
-#        try:
-#            ldb.modify(m)
-#            self.fail()
-#        except LdbError, (num, _):
-#            self.assertEquals(num, ERR_ATTRIBUTE_OR_VALUE_EXISTS)
+        res = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn,
+                         scope=SCOPE_BASE, attrs=["description"])
+        self.assertTrue(len(res) == 1)
+        self.assertTrue("description" in res[0])
+        self.assertTrue(len(res[0]["description"]) == 1)
+        self.assertEquals(res[0]["description"][0], "desc1")
+
+        self.delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
+
+        self.ldb.add({
+            "dn": "cn=ldaptestgroup,cn=users," + self.base_dn,
+            "objectclass": "group",
+            "description": ["desc1", "desc2"]})
+
+        m = Message()
+        m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
+        m["description"] = MessageElement("desc1", FLAG_MOD_REPLACE,
+          "description")
+        ldb.modify(m)
+
+        res = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn,
+                         scope=SCOPE_BASE, attrs=["description"])
+        self.assertTrue(len(res) == 1)
+        self.assertTrue("description" in res[0])
+        self.assertTrue(len(res[0]["description"]) == 1)
+        self.assertEquals(res[0]["description"][0], "desc1")
+
+        m = Message()
+        m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
+        m["description"] = MessageElement("desc3", FLAG_MOD_ADD,
+          "description")
+        try:
+            ldb.modify(m)
+            self.fail()
+        except LdbError, (num, _):
+            self.assertEquals(num, ERR_ATTRIBUTE_OR_VALUE_EXISTS)
 
         m = Message()
         m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
@@ -590,34 +716,100 @@ class BasicTests(unittest.TestCase):
         m["description"] = MessageElement("desc1", FLAG_MOD_DELETE,
           "description")
         ldb.modify(m)
+        res = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn,
+                         scope=SCOPE_BASE, attrs=["description"])
+        self.assertTrue(len(res) == 1)
+        self.assertFalse("description" in res[0])
 
-#        m = Message()
-#        m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
-#        m["description"] = MessageElement(["desc1","desc2"], FLAG_MOD_REPLACE,
-#          "description")
+        m = Message()
+        m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
+        m["description"] = MessageElement(["desc1","desc2"], FLAG_MOD_REPLACE,
+          "description")
+        try:
+            ldb.modify(m)
+            self.fail()
+        except LdbError, (num, _):
+            self.assertEquals(num, ERR_ATTRIBUTE_OR_VALUE_EXISTS)
+
+        m = Message()
+        m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
+        m["description"] = MessageElement(["desc3", "desc4"], FLAG_MOD_ADD,
+          "description")
+        try:
+            ldb.modify(m)
+            self.fail()
+        except LdbError, (num, _):
+            self.assertEquals(num, ERR_ATTRIBUTE_OR_VALUE_EXISTS)
+
+        m = Message()
+        m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
+        m["description"] = MessageElement("desc1", FLAG_MOD_ADD,
+          "description")
+        ldb.modify(m)
+
+        res = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn,
+                         scope=SCOPE_BASE, attrs=["description"])
+        self.assertTrue(len(res) == 1)
+        self.assertTrue("description" in res[0])
+        self.assertTrue(len(res[0]["description"]) == 1)
+        self.assertEquals(res[0]["description"][0], "desc1")
+
+        self.delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
+
+    def test_attribute_ranges(self):
+        """Test attribute ranges"""
+        print "Test attribute ranges"""
+
+        # Too short (min. 1)
+        try:
+            ldb.add({
+               "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
+               "objectClass": "person",
+               "sn": "" })
+            self.fail()
+        except LdbError, (num, _):
+            self.assertEquals(num, ERR_INVALID_ATTRIBUTE_SYNTAX)
+
+        # Too long (max. 64)
 #        try:
-#            ldb.modify(m)
+#            ldb.add({
+#               "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
+#               "objectClass": "person",
+#               "sn": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" })
 #            self.fail()
 #        except LdbError, (num, _):
-#            self.assertEquals(num, ERR_ATTRIBUTE_OR_VALUE_EXISTS)
+#            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
+
+        ldb.add({
+           "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
+           "objectClass": "person" })
+
+        # Too short (min. 1)
+        m = Message()
+        m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
+        m["sn"] = MessageElement("", FLAG_MOD_REPLACE, "sn")
+        try:
+            ldb.modify(m)
+            self.fail()
+        except LdbError, (num, _):
+            self.assertEquals(num, ERR_INVALID_ATTRIBUTE_SYNTAX)
 
+        # Too long (max. 64)
 #        m = Message()
-#        m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
-#        m["description"] = MessageElement(["desc3", "desc4"], FLAG_MOD_ADD,
-#          "description")
+#        m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
+#        m["sn"] = MessageElement("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", FLAG_MOD_REPLACE, "sn")
 #        try:
 #            ldb.modify(m)
 #            self.fail()
 #        except LdbError, (num, _):
-#            self.assertEquals(num, ERR_ATTRIBUTE_OR_VALUE_EXISTS)
+#            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
 
         m = Message()
-        m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
-        m["description"] = MessageElement("desc3", FLAG_MOD_ADD,
-          "description")
+        m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
+        m["sn"] = MessageElement("x", FLAG_MOD_REPLACE, "sn")
         ldb.modify(m)
 
-        self.delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
+        self.delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
 
     def test_empty_messages(self):
         """Test empty messages"""
@@ -1035,11 +1227,15 @@ objectClass: container
                           attrs=["objectGUID"]);
         res3 = ldb.search(base=self.base_dn, scope=SCOPE_BASE,
                           attrs=["parentGUID"]);
+        res4 = ldb.search(base=self.configuration_dn, scope=SCOPE_BASE,
+                          attrs=["parentGUID"]);
+        res5 = ldb.search(base=self.schema_dn, scope=SCOPE_BASE,
+                          attrs=["parentGUID"]);
 
         """Check if the parentGUID is valid """
         self.assertEquals(res1[0]["parentGUID"], res2[0]["objectGUID"]);
 
-        """Check if it returns nothing when there is no parent object"""
+        """Check if it returns nothing when there is no parent object - default NC"""
         has_parentGUID = False
         for key in res3[0].keys():
             if key == "parentGUID":
@@ -1047,6 +1243,22 @@ objectClass: container
                 break
         self.assertFalse(has_parentGUID);
 
+        """Check if it returns nothing when there is no parent object - configuration NC"""
+        has_parentGUID = False
+        for key in res4[0].keys():
+            if key == "parentGUID":
+                has_parentGUID = True
+                break
+        self.assertFalse(has_parentGUID);
+
+        """Check if it returns nothing when there is no parent object - schema NC"""
+        has_parentGUID = False
+        for key in res5[0].keys():
+            if key == "parentGUID":
+                has_parentGUID = True
+                break
+        self.assertFalse(has_parentGUID);
+
         """Ensures that if you look for another object attribute after the constructed
             parentGUID, it will return correctly"""
         has_another_attribute = False
@@ -1180,418 +1392,6 @@ objectClass: container
 
         self.delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
 
-    def test_groups(self):
-        """This tests the group behaviour (setting, changing) of a user account"""
-        print "Testing group behaviour\n"
-
-        ldb.add({
-            "dn": "cn=ldaptestgroup,cn=users," + self.base_dn,
-            "objectclass": "group"})
-
-        ldb.add({
-            "dn": "cn=ldaptestgroup2,cn=users," + self.base_dn,
-            "objectclass": "group"})
-
-        res1 = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn,
-                          scope=SCOPE_BASE, attrs=["objectSID"])
-        self.assertTrue(len(res1) == 1)
-        group_rid_1 = security.dom_sid(ldb.schema_format_value("objectSID",
-          res1[0]["objectSID"][0])).split()[1]
-
-        res1 = ldb.search("cn=ldaptestgroup2,cn=users," + self.base_dn,
-                          scope=SCOPE_BASE, attrs=["objectSID"])
-        self.assertTrue(len(res1) == 1)
-        group_rid_2 = security.dom_sid(ldb.schema_format_value("objectSID",
-          res1[0]["objectSID"][0])).split()[1]
-
-        # Try to create a user with an invalid primary group
-        try:
-            ldb.add({
-                "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
-                "objectclass": ["user", "person"],
-                "primaryGroupID": "0"})
-            self.fail()
-        except LdbError, (num, _):
-            self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
-        self.delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
-
-        # Try to Create a user with a valid primary group
-# TODO Some more investigation needed here
-#        try:
-#            ldb.add({
-#                "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
-#                "objectclass": ["user", "person"],
-#                "primaryGroupID": str(group_rid_1)})
-#            self.fail()
-#        except LdbError, (num, _):
-#            self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
-#        self.delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
-
-        # Test to see how we should behave when the user account doesn't
-        # exist
-        m = Message()
-        m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
-        m["primaryGroupID"] = MessageElement("0", FLAG_MOD_REPLACE,
-          "primaryGroupID")
-        try:
-            ldb.modify(m)
-            self.fail()
-        except LdbError, (num, _):
-            self.assertEquals(num, ERR_NO_SUCH_OBJECT)
-
-        # Test to see how we should behave when the account isn't a user
-        m = Message()
-        m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
-        m["primaryGroupID"] = MessageElement("0", FLAG_MOD_REPLACE,
-          "primaryGroupID")
-        try:
-            ldb.modify(m)
-            self.fail()
-        except LdbError, (num, _):
-            self.assertEquals(num, ERR_OBJECT_CLASS_VIOLATION)
-
-        ldb.add({
-            "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
-            "objectclass": ["user", "person"]})
-
-        # We should be able to reset our actual primary group
-        m = Message()
-        m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
-        m["primaryGroupID"] = MessageElement("513", FLAG_MOD_REPLACE,
-          "primaryGroupID")
-        ldb.modify(m)
-
-        # Try to add invalid primary group
-        m = Message()
-        m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
-        m["primaryGroupID"] = MessageElement("0", FLAG_MOD_REPLACE,
-          "primaryGroupID")
-        try:
-            ldb.modify(m)
-            self.fail()
-        except LdbError, (num, _):
-            self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
-
-        # Try to make group 1 primary - should be denied since it is not yet
-        # secondary
-        m = Message()
-        m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
-        m["primaryGroupID"] = MessageElement(str(group_rid_1),
-          FLAG_MOD_REPLACE, "primaryGroupID")
-        try:
-            ldb.modify(m)
-            self.fail()
-        except LdbError, (num, _):
-            self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
-
-        # Make group 1 secondary
-        m = Message()
-        m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
-        m["member"] = MessageElement("cn=ldaptestuser,cn=users," + self.base_dn,
-                                     FLAG_MOD_REPLACE, "member")
-        ldb.modify(m)
-
-        # Make group 1 primary
-        m = Message()
-        m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
-        m["primaryGroupID"] = MessageElement(str(group_rid_1),
-          FLAG_MOD_REPLACE, "primaryGroupID")
-        ldb.modify(m)
-
-        # Try to delete group 1 - should be denied
-        try:
-            ldb.delete("cn=ldaptestgroup,cn=users," + self.base_dn)
-            self.fail()
-        except LdbError, (num, _):
-            self.assertEquals(num, ERR_ENTRY_ALREADY_EXISTS)
-
-        # Try to add group 1 also as secondary - should be denied
-        m = Message()
-        m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
-        m["member"] = MessageElement("cn=ldaptestuser,cn=users," + self.base_dn,
-                                     FLAG_MOD_ADD, "member")
-        try:
-            ldb.modify(m)
-            self.fail()
-        except LdbError, (num, _):
-            self.assertEquals(num, ERR_ENTRY_ALREADY_EXISTS)
-
-        # Try to add invalid member to group 1 - should be denied
-        m = Message()
-        m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
-        m["member"] = MessageElement(
-          "cn=ldaptestuser3,cn=users," + self.base_dn,
-          FLAG_MOD_ADD, "member")
-        try:
-            ldb.modify(m)
-            self.fail()
-        except LdbError, (num, _):
-            self.assertEquals(num, ERR_NO_SUCH_OBJECT)
-
-        # Make group 2 secondary
-        m = Message()
-        m.dn = Dn(ldb, "cn=ldaptestgroup2,cn=users," + self.base_dn)
-        m["member"] = MessageElement("cn=ldaptestuser,cn=users," + self.base_dn,
-                                     FLAG_MOD_ADD, "member")
-        ldb.modify(m)
-
-        # Swap the groups
-        m = Message()
-        m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
-        m["primaryGroupID"] = MessageElement(str(group_rid_2),
-          FLAG_MOD_REPLACE, "primaryGroupID")
-        ldb.modify(m)
-
-        # Old primary group should contain a "member" attribute for the user,
-        # the new shouldn't contain anymore one
-        res1 = ldb.search("cn=ldaptestgroup, cn=users," + self.base_dn,
-                          scope=SCOPE_BASE, attrs=["member"])
-        self.assertTrue(len(res1) == 1)
-        self.assertTrue(len(res1[0]["member"]) == 1)
-        self.assertEquals(res1[0]["member"][0].lower(),
-          ("cn=ldaptestuser,cn=users," + self.base_dn).lower())
-
-        res1 = ldb.search("cn=ldaptestgroup2, cn=users," + self.base_dn,
-                          scope=SCOPE_BASE, attrs=["member"])
-        self.assertTrue(len(res1) == 1)
-        self.assertFalse("member" in res1[0])
-
-        # Also this should be denied
-        try:
-            ldb.add({
-              "dn": "cn=ldaptestuser1,cn=users," + self.base_dn,
-              "objectclass": ["user", "person"],
-              "primaryGroupID": "0"})
-            self.fail()
-        except LdbError, (num, _):
-            self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
-
-        self.delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
-        self.delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
-        self.delete_force(self.ldb, "cn=ldaptestgroup2,cn=users," + self.base_dn)
-
-    def test_sam_attributes(self):
-        """Test the behaviour of special attributes of SAM objects"""
-        print "Testing the behaviour of special attributes of SAM objects\n"""
-
-        ldb.add({
-            "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
-            "objectclass": ["user", "person"]})
-        ldb.add({
-            "dn": "cn=ldaptestgroup,cn=users," + self.base_dn,
-            "objectclass": "group"})
-
-        m = Message()
-        m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
-        m["groupType"] = MessageElement("0", FLAG_MOD_ADD,
-          "groupType")
-        try:
-            ldb.modify(m)
-            self.fail()
-        except LdbError, (num, _):
-            self.assertEquals(num, ERR_ATTRIBUTE_OR_VALUE_EXISTS)
-
-        m = Message()
-        m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
-        m["groupType"] = MessageElement([], FLAG_MOD_DELETE,
-          "groupType")
-        try:
-            ldb.modify(m)
-            self.fail()
-        except LdbError, (num, _):
-            self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
-
-        m = Message()
-        m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
-        m["primaryGroupID"] = MessageElement("0", FLAG_MOD_ADD,
-          "primaryGroupID")
-        try:
-            ldb.modify(m)
-            self.fail()
-        except LdbError, (num, _):
-            self.assertEquals(num, ERR_ATTRIBUTE_OR_VALUE_EXISTS)
-
-        m = Message()
-        m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
-        m["primaryGroupID"] = MessageElement([], FLAG_MOD_DELETE,
-          "primaryGroupID")
-        try:
-            ldb.modify(m)
-            self.fail()
-        except LdbError, (num, _):
-            self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
-
-        m = Message()
-        m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
-        m["userAccountControl"] = MessageElement("0", FLAG_MOD_ADD,
-          "userAccountControl")
-        try:
-            ldb.modify(m)
-            self.fail()
-        except LdbError, (num, _):
-            self.assertEquals(num, ERR_ATTRIBUTE_OR_VALUE_EXISTS)
-
-        m = Message()
-        m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
-        m["userAccountControl"] = MessageElement([], FLAG_MOD_DELETE,
-          "userAccountControl")
-        try:
-            ldb.modify(m)
-            self.fail()
-        except LdbError, (num, _):
-            self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
-
-        m = Message()
-        m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
-        m["sAMAccountType"] = MessageElement("0", FLAG_MOD_ADD,
-          "sAMAccountType")
-        try:
-            ldb.modify(m)
-            self.fail()
-        except LdbError, (num, _):
-            self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
-
-        m = Message()
-        m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
-        m["sAMAccountType"] = MessageElement([], FLAG_MOD_REPLACE,
-          "sAMAccountType")
-        try:
-            ldb.modify(m)
-            self.fail()
-        except LdbError, (num, _):
-            self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
-
-        m = Message()
-        m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
-        m["sAMAccountType"] = MessageElement([], FLAG_MOD_DELETE,
-          "sAMAccountType")
-        try:
-            ldb.modify(m)
-            self.fail()
-        except LdbError, (num, _):
-            self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
-
-        self.delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
-        self.delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
-
-    def test_primary_group_token_constructed(self):
-        """Test the primary group token behaviour (hidden-generated-readonly attribute on groups) and some other constructed attributes"""
-        print "Testing primary group token behaviour and other constructed attributes\n"
-
-        try:
-            ldb.add({
-                "dn": "cn=ldaptestgroup,cn=users," + self.base_dn,
-                "objectclass": "group",
-                "primaryGroupToken": "100"})
-            self.fail()
-        except LdbError, (num, _):
-            self.assertEquals(num, ERR_UNDEFINED_ATTRIBUTE_TYPE)
-        self.delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
-
-        ldb.add({
-            "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
-            "objectclass": ["user", "person"]})
-
-        ldb.add({
-            "dn": "cn=ldaptestgroup,cn=users," + self.base_dn,
-            "objectclass": "group"})
-
-        # Testing for one invalid, and one valid operational attribute, but also the things they are built from
-        res1 = ldb.search(self.base_dn,
-                          scope=SCOPE_BASE, attrs=["primaryGroupToken", "canonicalName", "objectClass", "objectSid"])
-        self.assertTrue(len(res1) == 1)
-        self.assertFalse("primaryGroupToken" in res1[0])
-        self.assertTrue("canonicalName" in res1[0])
-        self.assertTrue("objectClass" in res1[0])
-        self.assertTrue("objectSid" in res1[0])
-
-        res1 = ldb.search(self.base_dn,
-                          scope=SCOPE_BASE, attrs=["primaryGroupToken", "canonicalName"])
-        self.assertTrue(len(res1) == 1)
-        self.assertFalse("primaryGroupToken" in res1[0])
-        self.assertFalse("objectSid" in res1[0])
-        self.assertFalse("objectClass" in res1[0])
-        self.assertTrue("canonicalName" in res1[0])
-
-        res1 = ldb.search("cn=users,"+self.base_dn,
-                          scope=SCOPE_BASE, attrs=["primaryGroupToken"])
-        self.assertTrue(len(res1) == 1)
-        self.assertFalse("primaryGroupToken" in res1[0])
-
-        res1 = ldb.search("cn=ldaptestuser, cn=users," + self.base_dn,
-                          scope=SCOPE_BASE, attrs=["primaryGroupToken"])
-        self.assertTrue(len(res1) == 1)
-        self.assertFalse("primaryGroupToken" in res1[0])
-
-        res1 = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn,
-                          scope=SCOPE_BASE)
-        self.assertTrue(len(res1) == 1)
-        self.assertFalse("primaryGroupToken" in res1[0])
-
-        res1 = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn,
-                          scope=SCOPE_BASE, attrs=["primaryGroupToken", "objectSID"])
-        self.assertTrue(len(res1) == 1)
-        primary_group_token = int(res1[0]["primaryGroupToken"][0])
-
-        rid = security.dom_sid(ldb.schema_format_value("objectSID", res1[0]["objectSID"][0])).split()[1]
-        self.assertEquals(primary_group_token, rid)
-
-        m = Message()
-        m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
-        m["primaryGroupToken"] = "100"
-        try:
-            ldb.modify(m)
-            self.fail()
-        except LdbError, (num, _):
-            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
-
-        self.delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
-        self.delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
-
-    def test_tokenGroups(self):
-        """Test the tokenGroups behaviour (hidden-generated-readonly attribute on SAM objects)"""
-        print "Testing tokenGroups behaviour\n"
-
-        # The domain object shouldn't contain any "tokenGroups" entry
-        res = ldb.search(self.base_dn, scope=SCOPE_BASE, attrs=["tokenGroups"])
-        self.assertTrue(len(res) == 1)
-        self.assertFalse("tokenGroups" in res[0])
-
-        # The domain administrator should contain "tokenGroups" entries
-        # (the exact number depends on the domain/forest function level and the
-        # DC software versions)
-        res = ldb.search("cn=Administrator,cn=Users," + self.base_dn,
-                         scope=SCOPE_BASE, attrs=["tokenGroups"])
-        self.assertTrue(len(res) == 1)
-        self.assertTrue("tokenGroups" in res[0])
-
-        ldb.add({
-            "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
-            "objectclass": ["user", "person"]})
-
-        # This testuser should contain at least two "tokenGroups" entries
-        # (exactly two on an unmodified "Domain Users" and "Users" group)
-        res = ldb.search("cn=ldaptestuser,cn=users," + self.base_dn,
-                         scope=SCOPE_BASE, attrs=["tokenGroups"])
-        self.assertTrue(len(res) == 1)
-        self.assertTrue(len(res[0]["tokenGroups"]) >= 2)
-
-        # one entry which we need to find should point to domains "Domain Users"
-        # group and another entry should point to the builtin "Users"group
-        domain_users_group_found = False
-        users_group_found = False
-        for sid in res[0]["tokenGroups"]:
-            rid = security.dom_sid(ldb.schema_format_value("objectSID", sid)).split()[1]
-            if rid == 513:
-                domain_users_group_found = True
-            if rid == 545:
-                users_group_found = True
-
-        self.assertTrue(domain_users_group_found)
-        self.assertTrue(users_group_found)
-
-        self.delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
-
     def test_wkguid(self):
         """Test Well known GUID behaviours (including DN+Binary)"""
         print "Test Well known GUID behaviours (including DN+Binary)"""
@@ -1621,95 +1421,6 @@ objectClass: container
         self.assertEquals(len(res), 1)
         self.assertTrue("subScheamSubEntry" not in res[0])
 
-    def test_delete(self):
-        """Tests the delete operation"""
-        print "Tests the delete operations"""
-
-        ldb.add({
-            "dn": "cn=ldaptestcontainer," + self.base_dn,
-            "objectclass": "container"})
-        ldb.add({
-            "dn": "cn=entry1,cn=ldaptestcontainer," + self.base_dn,
-            "objectclass": "container"})
-        ldb.add({
-            "dn": "cn=entry2,cn=ldaptestcontainer," + self.base_dn,
-            "objectclass": "container"})
-
-        try:
-            ldb.delete("cn=ldaptestcontainer," + self.base_dn)
-            self.fail()
-        except LdbError, (num, _):
-            self.assertEquals(num, ERR_NOT_ALLOWED_ON_NON_LEAF)
-
-        ldb.delete("cn=ldaptestcontainer," + self.base_dn, ["tree_delete:0"])
-
-        try:
-            res = ldb.search("cn=ldaptestcontainer," + self.base_dn,
-                             scope=SCOPE_BASE, attrs=[])
-            self.fail()
-        except LdbError, (num, _):
-            self.assertEquals(num, ERR_NO_SUCH_OBJECT)
-        try:
-            res = ldb.search("cn=entry1,cn=ldaptestcontainer," + self.base_dn,
-                             scope=SCOPE_BASE, attrs=[])
-            self.fail()
-        except LdbError, (num, _):
-            self.assertEquals(num, ERR_NO_SUCH_OBJECT)
-        try:
-            res = ldb.search("cn=entry2,cn=ldaptestcontainer," + self.base_dn,
-                             scope=SCOPE_BASE, attrs=[])
-            self.fail()
-        except LdbError, (num, _):
-            self.assertEquals(num, ERR_NO_SUCH_OBJECT)
-
-        self.delete_force(self.ldb, "cn=entry1,cn=ldaptestcontainer," + self.base_dn)
-        self.delete_force(self.ldb, "cn=entry2,cn=ldaptestcontainer," + self.base_dn)
-        self.delete_force(self.ldb, "cn=ldaptestcontainer," + self.base_dn)
-
-        # Performs some protected object delete testing
-
-        res = ldb.search(base="", expression="", scope=SCOPE_BASE,
-                         attrs=["dsServiceName", "dNSHostName"])
-        self.assertEquals(len(res), 1)
-
-        try:
-            ldb.delete(res[0]["dsServiceName"][0])
-            self.fail()
-        except LdbError, (num, _):
-            self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
-
-        res = ldb.search(self.base_dn, scope=SCOPE_SUBTREE,
-                         attrs=["rIDSetReferences"],
-                         expression="(&(objectClass=computer)(dNSHostName=" + res[0]["dNSHostName"][0] + "))")
-        self.assertEquals(len(res), 1)
-
-        try:
-            ldb.delete(res[0]["rIDSetReferences"][0])
-            self.fail()
-        except LdbError, (num, _):
-            self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
-
-        try:
-            ldb.delete("cn=Enterprise Schema,cn=Partitions," + self.configuration_dn)
-            self.fail()
-        except LdbError, (num, _):
-            self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
-
-        try:
-            ldb.delete("cn=Enterprise Configuration,cn=Partitions," + self.configuration_dn)
-            self.fail()
-        except LdbError, (num, _):
-            self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
-
-        # Performs some "systemFlags" testing
-
-        # Delete failing since "SYSTEM_FLAG_DISALLOW_DELETE"
-        try:
-            ldb.delete("CN=Users," + self.base_dn)
-            self.fail()
-        except LdbError, (num, _):
-            self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
-
     def test_all(self):
         """Basic tests"""
 
@@ -2771,6 +2482,36 @@ nTSecurityDescriptor:: """ + desc_base64
         finally:
             self.delete_force(self.ldb, user_dn)
 
+    def test_dsheuristics(self):
+        """Tests the 'dSHeuristics' attribute"""
+        print "Tests the 'dSHeuristics' attribute"""
+
+        # Get the current value to restore it later
+        res = self.ldb.search("CN=Directory Service, CN=Windows NT, CN=Services, "
+                              + self.configuration_dn, scope=SCOPE_BASE, attrs=["dSHeuristics"])
+        if "dSHeuristics" in res[0]:
+            dsheuristics = res[0]["dSHeuristics"][0]
+        else:
+            dsheuristics = None
+        # Should not be longer than 18 chars?
+        try:
+            self.set_dsheuristics("123ABC-+!1asdfg@#^12")
+        except LdbError, (num, _):
+            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
+        # If it is >= 10 chars, tenthChar should be 1
+        try:
+            self.set_dsheuristics("00020000000002")
+        except LdbError, (num, _):
+            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
+        # apart from the above, all char values are accepted
+        self.set_dsheuristics("123ABC-+!1asdfg@#^")
+        res = self.ldb.search("CN=Directory Service, CN=Windows NT, CN=Services, "
+                              + self.configuration_dn, scope=SCOPE_BASE, attrs=["dSHeuristics"])
+        self.assertTrue("dSHeuristics" in res[0])
+        self.assertEquals(res[0]["dSHeuristics"][0], "123ABC-+!1asdfg@#^")
+        # restore old value
+        self.set_dsheuristics(dsheuristics)
+
 
 class BaseDnTests(unittest.TestCase):
 
@@ -2815,6 +2556,40 @@ class BaseDnTests(unittest.TestCase):
         self.assertTrue(res[0]["configurationNamingContext"][0] in ncs)
         self.assertTrue(res[0]["schemaNamingContext"][0] in ncs)
 
+    def test_serverPath(self):
+        """Testing the server paths in rootDSE"""
+        res = self.ldb.search("", scope=SCOPE_BASE,
+                              attrs=["dsServiceName", "serverName"])
+        self.assertEquals(len(res), 1)
+
+        self.assertTrue("CN=Servers" in res[0]["dsServiceName"][0])
+        self.assertTrue("CN=Sites" in res[0]["dsServiceName"][0])
+        self.assertTrue("CN=NTDS Settings" in res[0]["dsServiceName"][0])
+        self.assertTrue("CN=Servers" in res[0]["serverName"][0])
+        self.assertTrue("CN=Sites" in res[0]["serverName"][0])
+        self.assertFalse("CN=NTDS Settings" in res[0]["serverName"][0])
+
+    def test_dnsHostname(self):
+        """Testing the DNS hostname in rootDSE"""
+        res = self.ldb.search("", scope=SCOPE_BASE,
+                              attrs=["dnsHostName", "serverName"])
+        self.assertEquals(len(res), 1)
+
+        res2 = self.ldb.search(res[0]["serverName"][0], scope=SCOPE_BASE,
+                               attrs=["dNSHostName"])
+        self.assertEquals(len(res2), 1)
+
+        self.assertEquals(res[0]["dnsHostName"][0], res2[0]["dNSHostName"][0])
+
+    def test_ldapServiceName(self):
+        """Testing the ldap service name in rootDSE"""
+        res = self.ldb.search("", scope=SCOPE_BASE,
+                              attrs=["ldapServiceName", "dNSHostName"])
+        self.assertEquals(len(res), 1)
+
+        (hostname, _, dns_domainname) = res[0]["dNSHostName"][0].partition(".")
+        self.assertTrue(":%s$@%s" % (hostname, dns_domainname.upper())
+                        in res[0]["ldapServiceName"][0])
 
 if not "://" in host:
     if os.path.isfile(host):