s4:samldb/objectclass_attrs LDB modules - move "description" logic from "objectclass_...
authorMatthias Dieter Wallnöfer <mdw@samba.org>
Fri, 12 Nov 2010 17:57:57 +0000 (18:57 +0100)
committerMatthias Dieter Wallnöfer <mdw@samba.org>
Fri, 12 Nov 2010 18:55:04 +0000 (18:55 +0000)
This according to an answer from dochelp is SAM specific behaviour.

source4/dsdb/samdb/ldb_modules/objectclass_attrs.c
source4/dsdb/samdb/ldb_modules/samldb.c
source4/dsdb/tests/python/ldap.py
source4/dsdb/tests/python/sam.py

index 26eaaeaae52eaf8e177a5d0fd5c57605ddaf79f2..67d11b302d66d7d9fe476fb6e7cf96da20df37a8 100644 (file)
@@ -158,49 +158,6 @@ static int attr_handler(struct oc_context *ac)
                        }
                }
 
-               /* "description" on AD is very special: it's nearly single-
-                * valued (only on add operations it isn't). */
-               if ((ac->req->operation == LDB_MODIFY) &&
-                   (ldb_attr_cmp(attr->lDAPDisplayName, "description") == 0)) {
-                       /* Multi-valued add or replace operations are always
-                        * denied */
-                       if ((LDB_FLAG_MOD_TYPE(msg->elements[i].flags)
-                           != LDB_FLAG_MOD_DELETE) &&
-                           (msg->elements[i].num_values > 1)) {
-                               ldb_asprintf_errstring(ldb,
-                                                      "objectclass_attrs: attribute '%s' on entry '%s' is changed using a multi-valued add or replace operation!",
-                                                      msg->elements[i].name,
-                                                      ldb_dn_get_linearized(msg->dn));
-                               return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
-                       }
-
-                       /* Add operations are only allowed if no value exists */
-                       if (LDB_FLAG_MOD_TYPE(msg->elements[i].flags)
-                           == LDB_FLAG_MOD_ADD) {
-                               const char *attrs[] = { attr->lDAPDisplayName,
-                                                       NULL };
-                               struct ldb_result *res;
-                               struct ldb_message_element *el;
-
-                               ret = ldb_search(ldb, ac, &res, msg->dn,
-                                                LDB_SCOPE_BASE, attrs, NULL);
-                               if (ret != LDB_SUCCESS) {
-                                       return ret;
-                               }
-
-                               el = ldb_msg_find_element(res->msgs[0],
-                                                         attr->lDAPDisplayName);
-                               if (el != NULL) {
-                                       ldb_asprintf_errstring(ldb,
-                                                              "objectclass_attrs: attribute '%s' on entry '%s' is changed using an add operation, but there a value already exists!",
-                                                              msg->elements[i].name,
-                                                              ldb_dn_get_linearized(msg->dn));
-                                       return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
-                               }
-                               talloc_free(res);
-                       }
-               }
-
                /* "dSHeuristics" syntax check */
                if (ldb_attr_cmp(attr->lDAPDisplayName, "dSHeuristics") == 0) {
                        ret = oc_validate_dsheuristics(&(msg->elements[i]));
index 0cd8bc9bcca57ca595993682f0efe7543adcaaaa..4b8a303753bda97c589a962e7448990f2c546dc0 100644 (file)
@@ -1496,6 +1496,79 @@ static int samldb_member_check(struct samldb_ctx *ac)
                }
        }
 
+       talloc_free(res);
+
+       return LDB_SUCCESS;
+}
+
+/* SAM objects have special rules regarding the "description" attribute on
+ * modify operations. */
+static int samldb_description_check(struct samldb_ctx *ac)
+{
+       struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
+       const char * const attrs[] = { "objectClass", "description", NULL };
+       struct ldb_message_element *el;
+       struct ldb_result *res;
+       unsigned int i;
+       int ret;
+
+       /* Fetch informations from the existing object */
+
+       ret = ldb_search(ldb, ac, &res, ac->msg->dn, LDB_SCOPE_BASE, attrs,
+                        NULL);
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
+       if (res->count != 1) {
+               return ldb_operr(ldb);
+       }
+
+       /* if it's not a SAM object then please skip the constraints */
+       if ((samdb_find_attribute(ldb, res->msgs[0], "objectClass",
+                                 "group") == NULL) &&
+           (samdb_find_attribute(ldb, res->msgs[0], "objectClass",
+                                 "samDomain") == NULL) &&
+           (samdb_find_attribute(ldb, res->msgs[0], "objectClass",
+                                 "samServer") == NULL) &&
+           (samdb_find_attribute(ldb, res->msgs[0], "objectClass",
+                                 "user") == NULL)) {
+               talloc_free(res);
+               return LDB_SUCCESS;
+       }
+
+       /* We've to walk over all modification entries and consider the
+        * "description" ones. */
+       for (i = 0; i < ac->msg->num_elements; i++) {
+               if (ldb_attr_cmp(ac->msg->elements[i].name,
+                                "description") != 0) {
+                       continue;
+               }
+
+               el = &ac->msg->elements[i];
+
+               /* Multi-valued add or replace operations are always denied */
+               if ((LDB_FLAG_MOD_TYPE(el->flags) != LDB_FLAG_MOD_DELETE) &&
+                   (el->num_values > 1)) {
+                       ldb_asprintf_errstring(ldb,
+                                              "samldb: Description on SAM entry '%s' is changed using a multi-valued add or replace operation!",
+                                              ldb_dn_get_linearized(ac->msg->dn));
+                       return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
+               }
+
+               /* Add operations are only allowed if no value exists */
+               if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_ADD) {
+                       if (ldb_msg_find_element(res->msgs[0], "description")
+                                                               != NULL) {
+                               ldb_asprintf_errstring(ldb,
+                                                      "samldb: Description on SAM entry '%s' is changed using an add operation while a value already exists!",
+                                                      ldb_dn_get_linearized(ac->msg->dn));
+                               return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
+                       }
+               }
+       }
+
+       talloc_free(res);
+
        return LDB_SUCCESS;
 }
 
@@ -1894,6 +1967,14 @@ static int samldb_modify(struct ldb_module *module, struct ldb_request *req)
                }
        }
 
+       el = ldb_msg_find_element(ac->msg, "description");
+       if (el != NULL) {
+               ret = samldb_description_check(ac);
+               if (ret != LDB_SUCCESS) {
+                       return ret;
+               }
+       }
+
        el = ldb_msg_find_element(ac->msg, "dNSHostName");
        el2 = ldb_msg_find_element(ac->msg, "sAMAccountName");
        if ((el != NULL) || (el2 != NULL)) {
index 833e141b7def4ae6477dae211f792a1a97afa05f..26a0d720c58be9851c5901d6ef7279859e33d500 100755 (executable)
@@ -618,160 +618,6 @@ class BasicTests(unittest.TestCase):
 
         self.delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
 
-    def test_description_attribute(self):
-        """Test description attribute"""
-        print "Test description attribute"""
-
-        self.ldb.add({
-            "dn": "cn=ldaptestgroup,cn=users," + self.base_dn,
-            "description": "desc2",
-            "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({
-            "dn": "cn=ldaptestgroup,cn=users," + self.base_dn,
-            "objectclass": "group",
-            "description": ["desc1", "desc2"]})
-
-        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)
-        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")
-
-        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)
-        m["description"] = MessageElement(["desc1","desc2"], FLAG_MOD_DELETE,
-          "description")
-        try:
-            ldb.modify(m)
-            self.fail()
-        except LdbError, (num, _):
-            self.assertEquals(num, ERR_NO_SUCH_ATTRIBUTE)
-
-        m = Message()
-        m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
-        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")
-        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"""
@@ -1943,6 +1789,15 @@ servicePrincipalName: host/ldaptest2computer29
                  "objectClass": "user",
                  "cn": "LDAPtestUSER4"})
 
+        # Here we don't enforce these hard "description" constraints
+        ldb.modify_ldif("""
+dn: cn=ldaptestcontainer,""" + self.base_dn + """
+changetype: modify
+replace: description
+description: desc1
+description: desc2
+""")
+
         ldb.modify_ldif("""
 dn: cn=ldaptestgroup2,cn=users,""" + self.base_dn + """
 changetype: modify
index 245d0516599fccf1c32be73dbda0f517a3b554c1..b64cce1c3f5b92742342081e4eff96dd718537aa 100755 (executable)
@@ -17,7 +17,7 @@ from samba.auth import system_session
 from ldb import 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_OTHER
+from ldb import ERR_OTHER, ERR_NO_SUCH_ATTRIBUTE
 from ldb import ERR_OBJECT_CLASS_VIOLATION
 from ldb import ERR_CONSTRAINT_VIOLATION
 from ldb import ERR_UNDEFINED_ATTRIBUTE_TYPE
@@ -2251,6 +2251,160 @@ class SamTests(unittest.TestCase):
 
         self.delete_force(self.ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn)
 
+    def test_sam_description_attribute(self):
+        """Test SAM description attribute"""
+        print "Test SAM description attribute"""
+
+        self.ldb.add({
+            "dn": "cn=ldaptestgroup,cn=users," + self.base_dn,
+            "description": "desc2",
+            "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({
+            "dn": "cn=ldaptestgroup,cn=users," + self.base_dn,
+            "objectclass": "group",
+            "description": ["desc1", "desc2"]})
+
+        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)
+        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")
+
+        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)
+        m["description"] = MessageElement(["desc1","desc2"], FLAG_MOD_DELETE,
+          "description")
+        try:
+            ldb.modify(m)
+            self.fail()
+        except LdbError, (num, _):
+            self.assertEquals(num, ERR_NO_SUCH_ATTRIBUTE)
+
+        m = Message()
+        m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
+        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")
+        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)
+
 
 if not "://" in host:
     if os.path.isfile(host):