r25781: Handle and test linked attribute renames.
authorAndrew Bartlett <abartlet@samba.org>
Thu, 1 Nov 2007 11:34:06 +0000 (12:34 +0100)
committerStefan Metzmacher <metze@samba.org>
Fri, 21 Dec 2007 04:43:56 +0000 (05:43 +0100)
Andrew Bartlett
(This used to be commit 56d9dd5140b6d7d7bbaa2f59ecdff7ee70c4faac)

source4/dsdb/samdb/ldb_modules/linked_attributes.c
testprogs/ejs/ldap.js

index d3093dbd714785ca69f6513ddd59baf6e1cbf279..7721296b7cf95d0def65605b45aefb9e24bd8885 100644 (file)
@@ -41,6 +41,8 @@ struct linked_attributes_context {
        struct ldb_request **down_req;
        int num_requests;
        int finished_requests;
+
+       const char **linked_attrs;
 };
 
 static struct linked_attributes_context *linked_attributes_init_handle(struct ldb_request *req, 
@@ -369,22 +371,323 @@ static int linked_attributes_modify(struct ldb_module *module, struct ldb_reques
        return ret;
 }
 
-/* delete */
-static int linked_attributes_delete(struct ldb_module *module, struct ldb_request *req)
+static int setup_modifies(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, 
+                         struct linked_attributes_context *ac,
+                         struct ldb_message *msg, 
+                         struct ldb_dn *olddn, struct ldb_dn *newdn) 
 {
-       /* Look up list of linked attributes */
-       /* Search to see if any linked attributes are in this entry */
-       return ldb_next_request(module, req);
+       int i, j, ret = LDB_SUCCESS;
+       const struct dsdb_schema *schema = dsdb_get_schema(ldb);
+       /* Look up each of the returned attributes */
+       /* Find their schema */
+       /* And it is an actual entry: now create a series of modify requests */
+       for (i=0; i < msg->num_elements; i++) {
+               int otherid;
+               const struct dsdb_attribute *target_attr;
+               const struct ldb_message_element *el = &msg->elements[i];
+               const struct dsdb_attribute *schema_attr
+                       = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
+               if (!schema_attr) {
+                       ldb_asprintf_errstring(ldb, 
+                                              "attribute %s is not a valid attribute in schema", el->name);
+                       return LDB_ERR_OBJECT_CLASS_VIOLATION;                  
+               }
+               /* We have a valid attribute, but if it's not linked they maybe we just got an extra return on our search... */
+               if (schema_attr->linkID == 0) {
+                       continue;
+               }
+               
+               /* Depending on which direction this link is in, we need to find it's partner */
+               if ((schema_attr->linkID & 1) == 1) {
+                       otherid = schema_attr->linkID - 1;
+               } else {
+                       otherid = schema_attr->linkID + 1;
+               }
+               
+               /* Now find the target attribute */
+               target_attr = dsdb_attribute_by_linkID(schema, otherid);
+               if (!target_attr) {
+                       ldb_asprintf_errstring(ldb, 
+                                              "attribute %s does not have valid link target", el->name);
+                       return LDB_ERR_OBJECT_CLASS_VIOLATION;                  
+               }
+               
+               /* For each value being moded, we need to setup the modify */
+               for (j=0; j < el->num_values; j++) {
+                       struct ldb_message_element *ret_el;
+                       struct ldb_request *new_req;
+                       /* Create the modify request */
+                       struct ldb_message *new_msg = ldb_msg_new(ac->down_req);
+                       if (!new_msg) {
+                               ldb_oom(ldb);
+                               return LDB_ERR_OPERATIONS_ERROR;
+                       }
+                       new_msg->dn = ldb_dn_new(new_msg, ldb, (char *)el->values[j].data);
+                       if (!new_msg->dn) {
+                               ldb_asprintf_errstring(ldb, 
+                                                      "attribute %s value %s was not a valid DN", msg->elements[i].name,
+                                                      el->values[j].data);
+                               return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
+                       }
+                       
+                       if (olddn) {
+                               ret = ldb_msg_add_empty(new_msg, target_attr->lDAPDisplayName, 
+                                                       LDB_FLAG_MOD_DELETE, &ret_el);
+                               if (ret != LDB_SUCCESS) {
+                                       return ret;
+                               }       
+                               ret_el->values = talloc_array(new_msg, struct ldb_val, 1);
+                               if (!ret_el->values) {
+                                       ldb_oom(ldb);
+                                       return LDB_ERR_OPERATIONS_ERROR;
+                               }
+                               ret_el->values[0] = data_blob_string_const(ldb_dn_get_linearized(olddn));
+                               ret_el->num_values = 1;
+                       }
+                       
+                       if (newdn) {
+                               ret = ldb_msg_add_empty(new_msg, target_attr->lDAPDisplayName, 
+                                                       LDB_FLAG_MOD_ADD, &ret_el);
+                               if (ret != LDB_SUCCESS) {
+                                       return ret;
+                               }       
+                               ret_el->values = talloc_array(new_msg, struct ldb_val, 1);
+                               if (!ret_el->values) {
+                                       ldb_oom(ldb);
+                                       return LDB_ERR_OPERATIONS_ERROR;
+                               }
+                               ret_el->values[0] = data_blob_string_const(ldb_dn_get_linearized(newdn));
+                               ret_el->num_values = 1;
+                       }
+
+                       ret = ldb_build_mod_req(&new_req, ldb, ac->down_req,
+                                               new_msg,
+                                               NULL,
+                                               NULL,
+                                               NULL);
+                       if (ret != LDB_SUCCESS) {
+                               return ret;
+                       }
+                       
+                       talloc_steal(new_req, new_msg);
+                       
+                       ldb_set_timeout_from_prev_req(ldb, ac->orig_req, new_req);
+                       
+                       /* Now add it to the list */
+                       ac->down_req = talloc_realloc(ac, ac->down_req, 
+                                                     struct ldb_request *, ac->num_requests + 1);
+                       if (!ac->down_req) {
+                               ldb_oom(ldb);
+                               return LDB_ERR_OPERATIONS_ERROR;
+                       }
+                       ac->down_req[ac->num_requests] = new_req;
+                       ac->num_requests++;
+                       
+                       /* Run the new request */
+                       ret = ldb_next_request(ac->module, new_req);
+                       if (ret != LDB_SUCCESS) {
+                               return ret;
+                       }
+               }
+       }
+       return ret;
 }
 
+static int linked_attributes_rename_del_search_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares) 
+{
+       struct ldb_request *req;
+       struct linked_attributes_context *ac = talloc_get_type(context, struct linked_attributes_context);
+       struct ldb_dn *olddn, *newdn;
+       TALLOC_CTX *mem_ctx = talloc_new(ac);
+    
+       if (!mem_ctx) {
+               ldb_oom(ldb);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       
+       switch (ac->orig_req->operation) {
+       case LDB_DELETE:
+       {
+               olddn = ac->orig_req->op.del.dn;
+               newdn = NULL;
+               break;
+       } 
+       case LDB_RENAME:
+       {
+               olddn = ac->orig_req->op.rename.olddn;
+               newdn = ac->orig_req->op.rename.newdn;
+               break;
+       }       
+       default:
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       
+
+       /* OK, we have one search result here: */
+
+       /* Only entries are interesting, and we only want the olddn */
+       if (ares->type == LDB_REPLY_ENTRY
+           && ldb_dn_compare(ares->message->dn, olddn) == 0) {
+               /* only bother at all if there were some linked attributes found */
+               if (ares->message->num_elements > 0) {
+                       return setup_modifies(ldb, mem_ctx, ac,
+                                             ares->message, olddn, newdn);
+               }
+               talloc_free(ares);
+               return LDB_SUCCESS;
+       } else if (ares->type == LDB_REPLY_ENTRY) {
+               /* Guh?  We only asked for this DN */
+               return LDB_ERR_OPERATIONS_ERROR;
+       } else if (ares->type == LDB_REPLY_DONE) {
+               req = talloc(mem_ctx, struct ldb_request);
+               *req = *ac->orig_req;
+               talloc_free(ares);
+
+               ac->down_req = talloc_realloc(ac, ac->down_req, 
+                                             struct ldb_request *, ac->num_requests + 1);
+               if (!ac->down_req) {
+                       ldb_oom(ldb);
+                       return LDB_ERR_OPERATIONS_ERROR;
+               }
+               ac->down_req[ac->num_requests] = req;
+               ac->num_requests++;
+               
+               return ldb_next_request(ac->module, req);
+
+       } else {
+               talloc_free(ares);
+               return LDB_SUCCESS;
+       }
+       
+       
+}
 /* rename */
 static int linked_attributes_rename(struct ldb_module *module, struct ldb_request *req)
 {
        /* Look up list of linked attributes */
-       /* Search to see if any linked attributes are in this entry */
-       return ldb_next_request(module, req);
+       const char **attrs;
+       WERROR werr;
+       int ret;
+       struct linked_attributes_context *ac;
+       struct ldb_request *new_req;
+       const struct dsdb_schema *schema = dsdb_get_schema(module->ldb);
+       if (!schema) {
+               /* without schema, this doesn't make any sense */
+               return ldb_next_request(module, req);
+       }
+
+       /* This gets complex:  We need to:
+          - Do a search for the entry 
+          - Wait for these result to appear
+          - In the callback for the result, issue a modify request based on the linked attributes found
+          - Wait for each modify result
+          - Regain our sainity 
+       */
+
+       ac = linked_attributes_init_handle(req, module);
+       if (!ac) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       
+       werr = dsdb_linked_attribute_lDAPDisplayName_list(schema, ac, &attrs);
+       if (!W_ERROR_IS_OK(werr)) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       
+       ret = ldb_build_search_req(&new_req, module->ldb, req,
+                                  req->op.rename.olddn, 
+                                  LDB_SCOPE_BASE,
+                                  "(objectClass=*)",
+                                  attrs,
+                                  NULL, 
+                                  ac, 
+                                  linked_attributes_rename_del_search_callback);
+
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
+
+       talloc_steal(new_req, attrs);
+
+       ac->down_req = talloc_realloc(ac, ac->down_req, 
+                                       struct ldb_request *, ac->num_requests + 1);
+       if (!ac->down_req) {
+               ldb_oom(ac->module->ldb);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       ac->down_req[ac->num_requests] = new_req;
+       if (req == NULL) {
+               ldb_oom(ac->module->ldb);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       ac->num_requests++;
+       return ldb_next_request(module, new_req);
 }
 
+/* delete */
+static int linked_attributes_delete(struct ldb_module *module, struct ldb_request *req)
+{
+       /* Look up list of linked attributes */
+       const char **attrs;
+       WERROR werr;
+       int ret;
+       struct ldb_request *new_req;
+       struct linked_attributes_context *ac;
+       const struct dsdb_schema *schema = dsdb_get_schema(module->ldb);
+       if (!schema) {
+               /* without schema, this doesn't make any sense */
+               return ldb_next_request(module, req);
+       }
+
+       /* This gets complex:  We need to:
+          - Do a search for the entry 
+          - Wait for these result to appear
+          - In the callback for the result, issue a modify request based on the linked attributes found
+          - Wait for each modify result
+          - Regain our sainity 
+       */
+
+       ac = linked_attributes_init_handle(req, module);
+       if (!ac) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       
+       werr = dsdb_linked_attribute_lDAPDisplayName_list(schema, ac, &attrs);
+       if (!W_ERROR_IS_OK(werr)) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       };
+       
+       ret = ldb_build_search_req(&new_req, module->ldb, req,
+                                  req->op.del.dn, 
+                                  LDB_SCOPE_BASE,
+                                  "(objectClass=*)",
+                                  attrs,
+                                  NULL, 
+                                  ac, 
+                                  linked_attributes_rename_del_search_callback);
+
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
+
+       talloc_steal(new_req, attrs);
+
+       ac->down_req = talloc_realloc(ac, ac->down_req, 
+                                       struct ldb_request *, ac->num_requests + 1);
+       if (!ac->down_req) {
+               ldb_oom(ac->module->ldb);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       ac->down_req[ac->num_requests] = new_req;
+       if (req == NULL) {
+               ldb_oom(ac->module->ldb);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       ac->num_requests++;
+       return ldb_next_request(module, new_req);
+}
+
+
 static int linked_attributes_wait_none(struct ldb_handle *handle) {
        struct linked_attributes_context *ac;
        int i, ret = LDB_ERR_OPERATIONS_ERROR;
index b5c73e4f65a7450400a3f3b86b0ba81e5ec1ddc6..c18e29378037ab29d9e856169feb8713ce58646d 100755 (executable)
@@ -55,6 +55,30 @@ cN: LDAPtestUSER
                }
        }
 
+       ldb.del("cn=ldaptestgroup,cn=users," + base_dn);
+
+       var ok = ldb.add("
+dn: cn=ldaptestgroup,cn=uSers," + base_dn + "
+objectclass: group
+member: cn=ldaptestuser,cn=useRs," + base_dn + "
+");
+       if (ok.error != 0) {
+               ok = ldb.del("cn=ldaptestgroup,cn=users," + base_dn);
+               if (ok.error != 0) {
+                       println(ok.errstr);
+                       assert(ok.error == 0);
+               }
+               ok = ldb.add("
+dn: cn=ldaptestgroup,cn=uSers," + base_dn + "
+objectclass: group
+member: cn=ldaptestuser,cn=useRs," + base_dn + "
+");
+               if (ok.error != 0) {
+                       println(ok.errstr);
+                       assert(ok.error == 0);
+               }
+       }
+
        var ok = ldb.add("
 dn: cn=ldaptestcomputer,cn=computers," + base_dn + "
 objectclass: computer
@@ -77,6 +101,11 @@ cn: LDAPtestCOMPUTER
                }
        }
 
+       if (ok.error != 0) {
+               println(ok.errstr);
+               assert(ok.error == 0);
+       }
+
        var ok = ldb.add("
 dn: cn=ldaptest2computer,cn=computers," + base_dn + "
 objectClass: computer
@@ -140,6 +169,20 @@ cn: LDAPtestUSER2
                }
        }
 
+       println("Testing Group Modifies");
+       ok = ldb.modify("
+dn: cn=ldaptestgroup,cn=users," + base_dn + "
+changetype: modify
+add: member
+member: cn=ldaptestuser2,cn=users," + base_dn + "
+member: cn=ldaptestcomputer,cn=computers," + base_dn + "
+");
+
+       if (ok.error != 0) {
+               println(ok.errstr);
+               assert(ok.error == 0);
+       }
+
        ok = ldb.del("cn=ldaptestuser3,cn=users," + base_dn);
 
        println("Testing Renames");
@@ -230,6 +273,14 @@ cn: LDAPtestUSER3
 
        ok = ldb.del("cn=ldaptestuser5,cn=users," + base_dn);
 
+       ok = ldb.del("cn=ldaptestgroup2,cn=users," + base_dn);
+
+       ok = ldb.rename("cn=ldaptestgroup,cn=users," + base_dn, "cn=ldaptestgroup2,cn=users," + base_dn);
+       if (ok.error != 0) {
+               println(ok.errstr);
+               assert(ok.error == 0);
+       }
+
        println("Testing subtree Renames");
 
        ok = ldb.add("
@@ -562,7 +613,13 @@ objectClass: user
 //     assert(res.msgs[0].userAccountControl == 4098);
 
 
-        var attrs = new Array("cn", "name", "objectClass", "objectGUID", "whenCreated", "nTSecurityDescriptor");
+       ok = ldb.del(res.msgs[0].dn);
+       if (ok.error != 0) {
+               println(ok.errstr);
+               assert(ok.error == 0);
+       }
+
+        var attrs = new Array("cn", "name", "objectClass", "objectGUID", "whenCreated", "nTSecurityDescriptor", "memberOf");
        println("Testing ldb.search for (&(cn=ldaptestUSer2)(objectClass=user))");
        var res = ldb.search("(&(cn=ldaptestUSer2)(objectClass=user))", base_dn, ldb.SCOPE_SUBTREE, attrs);
        if (res.error != 0 || res.msgs.length != 1) {
@@ -581,7 +638,7 @@ objectClass: user
        assert(res.msgs[0].objectGUID != undefined);
        assert(res.msgs[0].whenCreated != undefined);
        assert(res.msgs[0].nTSecurityDescriptor != undefined);
-
+       assert(res.msgs[0].memberOf[0] == ("CN=ldaptestgroup2,CN=Users," + base_dn));
 
        ok = ldb.del(res.msgs[0].dn);
        if (ok.error != 0) {
@@ -614,6 +671,12 @@ objectClass: user
                assert(ok.error == 0);
        }
 
+       ok = ldb.del(("CN=ldaptestgroup2,CN=Users," + base_dn))
+       if (ok.error != 0) {
+               println(ok.errstr);
+               assert(ok.error == 0);
+       }
+
        println("Testing ldb.search for (&(cn=ldaptestutf8user2 ÈÙÉÌÒÀ)(objectClass=user))");
        var res = ldb.search("(&(cn=ldaptestutf8user ÈÙÉÌÒÀ)(objectClass=user))");