r25747: Implement linked attributes, for add operations.
authorAndrew Bartlett <abartlet@samba.org>
Mon, 29 Oct 2007 09:54:06 +0000 (10:54 +0100)
committerStefan Metzmacher <metze@samba.org>
Fri, 21 Dec 2007 04:43:41 +0000 (05:43 +0100)
Much more work is still required here, particularly to handle this
better during the provision, and to handle modifies and deletes, but
this is a start.

Andrew Bartlett
(This used to be commit 2ba99d58e9fe1f8e4b15a58a2fdfce6e876f99b4)

source4/dsdb/samdb/ldb_modules/config.mk
source4/dsdb/samdb/ldb_modules/linked_attributes.c [new file with mode: 0644]
source4/scripting/libjs/provision.js
source4/setup/provision_users.ldif

index f35f26371b0732429abb816252aa86dda07ec2cd..a93dea7db75e4b336e90f932c6375c1d25842eb2 100644 (file)
@@ -244,3 +244,15 @@ OBJ_FILES = \
 # End MODULE ldb_subtree_rename
 ################################################
 
+################################################
+# Start MODULE ldb_linked_attributes
+[MODULE::ldb_linked_attributes]
+INIT_FUNCTION = ldb_linked_attributes_init
+CFLAGS = -Ilib/ldb/include
+PRIVATE_DEPENDENCIES = LIBTALLOC SAMDB 
+SUBSYSTEM = LIBLDB
+OBJ_FILES = \
+               linked_attributes.o
+# End MODULE ldb_linked_attributes
+################################################
+
diff --git a/source4/dsdb/samdb/ldb_modules/linked_attributes.c b/source4/dsdb/samdb/ldb_modules/linked_attributes.c
new file mode 100644 (file)
index 0000000..f386795
--- /dev/null
@@ -0,0 +1,312 @@
+/* 
+   ldb database library
+
+   Copyright (C) Andrew Bartlett <abartlet@samba.org> 2007
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ *  Name: ldb
+ *
+ *  Component: ldb linked_attributes module
+ *
+ *  Description: Module to ensure linked attribute pairs remain in sync
+ *
+ *  Author: Andrew Bartlett
+ */
+
+#include "includes.h"
+#include "ldb/include/ldb.h"
+#include "ldb/include/ldb_errors.h"
+#include "ldb/include/ldb_private.h"
+#include "dsdb/samdb/samdb.h"
+
+struct linked_attributes_context {
+       struct ldb_module *module;
+       struct ldb_handle *handle;
+       struct ldb_request *orig_req;
+
+       struct ldb_request **down_req;
+       int num_requests;
+       int finished_requests;
+};
+
+static struct linked_attributes_context *linked_attributes_init_handle(struct ldb_request *req, 
+                                                                struct ldb_module *module)
+{
+       struct linked_attributes_context *ac;
+       struct ldb_handle *h;
+
+       h = talloc_zero(req, struct ldb_handle);
+       if (h == NULL) {
+               ldb_set_errstring(module->ldb, "Out of Memory");
+               return NULL;
+       }
+
+       h->module = module;
+
+       ac = talloc_zero(h, struct linked_attributes_context);
+       if (ac == NULL) {
+               ldb_set_errstring(module->ldb, "Out of Memory");
+               talloc_free(h);
+               return NULL;
+       }
+
+       h->private_data = ac;
+
+       ac->module = module;
+       ac->handle = h;
+       ac->orig_req = req;
+
+       req->handle = h;
+
+       return ac;
+}
+
+/* add */
+static int linked_attributes_add(struct ldb_module *module, struct ldb_request *req)
+{
+       int i, j, ret;
+       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);
+       }
+
+       if (ldb_dn_is_special(req->op.mod.message->dn)) {
+               /* do not manipulate our control entries */
+               return ldb_next_request(module, req);
+       }
+
+
+       ac = linked_attributes_init_handle(req, module);
+       if (!ac) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       
+       /* prepare the first operation */
+       ac->down_req = talloc_realloc(ac, ac->down_req, 
+                                     struct ldb_request *, 1);
+       if (!ac->down_req) {
+               ldb_oom(ac->module->ldb);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       
+       ac->down_req[0] = talloc(ac->down_req, struct ldb_request);
+       if (!ac->down_req[0]) {
+               ldb_oom(ac->module->ldb);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       *(ac->down_req[0]) = *req; /* copy the request */
+       
+       ac->num_requests++;
+       
+       /* Run the original request */
+       ret = ldb_next_request(module, req);
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
+
+       for (i=0; i < req->op.add.message->num_elements; i++) {
+               const struct dsdb_attribute *target_attr;
+               const struct ldb_message_element *el = &req->op.add.message->elements[i];
+               const struct dsdb_attribute *schema_attr
+                       = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
+               if (!schema_attr) {
+                       ldb_asprintf_errstring(module->ldb, 
+                                              "attribute %s is not a valid attribute in schema", req->op.add.message->elements[i].name);
+                       return LDB_ERR_OBJECT_CLASS_VIOLATION;                  
+               }
+               /* We have a valid attribute, not find out if it is linked */
+               if (schema_attr->linkID == 0) {
+                       continue;
+               }
+               
+               if ((schema_attr->linkID & 1) == 1) {
+                       /* Odd is for the target.  Illigal to modify */
+                       ldb_asprintf_errstring(module->ldb, 
+                                              "attribute %s must not be modified directly, it is a linked attribute", req->op.add.message->elements[i].name);
+                       return LDB_ERR_UNWILLING_TO_PERFORM;
+               }
+               
+               /* Even link IDs are for the originating attribute */
+               
+               /* Now find the target attribute */
+               target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID + 1);
+               if (!target_attr) {
+                       ldb_asprintf_errstring(module->ldb, 
+                                              "attribute %s does not have valid link target", req->op.add.message->elements[i].name);
+                       return LDB_ERR_OBJECT_CLASS_VIOLATION;                  
+               }
+
+               /* Prepare the modify (add element) on the targets */
+
+               /* For each value being added, we need to setup the modify */
+               for (j=0; j < el->num_values; j++) {
+                       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(module->ldb);
+                               return LDB_ERR_OPERATIONS_ERROR;
+                       }
+                       new_msg->dn = ldb_dn_new(new_msg, module->ldb, (char *)el->values[j].data);
+                       if (!new_msg->dn) {
+                               ldb_asprintf_errstring(module->ldb, 
+                                              "attribute %s value %s was not a valid DN", req->op.add.message->elements[i].name,
+                                                      el->values[j].data);
+                               return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
+                       }
+
+                       ret = ldb_msg_add_empty(new_msg, target_attr->lDAPDisplayName, 
+                                               LDB_FLAG_MOD_ADD, NULL);
+                       if (ret != LDB_SUCCESS) {
+                               return ret;
+                       }
+                       
+                       ret = ldb_msg_add_string(new_msg, target_attr->lDAPDisplayName, 
+                                                ldb_dn_get_linearized(ac->orig_req->op.add.message->dn));
+                       if (ret != LDB_SUCCESS) {
+                               return ret;
+                       }
+
+                       ret = ldb_build_mod_req(&new_req, module->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(module->ldb, 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(ac->module->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(module, new_req);
+                       if (ret != LDB_SUCCESS) {
+                               return ret;
+                       }
+               }
+       }
+       return ret;
+}
+
+/* modify */
+static int linked_attributes_modify(struct ldb_module *module, struct ldb_request *req)
+{
+       return ldb_next_request(module, req);
+}
+
+/* delete */
+static int linked_attributes_delete(struct ldb_module *module, struct ldb_request *req)
+{
+       return ldb_next_request(module, req);
+}
+
+/* rename */
+static int linked_attributes_rename(struct ldb_module *module, struct ldb_request *req)
+{
+       return ldb_next_request(module, req);
+}
+
+static int linked_attributes_wait_none(struct ldb_handle *handle) {
+       struct linked_attributes_context *ac;
+       int i, ret = LDB_ERR_OPERATIONS_ERROR;
+       if (!handle || !handle->private_data) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       if (handle->state == LDB_ASYNC_DONE) {
+               return handle->status;
+       }
+
+       handle->state = LDB_ASYNC_PENDING;
+       handle->status = LDB_SUCCESS;
+
+       ac = talloc_get_type(handle->private_data, struct linked_attributes_context);
+
+       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->down_req[i]->handle->status != LDB_SUCCESS) {
+                       handle->status = ac->down_req[i]->handle->status;
+                       goto done;
+               }
+               
+               if (ac->down_req[i]->handle->state != LDB_ASYNC_DONE) {
+                       return LDB_SUCCESS;
+               }
+       }
+
+done:
+       handle->state = LDB_ASYNC_DONE;
+       return ret;
+
+}
+
+static int linked_attributes_wait_all(struct ldb_handle *handle) {
+
+       int ret;
+
+       while (handle->state != LDB_ASYNC_DONE) {
+               ret = linked_attributes_wait_none(handle);
+               if (ret != LDB_SUCCESS) {
+                       return ret;
+               }
+       }
+
+       return handle->status;
+}
+
+static int linked_attributes_wait(struct ldb_handle *handle, enum ldb_wait_type type)
+{
+       if (type == LDB_WAIT_ALL) {
+               return linked_attributes_wait_all(handle);
+       } else {
+               return linked_attributes_wait_none(handle);
+       }
+}
+
+static const struct ldb_module_ops linked_attributes_ops = {
+       .name              = "linked_attributes",
+       .add               = linked_attributes_add,
+       .modify            = linked_attributes_modify,
+       .del               = linked_attributes_delete,
+       .rename            = linked_attributes_rename,
+       .wait              = linked_attributes_wait,
+};
+
+int ldb_linked_attributes_init(void)
+{
+       return ldb_register_module(&linked_attributes_ops);
+}
index e5a91b40b502c303d40cc5408aa99005feb0957f..ef43ed721d46ec1ee8584c648bf4bb03d6e91af1 100644 (file)
@@ -966,6 +966,7 @@ function provision_guess()
                                        "objectclass",
                                        "rdn_name",
                                        "subtree_rename",
+                                       "linked_attributes",
                                        "show_deleted",
                                        "partition");
        subobj.MODULES_LIST = join(",", modules_list);
index 030fe5d7425ad2a0bd6b7a72cf5d455e8e64eba1..95c28f92d845f683a4f5d65a18c01edccb38cf6d 100644 (file)
@@ -2,11 +2,6 @@ dn: CN=Administrator,CN=Users,${DOMAINDN}
 objectClass: user
 cn: Administrator
 description: Built-in account for administering the computer/domain
-memberOf: CN=Group Policy Creator Owners,CN=Users,${DOMAINDN}
-memberOf: CN=Domain Admins,CN=Users,${DOMAINDN}
-memberOf: CN=Enterprise Admins,CN=Users,${DOMAINDN}
-memberOf: CN=Schema Admins,CN=Users,${DOMAINDN}
-memberOf: CN=Administrators,CN=Builtin,${DOMAINDN}
 userAccountControl: 66048
 objectSid: ${DOMAINSID}-500
 adminCount: 1
@@ -19,7 +14,6 @@ dn: CN=Guest,CN=Users,${DOMAINDN}
 objectClass: user
 cn: Guest
 description: Built-in account for guest access to the computer/domain
-memberOf: CN=Guests,CN=Builtin,${DOMAINDN}
 userAccountControl: 66082
 primaryGroupID: 514
 objectSid: ${DOMAINSID}-501
@@ -241,7 +235,6 @@ objectClass: group
 cn: Enterprise Admins
 description: Designated administrators of the enterprise
 member: CN=Administrator,CN=Users,${DOMAINDN}
-memberOf: CN=Administrators,CN=Builtin,${DOMAINDN}
 objectSid: ${DOMAINSID}-519
 adminCount: 1
 sAMAccountName: Enterprise Admins
@@ -264,7 +257,6 @@ objectClass: group
 cn: Domain Admins
 description: Designated administrators of the domain
 member: CN=Administrator,CN=Users,${DOMAINDN}
-memberOf: CN=Administrators,CN=Builtin,${DOMAINDN}
 objectSid: ${DOMAINSID}-512
 adminCount: 1
 sAMAccountName: Domain Admins
@@ -275,7 +267,6 @@ objectClass: top
 objectClass: group
 cn: Domain Users
 description: All domain users
-memberOf: CN=Users,CN=Builtin,${DOMAINDN}
 objectSid: ${DOMAINSID}-513
 sAMAccountName: Domain Users
 isCriticalSystemObject: TRUE
@@ -285,7 +276,6 @@ objectClass: top
 objectClass: group
 cn: Domain Guests
 description: All domain guests
-memberOf: CN=Guests,CN=Builtin,${DOMAINDN}
 objectSid: ${DOMAINSID}-514
 sAMAccountName: Domain Guests
 isCriticalSystemObject: TRUE