43b223b52e19f078a5adaf483a5123d8352a3e32
[ira/wip.git] / source4 / lib / ldb / modules / operational.c
1 /* 
2    ldb database library
3
4    Copyright (C) Andrew Tridgell 2005
5    Copyright (C) Simo Sorce 2006-2008
6
7      ** NOTE! The following LGPL license applies to the ldb
8      ** library. This does NOT imply that all of Samba is released
9      ** under the LGPL
10    
11    This library is free software; you can redistribute it and/or
12    modify it under the terms of the GNU Lesser General Public
13    License as published by the Free Software Foundation; either
14    version 3 of the License, or (at your option) any later version.
15
16    This library is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19    Lesser General Public License for more details.
20
21    You should have received a copy of the GNU Lesser General Public
22    License along with this library; if not, see <http://www.gnu.org/licenses/>.
23 */
24 /*
25   handle operational attributes
26  */
27
28 /*
29   createTimestamp: HIDDEN, searchable, ldaptime, alias for whenCreated
30   modifyTimestamp: HIDDEN, searchable, ldaptime, alias for whenChanged
31
32      for the above two, we do the search as normal, and if
33      createTimestamp or modifyTimestamp is asked for, then do
34      additional searches for whenCreated and whenChanged and fill in
35      the resulting values
36
37      we also need to replace these with the whenCreated/whenChanged
38      equivalent in the search expression trees
39
40   whenCreated: not-HIDDEN, CONSTRUCTED, SEARCHABLE
41   whenChanged: not-HIDDEN, CONSTRUCTED, SEARCHABLE
42
43      on init we need to setup attribute handlers for these so
44      comparisons are done correctly. The resolution is 1 second.
45
46      on add we need to add both the above, for current time
47
48      on modify we need to change whenChanged
49
50
51   subschemaSubentry: HIDDEN, not-searchable, 
52                      points at DN CN=Aggregate,$SCHEMADN
53
54      for this one we do the search as normal, then add the static
55      value if requested. How do we work out the $BASEDN from inside a
56      module?
57      
58
59   structuralObjectClass: HIDDEN, CONSTRUCTED, not-searchable. always same as objectclass?
60
61      for this one we do the search as normal, then if requested ask
62      for objectclass, change the attribute name, and add it
63
64   allowedAttributesEffective: HIDDEN, CONSTRUCTED, not-searchable, 
65      list of attributes that can be modified - requires schema lookup
66
67
68   attributeTypes: in schema only
69   objectClasses: in schema only
70   matchingRules: in schema only
71   matchingRuleUse: in schema only
72   creatorsName: not supported by w2k3?
73   modifiersName: not supported by w2k3?
74 */
75
76 #include "ldb_includes.h"
77 #include "ldb_module.h"
78
79 #ifndef ARRAY_SIZE
80 #define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
81 #endif
82
83 /*
84   construct a canonical name from a message
85 */
86 static int construct_canonical_name(struct ldb_module *module, struct ldb_message *msg)
87 {
88         char *canonicalName;
89         canonicalName = ldb_dn_canonical_string(msg, msg->dn);
90         if (canonicalName == NULL) {
91                 return -1;
92         }
93         return ldb_msg_add_steal_string(msg, "canonicalName", canonicalName);
94 }
95
96 /*
97   a list of attribute names that should be substituted in the parse
98   tree before the search is done
99 */
100 static const struct {
101         const char *attr;
102         const char *replace;
103 } parse_tree_sub[] = {
104         { "createTimestamp", "whenCreated" },
105         { "modifyTimestamp", "whenChanged" }
106 };
107
108
109 /*
110   a list of attribute names that are hidden, but can be searched for
111   using another (non-hidden) name to produce the correct result
112 */
113 static const struct {
114         const char *attr;
115         const char *replace;
116         int (*constructor)(struct ldb_module *, struct ldb_message *);
117 } search_sub[] = {
118         { "createTimestamp", "whenCreated", NULL },
119         { "modifyTimestamp", "whenChanged", NULL },
120         { "structuralObjectClass", "objectClass", NULL },
121         { "canonicalName", "distinguishedName", construct_canonical_name }
122 };
123
124 /*
125   post process a search result record. For any search_sub[] attributes that were
126   asked for, we need to call the appropriate copy routine to copy the result
127   into the message, then remove any attributes that we added to the search but were
128   not asked for by the user
129 */
130 static int operational_search_post_process(struct ldb_module *module,
131                                            struct ldb_message *msg, 
132                                            const char * const *attrs)
133 {
134         struct ldb_context *ldb;
135         int i, a=0;
136
137         ldb = ldb_module_get_ctx(module);
138
139         for (a=0;attrs && attrs[a];a++) {
140                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
141                         if (ldb_attr_cmp(attrs[a], search_sub[i].attr) != 0) {
142                                 continue;
143                         }
144
145                         /* construct the new attribute, using either a supplied 
146                            constructor or a simple copy */
147                         if (search_sub[i].constructor) {
148                                 if (search_sub[i].constructor(module, msg) != 0) {
149                                         goto failed;
150                                 }
151                         } else if (ldb_msg_copy_attr(msg,
152                                                      search_sub[i].replace,
153                                                      search_sub[i].attr) != 0) {
154                                 goto failed;
155                         }
156
157                         /* remove the added search attribute, unless it was asked for 
158                            by the user */
159                         if (search_sub[i].replace == NULL ||
160                             ldb_attr_in_list(attrs, search_sub[i].replace) ||
161                             ldb_attr_in_list(attrs, "*")) {
162                                 continue;
163                         }
164
165                         ldb_msg_remove_attr(msg, search_sub[i].replace);
166                 }
167         }
168
169         return 0;
170
171 failed:
172         ldb_debug_set(ldb, LDB_DEBUG_WARNING, 
173                       "operational_search_post_process failed for attribute '%s'\n", 
174                       attrs[a]);
175         return -1;
176 }
177
178
179 /*
180   hook search operations
181 */
182
183 struct operational_context {
184         struct ldb_module *module;
185         struct ldb_request *req;
186
187         const char * const *attrs;
188 };
189
190 static int operational_callback(struct ldb_request *req, struct ldb_reply *ares)
191 {
192         struct operational_context *ac;
193         int ret;
194
195         ac = talloc_get_type(req->context, struct operational_context);
196
197         if (!ares) {
198                 return ldb_module_done(ac->req, NULL, NULL,
199                                         LDB_ERR_OPERATIONS_ERROR);
200         }
201         if (ares->error != LDB_SUCCESS) {
202                 return ldb_module_done(ac->req, ares->controls,
203                                         ares->response, ares->error);
204         }
205
206         switch (ares->type) {
207         case LDB_REPLY_ENTRY:
208                 /* for each record returned post-process to add any derived
209                    attributes that have been asked for */
210                 ret = operational_search_post_process(ac->module,
211                                                         ares->message,
212                                                         ac->attrs);
213                 if (ret != 0) {
214                         return ldb_module_done(ac->req, NULL, NULL,
215                                                 LDB_ERR_OPERATIONS_ERROR);
216                 }
217                 return ldb_module_send_entry(ac->req, ares->message, ares->controls);
218
219         case LDB_REPLY_REFERRAL:
220                 /* ignore referrals */
221                 break;
222
223         case LDB_REPLY_DONE:
224
225                 return ldb_module_done(ac->req, ares->controls,
226                                         ares->response, LDB_SUCCESS);
227         }
228
229         talloc_free(ares);
230         return LDB_SUCCESS;
231 }
232
233 static int operational_search(struct ldb_module *module, struct ldb_request *req)
234 {
235         struct ldb_context *ldb;
236         struct operational_context *ac;
237         struct ldb_request *down_req;
238         const char **search_attrs = NULL;
239         int i, a;
240         int ret;
241
242         ldb = ldb_module_get_ctx(module);
243
244         ac = talloc(req, struct operational_context);
245         if (ac == NULL) {
246                 return LDB_ERR_OPERATIONS_ERROR;
247         }
248
249         ac->module = module;
250         ac->req = req;
251         ac->attrs = req->op.search.attrs;
252
253         /*  FIXME: We must copy the tree and keep the original
254          *  unmodified. SSS */
255         /* replace any attributes in the parse tree that are
256            searchable, but are stored using a different name in the
257            backend */
258         for (i=0;i<ARRAY_SIZE(parse_tree_sub);i++) {
259                 ldb_parse_tree_attr_replace(req->op.search.tree, 
260                                             parse_tree_sub[i].attr, 
261                                             parse_tree_sub[i].replace);
262         }
263
264         /* in the list of attributes we are looking for, rename any
265            attributes to the alias for any hidden attributes that can
266            be fetched directly using non-hidden names */
267         for (a=0;ac->attrs && ac->attrs[a];a++) {
268                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
269                         if (ldb_attr_cmp(ac->attrs[a], search_sub[i].attr) == 0 &&
270                             search_sub[i].replace) {
271                                 if (!search_attrs) {
272                                         search_attrs = ldb_attr_list_copy(req, ac->attrs);
273                                         if (search_attrs == NULL) {
274                                                 return LDB_ERR_OPERATIONS_ERROR;
275                                         }
276                                 }
277                                 search_attrs[a] = search_sub[i].replace;
278                         }
279                 }
280         }
281
282         ret = ldb_build_search_req_ex(&down_req, ldb, ac,
283                                         req->op.search.base,
284                                         req->op.search.scope,
285                                         req->op.search.tree,
286                                         /* use new set of attrs if any */
287                                         search_attrs == NULL?req->op.search.attrs:search_attrs,
288                                         req->controls,
289                                         ac, operational_callback,
290                                         req);
291         if (ret != LDB_SUCCESS) {
292                 return LDB_ERR_OPERATIONS_ERROR;
293         }
294
295         /* perform the search */
296         return ldb_next_request(module, down_req);
297 }
298
299 static int operational_init(struct ldb_module *ctx)
300 {
301         int ret = 0;
302
303         if (ret != 0) {
304                 return ret;
305         }
306
307         return ldb_next_init(ctx);
308 }
309
310 const struct ldb_module_ops ldb_operational_module_ops = {
311         .name              = "operational",
312         .search            = operational_search,
313         .init_context      = operational_init
314 };