From 8a8948a17a947bccf04df817f5011093c0c5c523 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Thu, 1 Nov 2007 12:34:06 +0100 Subject: r25781: Handle and test linked attribute renames. Andrew Bartlett (This used to be commit 56d9dd5140b6d7d7bbaa2f59ecdff7ee70c4faac) --- source4/dsdb/samdb/ldb_modules/linked_attributes.c | 317 ++++++++++++++++++++- 1 file changed, 310 insertions(+), 7 deletions(-) (limited to 'source4/dsdb/samdb') diff --git a/source4/dsdb/samdb/ldb_modules/linked_attributes.c b/source4/dsdb/samdb/ldb_modules/linked_attributes.c index d3093dbd71..7721296b7c 100644 --- a/source4/dsdb/samdb/ldb_modules/linked_attributes.c +++ b/source4/dsdb/samdb/ldb_modules/linked_attributes.c @@ -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; -- cgit