4 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2007
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 * Component: ldb linked_attributes module
25 * Description: Module to ensure linked attribute pairs remain in sync
27 * Author: Andrew Bartlett
31 #include "ldb/include/ldb.h"
32 #include "ldb/include/ldb_errors.h"
33 #include "ldb/include/ldb_private.h"
34 #include "dsdb/samdb/samdb.h"
36 struct linked_attributes_context {
37 struct ldb_module *module;
38 struct ldb_handle *handle;
39 struct ldb_request *orig_req;
41 struct ldb_request **down_req;
43 int finished_requests;
46 static struct linked_attributes_context *linked_attributes_init_handle(struct ldb_request *req,
47 struct ldb_module *module)
49 struct linked_attributes_context *ac;
52 h = talloc_zero(req, struct ldb_handle);
54 ldb_set_errstring(module->ldb, "Out of Memory");
60 ac = talloc_zero(h, struct linked_attributes_context);
62 ldb_set_errstring(module->ldb, "Out of Memory");
79 static int linked_attributes_add(struct ldb_module *module, struct ldb_request *req)
82 struct linked_attributes_context *ac;
84 const struct dsdb_schema *schema = dsdb_get_schema(module->ldb);
86 /* without schema, this doesn't make any sense */
87 return ldb_next_request(module, req);
90 if (ldb_dn_is_special(req->op.mod.message->dn)) {
91 /* do not manipulate our control entries */
92 return ldb_next_request(module, req);
96 ac = linked_attributes_init_handle(req, module);
98 return LDB_ERR_OPERATIONS_ERROR;
101 /* prepare the first operation */
102 ac->down_req = talloc_realloc(ac, ac->down_req,
103 struct ldb_request *, 1);
105 ldb_oom(ac->module->ldb);
106 return LDB_ERR_OPERATIONS_ERROR;
109 ac->down_req[0] = talloc(ac->down_req, struct ldb_request);
110 if (!ac->down_req[0]) {
111 ldb_oom(ac->module->ldb);
112 return LDB_ERR_OPERATIONS_ERROR;
114 *(ac->down_req[0]) = *req; /* copy the request */
118 /* Run the original request */
119 ret = ldb_next_request(module, req);
120 if (ret != LDB_SUCCESS) {
124 for (i=0; i < req->op.add.message->num_elements; i++) {
125 const struct dsdb_attribute *target_attr;
126 const struct ldb_message_element *el = &req->op.add.message->elements[i];
127 const struct dsdb_attribute *schema_attr
128 = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
130 ldb_asprintf_errstring(module->ldb,
131 "attribute %s is not a valid attribute in schema", req->op.add.message->elements[i].name);
132 return LDB_ERR_OBJECT_CLASS_VIOLATION;
134 /* We have a valid attribute, not find out if it is linked */
135 if (schema_attr->linkID == 0) {
139 if ((schema_attr->linkID & 1) == 1) {
140 /* Odd is for the target. Illigal to modify */
141 ldb_asprintf_errstring(module->ldb,
142 "attribute %s must not be modified directly, it is a linked attribute", req->op.add.message->elements[i].name);
143 return LDB_ERR_UNWILLING_TO_PERFORM;
146 /* Even link IDs are for the originating attribute */
148 /* Now find the target attribute */
149 target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID + 1);
151 ldb_asprintf_errstring(module->ldb,
152 "attribute %s does not have valid link target", req->op.add.message->elements[i].name);
153 return LDB_ERR_OBJECT_CLASS_VIOLATION;
156 /* Prepare the modify (add element) on the targets */
158 /* For each value being added, we need to setup the modify */
159 for (j=0; j < el->num_values; j++) {
160 struct ldb_request *new_req;
161 /* Create the modify request */
162 struct ldb_message *new_msg = ldb_msg_new(ac->down_req);
164 ldb_oom(module->ldb);
165 return LDB_ERR_OPERATIONS_ERROR;
167 new_msg->dn = ldb_dn_new(new_msg, module->ldb, (char *)el->values[j].data);
169 ldb_asprintf_errstring(module->ldb,
170 "attribute %s value %s was not a valid DN", req->op.add.message->elements[i].name,
172 return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
175 ret = ldb_msg_add_empty(new_msg, target_attr->lDAPDisplayName,
176 LDB_FLAG_MOD_ADD, NULL);
177 if (ret != LDB_SUCCESS) {
181 ret = ldb_msg_add_string(new_msg, target_attr->lDAPDisplayName,
182 ldb_dn_get_linearized(ac->orig_req->op.add.message->dn));
183 if (ret != LDB_SUCCESS) {
187 ret = ldb_build_mod_req(&new_req, module->ldb, ac->down_req,
192 if (ret != LDB_SUCCESS) {
196 talloc_steal(new_req, new_msg);
198 ldb_set_timeout_from_prev_req(module->ldb, req, new_req);
200 /* Now add it to the list */
201 ac->down_req = talloc_realloc(ac, ac->down_req,
202 struct ldb_request *, ac->num_requests + 1);
204 ldb_oom(ac->module->ldb);
205 return LDB_ERR_OPERATIONS_ERROR;
207 ac->down_req[ac->num_requests] = new_req;
210 /* Run the new request */
211 ret = ldb_next_request(module, new_req);
212 if (ret != LDB_SUCCESS) {
221 static int linked_attributes_modify(struct ldb_module *module, struct ldb_request *req)
223 return ldb_next_request(module, req);
227 static int linked_attributes_delete(struct ldb_module *module, struct ldb_request *req)
229 return ldb_next_request(module, req);
233 static int linked_attributes_rename(struct ldb_module *module, struct ldb_request *req)
235 return ldb_next_request(module, req);
238 static int linked_attributes_wait_none(struct ldb_handle *handle) {
239 struct linked_attributes_context *ac;
240 int i, ret = LDB_ERR_OPERATIONS_ERROR;
241 if (!handle || !handle->private_data) {
242 return LDB_ERR_OPERATIONS_ERROR;
245 if (handle->state == LDB_ASYNC_DONE) {
246 return handle->status;
249 handle->state = LDB_ASYNC_PENDING;
250 handle->status = LDB_SUCCESS;
252 ac = talloc_get_type(handle->private_data, struct linked_attributes_context);
254 for (i=0; i < ac->num_requests; i++) {
255 ret = ldb_wait(ac->down_req[i]->handle, LDB_WAIT_NONE);
257 if (ret != LDB_SUCCESS) {
258 handle->status = ret;
261 if (ac->down_req[i]->handle->status != LDB_SUCCESS) {
262 handle->status = ac->down_req[i]->handle->status;
266 if (ac->down_req[i]->handle->state != LDB_ASYNC_DONE) {
272 handle->state = LDB_ASYNC_DONE;
277 static int linked_attributes_wait_all(struct ldb_handle *handle) {
281 while (handle->state != LDB_ASYNC_DONE) {
282 ret = linked_attributes_wait_none(handle);
283 if (ret != LDB_SUCCESS) {
288 return handle->status;
291 static int linked_attributes_wait(struct ldb_handle *handle, enum ldb_wait_type type)
293 if (type == LDB_WAIT_ALL) {
294 return linked_attributes_wait_all(handle);
296 return linked_attributes_wait_none(handle);
300 static const struct ldb_module_ops linked_attributes_ops = {
301 .name = "linked_attributes",
302 .add = linked_attributes_add,
303 .modify = linked_attributes_modify,
304 .del = linked_attributes_delete,
305 .rename = linked_attributes_rename,
306 .wait = linked_attributes_wait,
309 int ldb_linked_attributes_init(void)
311 return ldb_register_module(&linked_attributes_ops);