r17299: Improve the partition module to replicate attribute records into all
authorAndrew Bartlett <abartlet@samba.org>
Sat, 29 Jul 2006 01:13:53 +0000 (01:13 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 19:15:08 +0000 (14:15 -0500)
partitions.

Test that we do that correctly.

Andrew Bartlett
(This used to be commit 90c07b88010b848423dee9556a24e8d181c365dd)

source4/dsdb/samdb/ldb_modules/partition.c
testprogs/ejs/ldb.js

index 92fddca27004f3762d5b91f8348ca06c2227c995..a21fabb747192ba0bf18e8851afcd2a4411cea0e 100644 (file)
@@ -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;
                }
        }
index 0d2de577d35a55b34510c4d5a434ba42125ec1ef..fb1a2d4b0dc55d80772165e3e32949467fdf78a6 100755 (executable)
@@ -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();