7dc4ae08c380eee8947643e5afc7bb8394728ac8
[tprouty/samba.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
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
78 /*
79   construct a canonical name from a message
80 */
81 static int construct_canonical_name(struct ldb_module *module, struct ldb_message *msg)
82 {
83         char *canonicalName;
84         canonicalName = ldb_dn_canonical_string(msg, msg->dn);
85         if (canonicalName == NULL) {
86                 return -1;
87         }
88         return ldb_msg_add_steal_string(msg, "canonicalName", canonicalName);
89 }
90
91 /*
92   a list of attribute names that should be substituted in the parse
93   tree before the search is done
94 */
95 static const struct {
96         const char *attr;
97         const char *replace;
98 } parse_tree_sub[] = {
99         { "createTimestamp", "whenCreated" },
100         { "modifyTimestamp", "whenChanged" }
101 };
102
103
104 /*
105   a list of attribute names that are hidden, but can be searched for
106   using another (non-hidden) name to produce the correct result
107 */
108 static const struct {
109         const char *attr;
110         const char *replace;
111         int (*constructor)(struct ldb_module *, struct ldb_message *);
112 } search_sub[] = {
113         { "createTimestamp", "whenCreated", NULL },
114         { "modifyTimestamp", "whenChanged", NULL },
115         { "structuralObjectClass", "objectClass", NULL },
116         { "canonicalName", "distinguishedName", construct_canonical_name }
117 };
118
119 /*
120   post process a search result record. For any search_sub[] attributes that were
121   asked for, we need to call the appropriate copy routine to copy the result
122   into the message, then remove any attributes that we added to the search but were
123   not asked for by the user
124 */
125 static int operational_search_post_process(struct ldb_module *module,
126                                            struct ldb_message *msg, 
127                                            const char * const *attrs)
128 {
129         int i, a=0;
130
131         for (a=0;attrs && attrs[a];a++) {
132                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
133                         if (ldb_attr_cmp(attrs[a], search_sub[i].attr) != 0) {
134                                 continue;
135                         }
136
137                         /* construct the new attribute, using either a supplied 
138                            constructor or a simple copy */
139                         if (search_sub[i].constructor) {
140                                 if (search_sub[i].constructor(module, msg) != 0) {
141                                         goto failed;
142                                 }
143                         } else if (ldb_msg_copy_attr(msg,
144                                                      search_sub[i].replace,
145                                                      search_sub[i].attr) != 0) {
146                                 goto failed;
147                         }
148
149                         /* remove the added search attribute, unless it was asked for 
150                            by the user */
151                         if (search_sub[i].replace == NULL ||
152                             ldb_attr_in_list(attrs, search_sub[i].replace) ||
153                             ldb_attr_in_list(attrs, "*")) {
154                                 continue;
155                         }
156
157                         ldb_msg_remove_attr(msg, search_sub[i].replace);
158                 }
159         }
160
161         return 0;
162
163 failed:
164         ldb_debug_set(module->ldb, LDB_DEBUG_WARNING, 
165                       "operational_search_post_process failed for attribute '%s'\n", 
166                       attrs[a]);
167         return -1;
168 }
169
170
171 /*
172   hook search operations
173 */
174
175 struct operational_context {
176
177         struct ldb_module *module;
178         void *up_context;
179         int (*up_callback)(struct ldb_context *, void *, struct ldb_reply *);
180
181         const char * const *attrs;
182 };
183
184 static int operational_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
185 {
186         struct operational_context *ac;
187
188         if (!context || !ares) {
189                 ldb_set_errstring(ldb, "NULL Context or Result in callback");
190                 goto error;
191         }
192
193         ac = talloc_get_type(context, struct operational_context);
194
195         if (ares->type == LDB_REPLY_ENTRY) {
196                 /* for each record returned post-process to add any derived
197                    attributes that have been asked for */
198                 if (operational_search_post_process(ac->module, ares->message, ac->attrs) != 0) {
199                         goto error;
200                 }
201         }
202
203         return ac->up_callback(ldb, ac->up_context, ares);
204
205 error:
206         talloc_free(ares);
207         return LDB_ERR_OPERATIONS_ERROR;
208 }
209
210 static int operational_search(struct ldb_module *module, struct ldb_request *req)
211 {
212         struct operational_context *ac;
213         struct ldb_request *down_req;
214         const char **search_attrs = NULL;
215         int i, a, ret;
216
217         req->handle = NULL;
218
219         ac = talloc(req, struct operational_context);
220         if (ac == NULL) {
221                 return LDB_ERR_OPERATIONS_ERROR;
222         }
223
224         ac->module = module;
225         ac->up_context = req->context;
226         ac->up_callback = req->callback;
227         ac->attrs = req->op.search.attrs;
228
229         down_req = talloc_zero(req, struct ldb_request);
230         if (down_req == NULL) {
231                 return LDB_ERR_OPERATIONS_ERROR;
232         }
233
234         down_req->operation = req->operation;
235         down_req->op.search.base = req->op.search.base;
236         down_req->op.search.scope = req->op.search.scope;
237         down_req->op.search.tree = req->op.search.tree;
238
239         /*  FIXME: I hink we should copy the tree and keep the original
240          *  unmodified. SSS */
241         /* replace any attributes in the parse tree that are
242            searchable, but are stored using a different name in the
243            backend */
244         for (i=0;i<ARRAY_SIZE(parse_tree_sub);i++) {
245                 ldb_parse_tree_attr_replace(req->op.search.tree, 
246                                             parse_tree_sub[i].attr, 
247                                             parse_tree_sub[i].replace);
248         }
249
250         /* in the list of attributes we are looking for, rename any
251            attributes to the alias for any hidden attributes that can
252            be fetched directly using non-hidden names */
253         for (a=0;ac->attrs && ac->attrs[a];a++) {
254                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
255                         if (ldb_attr_cmp(ac->attrs[a], search_sub[i].attr) == 0 &&
256                             search_sub[i].replace) {
257                                 if (!search_attrs) {
258                                         search_attrs = ldb_attr_list_copy(req, ac->attrs);
259                                         if (search_attrs == NULL) {
260                                                 return LDB_ERR_OPERATIONS_ERROR;
261                                         }
262                                 }
263                                 search_attrs[a] = search_sub[i].replace;
264                         }
265                 }
266         }
267         
268         /* use new set of attrs if any */
269         if (search_attrs) down_req->op.search.attrs = search_attrs;
270         else down_req->op.search.attrs = req->op.search.attrs;
271         
272         down_req->controls = req->controls;
273
274         down_req->context = ac;
275         down_req->callback = operational_callback;
276         ldb_set_timeout_from_prev_req(module->ldb, req, down_req);
277
278         /* perform the search */
279         ret = ldb_next_request(module, down_req);
280
281         /* do not free down_req as the call results may be linked to it,
282          * it will be freed when the upper level request get freed */
283         if (ret == LDB_SUCCESS) {
284                 req->handle = down_req->handle;
285         }
286
287         return ret;
288 }
289
290 static int operational_init(struct ldb_module *ctx)
291 {
292         int ret = 0;
293
294         /* setup some standard attribute handlers */
295         ret |= ldb_schema_attribute_add(ctx->ldb, "whenCreated", 0, LDB_SYNTAX_UTC_TIME);
296         ret |= ldb_schema_attribute_add(ctx->ldb, "whenChanged", 0, LDB_SYNTAX_UTC_TIME);
297         ret |= ldb_schema_attribute_add(ctx->ldb, "subschemaSubentry", 0, LDB_SYNTAX_DN);
298         ret |= ldb_schema_attribute_add(ctx->ldb, "structuralObjectClass", 0, LDB_SYNTAX_OBJECTCLASS);
299
300         if (ret != 0) {
301                 return ret;
302         }
303
304         return ldb_next_init(ctx);
305 }
306
307 const struct ldb_module_ops ldb_operational_module_ops = {
308         .name              = "operational",
309         .search            = operational_search,
310         .init_context      = operational_init
311 };