summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source4/dsdb/samdb/ldb_modules/partition.c240
-rwxr-xr-xtestprogs/ejs/ldb.js65
2 files changed, 246 insertions, 59 deletions
diff --git a/source4/dsdb/samdb/ldb_modules/partition.c b/source4/dsdb/samdb/ldb_modules/partition.c
index 92fddca270..a21fabb747 100644
--- a/source4/dsdb/samdb/ldb_modules/partition.c
+++ b/source4/dsdb/samdb/ldb_modules/partition.c
@@ -42,15 +42,16 @@ struct partition {
};
struct partition_private_data {
struct partition **partitions;
+ struct ldb_dn **replicate;
};
struct partition_context {
struct ldb_module *module;
struct ldb_request *orig_req;
- struct ldb_request **search_req;
- BOOL *finished_search;
- int num_searches;
+ struct ldb_request **down_req;
+ int num_requests;
+ int finished_requests;
};
static struct ldb_handle *partition_init_handle(struct ldb_request *req, struct ldb_module *module)
@@ -118,35 +119,163 @@ struct ldb_module *find_backend(struct ldb_module *module, struct ldb_request *r
return module;
};
-static int partition_send_search(struct partition_context *ac, struct ldb_module *partition)
+
+/*
+ fire the caller's callback for every entry, but only send 'done' once.
+*/
+static int partition_search_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
+{
+ struct partition_context *ac;
+
+ if (!context || !ares) {
+ ldb_set_errstring(ldb, talloc_asprintf(ldb, "NULL Context or Result in callback"));
+ goto error;
+ }
+
+ ac = talloc_get_type(context, struct partition_context);
+
+ if (ares->type == LDB_REPLY_ENTRY) {
+ return ac->orig_req->callback(ldb, ac->orig_req->context, ares);
+ } else {
+ ac->finished_requests++;
+ if (ac->finished_requests == ac->num_requests) {
+ return ac->orig_req->callback(ldb, ac->orig_req->context, ares);
+ } else {
+ talloc_free(ares);
+ return LDB_SUCCESS;
+ }
+ }
+error:
+ talloc_free(ares);
+ return LDB_ERR_OPERATIONS_ERROR;
+}
+
+/*
+ only fire the 'last' callback, and only for START-TLS for now
+*/
+static int partition_other_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
+{
+ struct partition_context *ac;
+
+ if (!context || !ares) {
+ ldb_set_errstring(ldb, talloc_asprintf(ldb, "NULL Context or Result in callback"));
+ goto error;
+ }
+
+ ac = talloc_get_type(context, struct partition_context);
+
+ if (ares->type == LDB_REPLY_EXTENDED && strcmp(ares->response->oid, LDB_EXTENDED_START_TLS_OID)) {
+ ac->finished_requests++;
+ if (ac->finished_requests == ac->num_requests) {
+ return ac->orig_req->callback(ldb, ac->orig_req->context, ares);
+ }
+ talloc_free(ares);
+ return LDB_SUCCESS;
+ }
+ ldb_set_errstring(ldb, talloc_asprintf(ldb, "partition_other_callback: Unknown reply type, only supports START_TLS"));
+error:
+ talloc_free(ares);
+ return LDB_ERR_OPERATIONS_ERROR;
+}
+
+
+static int partition_send_request(struct partition_context *ac, struct ldb_module *partition)
{
int ret;
struct ldb_module *next = make_module_for_next_request(ac->module, ac->module->ldb, partition);
- ac->search_req = talloc_realloc(ac, ac->search_req,
- struct ldb_request *, ac->num_searches + 1);
- if (!ac->search_req) {
+ ac->down_req = talloc_realloc(ac, ac->down_req,
+ struct ldb_request *, ac->num_requests + 1);
+ if (!ac->down_req) {
ldb_set_errstring(ac->module->ldb, talloc_asprintf(ac->module->ldb, "Out of memory!"));
return LDB_ERR_OPERATIONS_ERROR;
}
- ac->search_req[ac->num_searches] = talloc(ac, struct ldb_request);
- if (ac->search_req[ac->num_searches] == NULL) {
+ ac->down_req[ac->num_requests] = talloc(ac, struct ldb_request);
+ if (ac->down_req[ac->num_requests] == NULL) {
ldb_set_errstring(ac->module->ldb, talloc_asprintf(ac->module->ldb, "Out of memory!"));
return LDB_ERR_OPERATIONS_ERROR;
}
- *ac->search_req[ac->num_searches] = *ac->orig_req; /* copy the request */
+ *ac->down_req[ac->num_requests] = *ac->orig_req; /* copy the request */
+ if (ac->down_req[ac->num_requests]->operation == LDB_SEARCH) {
+ ac->down_req[ac->num_requests]->callback = partition_search_callback;
+ ac->down_req[ac->num_requests]->context = ac;
+ } else {
+ ac->down_req[ac->num_requests]->callback = partition_other_callback;
+ ac->down_req[ac->num_requests]->context = ac;
+ }
+
/* Spray off search requests to all backends */
- ret = ldb_next_request(next, ac->search_req[ac->num_searches]);
+ ret = ldb_next_request(next, ac->down_req[ac->num_requests]);
if (ret != LDB_SUCCESS) {
return ret;
}
- ac->num_searches++;
+ ac->num_requests++;
return LDB_SUCCESS;
}
+/* Send a request down to all the partitions */
+static int partition_send_all(struct ldb_module *module,
+ struct partition_context *ac, struct ldb_request *req)
+{
+ int i;
+ struct partition_private_data *data = talloc_get_type(module->private_data,
+ struct partition_private_data);
+ int ret = partition_send_request(ac, module->next);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ for (i=0; data && data->partitions && data->partitions[i]; i++) {
+ ret = partition_send_request(ac, data->partitions[i]->module);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+ return LDB_SUCCESS;
+}
+
+/* Figure out which backend a request needs to be aimed at. Some
+ * requests must be replicated to all backends */
+static int partition_replicate(struct ldb_module *module, struct ldb_request *req, const struct ldb_dn *dn)
+{
+ int i;
+ struct ldb_module *backend;
+ struct partition_private_data *data = talloc_get_type(module->private_data,
+ struct partition_private_data);
+
+ /* Is this a special DN, we need to replicate to every backend? */
+ for (i=0; data->replicate && data->replicate[i]; i++) {
+ if (ldb_dn_compare(module->ldb,
+ data->replicate[i],
+ dn) == 0) {
+ struct ldb_handle *h;
+ struct partition_context *ac;
+
+ h = partition_init_handle(req, module);
+ if (!h) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ /* return our own handle to deal with this call */
+ req->handle = h;
+
+ ac = talloc_get_type(h->private_data, struct partition_context);
+
+ return partition_send_all(module, ac, req);
+ }
+ }
+
+ /* Otherwise, we need to find the backend to fire it to */
+
+ /* Find backend */
+ backend = find_backend(module, req, req->op.add.message->dn);
+
+ /* issue request */
+ return ldb_next_request(backend, req);
+
+}
+
/* search */
static int partition_search(struct ldb_module *module, struct ldb_request *req)
{
@@ -171,15 +300,12 @@ static int partition_search(struct ldb_module *module, struct ldb_request *req)
ac = talloc_get_type(h->private_data, struct partition_context);
- ac->orig_req = req;
- ac->num_searches = 0;
-
for (i=0; data && data->partitions && data->partitions[i]; i++) {
/* Find all partitions under the search base */
if (ldb_dn_compare_base(module->ldb,
req->op.search.base,
data->partitions[i]->dn) == 0) {
- ret = partition_send_search(ac, data->partitions[i]->module);
+ ret = partition_send_request(ac, data->partitions[i]->module);
if (ret != LDB_SUCCESS) {
return ret;
}
@@ -187,23 +313,10 @@ static int partition_search(struct ldb_module *module, struct ldb_request *req)
}
/* Perhaps we didn't match any partitions. Try the main partition, then all partitions */
- if (ac->num_searches == 0) {
- ret = partition_send_search(ac, module->next);
- if (ret != LDB_SUCCESS) {
- return ret;
- }
- for (i=0; data && data->partitions && data->partitions[i]; i++) {
- ret = partition_send_search(ac, data->partitions[i]->module);
- if (ret != LDB_SUCCESS) {
- return ret;
- }
- }
+ if (ac->num_requests == 0) {
+ return partition_send_all(module, ac, req);
}
- ac->finished_search = talloc_zero_array(ac, BOOL, ac->num_searches);
- if (!ac->finished_search) {
- return LDB_ERR_OPERATIONS_ERROR;
- }
return LDB_SUCCESS;
} else {
struct ldb_module *backend = find_backend(module, req, req->op.search.base);
@@ -215,34 +328,19 @@ static int partition_search(struct ldb_module *module, struct ldb_request *req)
/* add */
static int partition_add(struct ldb_module *module, struct ldb_request *req)
{
- /* Find backend */
- struct ldb_module *backend = find_backend(module, req, req->op.add.message->dn);
-
- /* issue request */
-
- return ldb_next_request(backend, req);
+ return partition_replicate(module, req, req->op.add.message->dn);
}
/* modify */
static int partition_modify(struct ldb_module *module, struct ldb_request *req)
{
- /* Find backend */
- struct ldb_module *backend = find_backend(module, req, req->op.mod.message->dn);
-
- /* issue request */
-
- return ldb_next_request(backend, req);
+ return partition_replicate(module, req, req->op.mod.message->dn);
}
/* delete */
static int partition_delete(struct ldb_module *module, struct ldb_request *req)
{
- /* Find backend */
- struct ldb_module *backend = find_backend(module, req, req->op.del.dn);
-
- /* issue request */
-
- return ldb_next_request(backend, req);
+ return partition_replicate(module, req, req->op.del.dn);
}
/* rename */
@@ -256,10 +354,7 @@ static int partition_rename(struct ldb_module *module, struct ldb_request *req)
return LDB_ERR_AFFECTS_MULTIPLE_DSAS;
}
- /* issue request */
-
- /* (later) consider if we should be searching multiple partitions */
- return ldb_next_request(backend, req);
+ return partition_replicate(module, req, req->op.rename.olddn);
}
/* start a transaction */
@@ -400,10 +495,11 @@ static int partition_init(struct ldb_module *module)
{
int ret, i;
TALLOC_CTX *mem_ctx = talloc_new(module);
- static const char *attrs[] = { "partition", NULL };
+ static const char *attrs[] = { "partition", "replicateEntries", NULL };
struct ldb_result *res;
struct ldb_message *msg;
struct ldb_message_element *partition_attributes;
+ struct ldb_message_element *replicate_attributes;
struct partition_private_data *data;
@@ -511,6 +607,32 @@ static int partition_init(struct ldb_module *module)
talloc_free(req);
}
+ replicate_attributes = ldb_msg_find_element(msg, "replicateEntries");
+ if (!replicate_attributes) {
+ ldb_set_errstring(module->ldb,
+ talloc_asprintf(module, "partition_init: "
+ "no entries to replicate specified"));
+ data->replicate = NULL;
+ } else {
+ data->replicate = talloc_array(data, struct ldb_dn *, replicate_attributes->num_values + 1);
+ if (!data->replicate) {
+ talloc_free(mem_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ for (i=0; i < replicate_attributes->num_values; i++) {
+ data->replicate[i] = ldb_dn_explode(data->replicate[i], replicate_attributes->values[i].data);
+ if (!data->replicate[i]) {
+ ldb_set_errstring(module->ldb,
+ talloc_asprintf(module, "partition_init: "
+ "invalid DN in partition replicate record: %s",
+ replicate_attributes->values[i].data));
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+ }
+ data->replicate[i] = NULL;
+ }
+
module->private_data = data;
talloc_steal(module, data);
@@ -536,19 +658,19 @@ static int partition_wait_none(struct ldb_handle *handle) {
ac = talloc_get_type(handle->private_data, struct partition_context);
- for (i=0; i < ac->num_searches; i++) {
- ret = ldb_wait(ac->search_req[i]->handle, LDB_WAIT_NONE);
+ for (i=0; i < ac->num_requests; i++) {
+ ret = ldb_wait(ac->down_req[i]->handle, LDB_WAIT_NONE);
if (ret != LDB_SUCCESS) {
handle->status = ret;
goto done;
}
- if (ac->search_req[i]->handle->status != LDB_SUCCESS) {
- handle->status = ac->search_req[i]->handle->status;
+ if (ac->down_req[i]->handle->status != LDB_SUCCESS) {
+ handle->status = ac->down_req[i]->handle->status;
goto done;
}
- if (ac->search_req[i]->handle->state != LDB_ASYNC_DONE) {
+ if (ac->down_req[i]->handle->state != LDB_ASYNC_DONE) {
return LDB_SUCCESS;
}
}
diff --git a/testprogs/ejs/ldb.js b/testprogs/ejs/ldb.js
index 0d2de577d3..fb1a2d4b0d 100755
--- a/testprogs/ejs/ldb.js
+++ b/testprogs/ejs/ldb.js
@@ -101,6 +101,16 @@ partition: cn=Sub,cn=Sub,cn=PartTest:" + prefix + "testsubsub.ldb
function modules_test(ldb)
{
println("Running modules tests");
+
+ ok = ldb.add("
+dn: @ATTRIBUTES
+caseattr: CASE_INSENSITIVE
+");
+ if (!ok) {
+ println("Failed to add: " + ldb.errstring());
+ assert(ok);
+ }
+
ok = ldb.add("
dn: cn=x8,cn=PartTest
objectClass: foo
@@ -207,6 +217,57 @@ x: 11
assert(res8[0].name == "x11");
assert(res8[0].cn == "x11");
+ ok = ldb.add("
+dn: caseattr=XY,cn=PartTest
+objectClass: foo
+x: Y
+");
+ if (!ok) {
+ println("Failed to add: " + ldb.errstring());
+ assert(ok);
+ }
+
+ ok = ldb.add("
+dn: caseattr=XZ,cn=PartTest
+objectClass: foo
+x: Z
+caseattr: XZ
+");
+ if (!ok) {
+ println("Failed to add: " + ldb.errstring());
+ assert(ok);
+ }
+
+ ok = ldb.add("
+dn: caseattr2=XZ,cn=PartTest
+objectClass: foo
+x: Z
+caseattr2: XZ
+");
+ if (!ok) {
+ println("Failed to add: " + ldb.errstring());
+ assert(ok);
+ }
+
+ var resX = ldb.search("caseattr=xz", "cn=parttest", ldb.SCOPE_DEFAULT, attrs);
+ assert(resX.length == 1);
+ assert(resX[0].objectGUID != undefined);
+ assert(resX[0].createTimestamp != undefined);
+ assert(resX[0].whenCreated != undefined);
+ assert(resX[0].name == "XZ");
+
+ var rescount = ldb.search("(|(caseattr=*)(cn=*))", "cn=parttest", ldb.SCOPE_DEFAULT, attrs);
+ assert(rescount.length == 5);
+
+ /* Check this attribute is *not* case sensitive */
+ var resXcount = ldb.search("caseattr=x*", "cn=parttest", ldb.SCOPE_DEFAULT, attrs);
+ assert(resXcount.length == 2);
+
+ /* Check that this attribute *is* case sensitive */
+ var resXcount2 = ldb.search("caseattr2=xz", "cn=parttest", ldb.SCOPE_DEFAULT, attrs);
+ assert(resXcount2.length == 0);
+
+
/* Now abort the transaction to show that even with
* partitions, it is aborted everywhere */
ok = ldb.transaction_cancel();
@@ -229,6 +290,10 @@ x: 11
var res11 = ldb.search("x=10", "cn=parttest", ldb.SCOPE_DEFAULT, attrs);
assert(res11.length == 0);
+ var attrs = new Array("*");
+ var res12 = ldb.search("caseattr=*", "cn=parttest", ldb.SCOPE_DEFAULT, attrs);
+ assert(res12.length == 0);
+
}
sys = sys_init();