r25747: Implement linked attributes, for add operations.
[ira/wip.git] / source4 / dsdb / samdb / ldb_modules / linked_attributes.c
1 /* 
2    ldb database library
3
4    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2007
5
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.
10    
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.
15    
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/>.
18 */
19
20 /*
21  *  Name: ldb
22  *
23  *  Component: ldb linked_attributes module
24  *
25  *  Description: Module to ensure linked attribute pairs remain in sync
26  *
27  *  Author: Andrew Bartlett
28  */
29
30 #include "includes.h"
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"
35
36 struct linked_attributes_context {
37         struct ldb_module *module;
38         struct ldb_handle *handle;
39         struct ldb_request *orig_req;
40
41         struct ldb_request **down_req;
42         int num_requests;
43         int finished_requests;
44 };
45
46 static struct linked_attributes_context *linked_attributes_init_handle(struct ldb_request *req, 
47                                                                  struct ldb_module *module)
48 {
49         struct linked_attributes_context *ac;
50         struct ldb_handle *h;
51
52         h = talloc_zero(req, struct ldb_handle);
53         if (h == NULL) {
54                 ldb_set_errstring(module->ldb, "Out of Memory");
55                 return NULL;
56         }
57
58         h->module = module;
59
60         ac = talloc_zero(h, struct linked_attributes_context);
61         if (ac == NULL) {
62                 ldb_set_errstring(module->ldb, "Out of Memory");
63                 talloc_free(h);
64                 return NULL;
65         }
66
67         h->private_data = ac;
68
69         ac->module = module;
70         ac->handle = h;
71         ac->orig_req = req;
72
73         req->handle = h;
74
75         return ac;
76 }
77
78 /* add */
79 static int linked_attributes_add(struct ldb_module *module, struct ldb_request *req)
80 {
81         int i, j, ret;
82         struct linked_attributes_context *ac;
83
84         const struct dsdb_schema *schema = dsdb_get_schema(module->ldb);
85         if (!schema) {
86                 /* without schema, this doesn't make any sense */
87                 return ldb_next_request(module, req);
88         }
89
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);
93         }
94
95
96         ac = linked_attributes_init_handle(req, module);
97         if (!ac) {
98                 return LDB_ERR_OPERATIONS_ERROR;
99         }
100         
101         /* prepare the first operation */
102         ac->down_req = talloc_realloc(ac, ac->down_req, 
103                                       struct ldb_request *, 1);
104         if (!ac->down_req) {
105                 ldb_oom(ac->module->ldb);
106                 return LDB_ERR_OPERATIONS_ERROR;
107         }
108         
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;
113         }
114         *(ac->down_req[0]) = *req; /* copy the request */
115         
116         ac->num_requests++;
117         
118         /* Run the original request */
119         ret = ldb_next_request(module, req);
120         if (ret != LDB_SUCCESS) {
121                 return ret;
122         }
123
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);
129                 if (!schema_attr) {
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;                  
133                 }
134                 /* We have a valid attribute, not find out if it is linked */
135                 if (schema_attr->linkID == 0) {
136                         continue;
137                 }
138                 
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;
144                 }
145                 
146                 /* Even link IDs are for the originating attribute */
147                 
148                 /* Now find the target attribute */
149                 target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID + 1);
150                 if (!target_attr) {
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;                  
154                 }
155
156                 /* Prepare the modify (add element) on the targets */
157
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);
163                         if (!new_msg) {
164                                 ldb_oom(module->ldb);
165                                 return LDB_ERR_OPERATIONS_ERROR;
166                         }
167                         new_msg->dn = ldb_dn_new(new_msg, module->ldb, (char *)el->values[j].data);
168                         if (!new_msg->dn) {
169                                 ldb_asprintf_errstring(module->ldb, 
170                                                "attribute %s value %s was not a valid DN", req->op.add.message->elements[i].name,
171                                                        el->values[j].data);
172                                 return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
173                         }
174
175                         ret = ldb_msg_add_empty(new_msg, target_attr->lDAPDisplayName, 
176                                                 LDB_FLAG_MOD_ADD, NULL);
177                         if (ret != LDB_SUCCESS) {
178                                 return ret;
179                         }
180                         
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) {
184                                 return ret;
185                         }
186
187                         ret = ldb_build_mod_req(&new_req, module->ldb, ac->down_req,
188                                                 new_msg,
189                                                 NULL,
190                                                 NULL,
191                                                 NULL);
192                         if (ret != LDB_SUCCESS) {
193                                 return ret;
194                         }
195                         
196                         talloc_steal(new_req, new_msg);
197                         
198                         ldb_set_timeout_from_prev_req(module->ldb, req, new_req);
199                         
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);
203                         if (!ac->down_req) {
204                                 ldb_oom(ac->module->ldb);
205                                 return LDB_ERR_OPERATIONS_ERROR;
206                         }
207                         ac->down_req[ac->num_requests] = new_req;
208                         ac->num_requests++;
209
210                         /* Run the new request */
211                         ret = ldb_next_request(module, new_req);
212                         if (ret != LDB_SUCCESS) {
213                                 return ret;
214                         }
215                 }
216         }
217         return ret;
218 }
219
220 /* modify */
221 static int linked_attributes_modify(struct ldb_module *module, struct ldb_request *req)
222 {
223         return ldb_next_request(module, req);
224 }
225
226 /* delete */
227 static int linked_attributes_delete(struct ldb_module *module, struct ldb_request *req)
228 {
229         return ldb_next_request(module, req);
230 }
231
232 /* rename */
233 static int linked_attributes_rename(struct ldb_module *module, struct ldb_request *req)
234 {
235         return ldb_next_request(module, req);
236 }
237
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;
243         }
244
245         if (handle->state == LDB_ASYNC_DONE) {
246                 return handle->status;
247         }
248
249         handle->state = LDB_ASYNC_PENDING;
250         handle->status = LDB_SUCCESS;
251
252         ac = talloc_get_type(handle->private_data, struct linked_attributes_context);
253
254         for (i=0; i < ac->num_requests; i++) {
255                 ret = ldb_wait(ac->down_req[i]->handle, LDB_WAIT_NONE);
256                 
257                 if (ret != LDB_SUCCESS) {
258                         handle->status = ret;
259                         goto done;
260                 }
261                 if (ac->down_req[i]->handle->status != LDB_SUCCESS) {
262                         handle->status = ac->down_req[i]->handle->status;
263                         goto done;
264                 }
265                 
266                 if (ac->down_req[i]->handle->state != LDB_ASYNC_DONE) {
267                         return LDB_SUCCESS;
268                 }
269         }
270
271 done:
272         handle->state = LDB_ASYNC_DONE;
273         return ret;
274
275 }
276
277 static int linked_attributes_wait_all(struct ldb_handle *handle) {
278
279         int ret;
280
281         while (handle->state != LDB_ASYNC_DONE) {
282                 ret = linked_attributes_wait_none(handle);
283                 if (ret != LDB_SUCCESS) {
284                         return ret;
285                 }
286         }
287
288         return handle->status;
289 }
290
291 static int linked_attributes_wait(struct ldb_handle *handle, enum ldb_wait_type type)
292 {
293         if (type == LDB_WAIT_ALL) {
294                 return linked_attributes_wait_all(handle);
295         } else {
296                 return linked_attributes_wait_none(handle);
297         }
298 }
299
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,
307 };
308
309 int ldb_linked_attributes_init(void)
310 {
311         return ldb_register_module(&linked_attributes_ops);
312 }