diff options
-rw-r--r-- | source4/dsdb/samdb/ldb_modules/linked_attributes.c | 317 | ||||
-rwxr-xr-x | testprogs/ejs/ldap.js | 67 |
2 files changed, 375 insertions, 9 deletions
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; diff --git a/testprogs/ejs/ldap.js b/testprogs/ejs/ldap.js index b5c73e4f65..c18e293780 100755 --- a/testprogs/ejs/ldap.js +++ b/testprogs/ejs/ldap.js @@ -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))"); |