summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source4/dsdb/samdb/ldb_modules/linked_attributes.c317
-rwxr-xr-xtestprogs/ejs/ldap.js67
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))");