4 Copyright (C) Andrew Tridgell 2005
6 ** NOTE! The following LGPL license applies to the ldb
7 ** library. This does NOT imply that all of Samba is released
10 This library is free software; you can redistribute it and/or
11 modify it under the terms of the GNU Lesser General Public
12 License as published by the Free Software Foundation; either
13 version 2 of the License, or (at your option) any later version.
15 This library is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 Lesser General Public License for more details.
20 You should have received a copy of the GNU Lesser General Public
21 License along with this library; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 handle operational attributes
29 createTimestamp: HIDDEN, searchable, ldaptime, alias for whenCreated
30 modifyTimestamp: HIDDEN, searchable, ldaptime, alias for whenChanged
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
37 we also need to replace these with the whenCreated/whenChanged
38 equivalent in the search expression trees
40 whenCreated: not-HIDDEN, CONSTRUCTED, SEARCHABLE
41 whenChanged: not-HIDDEN, CONSTRUCTED, SEARCHABLE
43 on init we need to setup attribute handlers for these so
44 comparisons are done correctly. The resolution is 1 second.
46 on add we need to add both the above, for current time
48 on modify we need to change whenChanged
51 subschemaSubentry: HIDDEN, not-searchable,
52 points at DN CN=Aggregate,CN=Schema,CN=Configuration,$BASEDN
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
59 structuralObjectClass: HIDDEN, CONSTRUCTED, not-searchable. always same as objectclass?
61 for this one we do the search as normal, then if requested ask
62 for objectclass, change the attribute name, and add it
64 allowedAttributesEffective: HIDDEN, CONSTRUCTED, not-searchable,
65 list of attributes that can be modified - requires schema lookup
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?
78 #include "ldb/include/ldb.h"
79 #include "ldb/include/ldb_errors.h"
80 #include "ldb/include/ldb_private.h"
84 construct a canonical name from a message
86 static int construct_canonical_name(struct ldb_module *module, struct ldb_message *msg)
89 canonicalName = ldb_dn_canonical_string(msg, msg->dn);
90 if (canonicalName == NULL) {
93 return ldb_msg_add_string(msg, "canonicalName", canonicalName);
97 a list of attribute names that should be substituted in the parse
98 tree before the search is done
100 static const struct {
103 } parse_tree_sub[] = {
104 { "createTimestamp", "whenCreated" },
105 { "modifyTimestamp", "whenChanged" }
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
113 static const struct {
116 int (*constructor)(struct ldb_module *, struct ldb_message *);
118 { "createTimestamp", "whenCreated", NULL },
119 { "modifyTimestamp", "whenChanged", NULL },
120 { "structuralObjectClass", "objectClass", NULL },
121 { "canonicalName", "distinguishedName", construct_canonical_name }
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
130 static int operational_search_post_process(struct ldb_module *module,
131 struct ldb_message *msg,
132 const char * const *attrs)
136 for (a=0;attrs && attrs[a];a++) {
137 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
138 if (ldb_attr_cmp(attrs[a], search_sub[i].attr) != 0) {
142 /* construct the new attribute, using either a supplied
143 constructor or a simple copy */
144 if (search_sub[i].constructor) {
145 if (search_sub[i].constructor(module, msg) != 0) {
148 } else if (ldb_msg_copy_attr(msg,
149 search_sub[i].replace,
150 search_sub[i].attr) != 0) {
154 /* remove the added search attribute, unless it was asked for
156 if (search_sub[i].replace == NULL ||
157 ldb_attr_in_list(attrs, search_sub[i].replace) ||
158 ldb_attr_in_list(attrs, "*")) {
162 ldb_msg_remove_attr(msg, search_sub[i].replace);
169 ldb_debug_set(module->ldb, LDB_DEBUG_WARNING,
170 "operational_search_post_process failed for attribute '%s'\n",
176 hook search operations
178 static int operational_search_bytree(struct ldb_module *module, struct ldb_request *req)
182 const char * const *attrs = req->op.search.attrs;
183 const char **search_attrs = NULL;
185 req->op.search.res = NULL;
187 /* replace any attributes in the parse tree that are
188 searchable, but are stored using a different name in the
190 for (i=0;i<ARRAY_SIZE(parse_tree_sub);i++) {
191 ldb_parse_tree_attr_replace(req->op.search.tree,
192 parse_tree_sub[i].attr,
193 parse_tree_sub[i].replace);
196 /* in the list of attributes we are looking for, rename any
197 attributes to the alias for any hidden attributes that can
198 be fetched directly using non-hidden names */
199 for (a=0;attrs && attrs[a];a++) {
200 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
201 if (ldb_attr_cmp(attrs[a], search_sub[i].attr) == 0 &&
202 search_sub[i].replace) {
204 search_attrs = ldb_attr_list_copy(req, attrs);
205 if (search_attrs == NULL) {
209 search_attrs[a] = search_sub[i].replace;
214 /* use new set of attrs if any */
215 if (search_attrs) req->op.search.attrs = search_attrs;
216 /* perform the search */
217 ret = ldb_next_request(module, req);
218 /* set back saved attrs if needed */
219 if (search_attrs) req->op.search.attrs = attrs;
221 /* check operation result */
222 if (ret != LDB_SUCCESS) {
226 /* for each record returned post-process to add any derived
227 attributes that have been asked for */
228 for (r = 0; r < req->op.search.res->count; r++) {
229 if (operational_search_post_process(module, req->op.search.res->msgs[r], attrs) != 0) {
235 talloc_free(search_attrs);
239 talloc_free(search_attrs);
240 talloc_free(req->op.search.res);
241 ldb_oom(module->ldb);
242 return LDB_ERR_OTHER;
246 add a time element to a record
248 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
250 struct ldb_message_element *el;
253 if (ldb_msg_find_element(msg, attr) != NULL) {
257 s = ldb_timestring(msg, t);
262 if (ldb_msg_add_string(msg, attr, s) != 0) {
266 el = ldb_msg_find_element(msg, attr);
267 /* always set as replace. This works because on add ops, the flag
269 el->flags = LDB_FLAG_MOD_REPLACE;
278 static int operational_add(struct ldb_module *module, struct ldb_request *req)
280 const struct ldb_message *msg = req->op.add.message;
281 time_t t = time(NULL);
282 struct ldb_message *msg2;
285 if (ldb_dn_is_special(msg->dn)) {
286 return ldb_next_request(module, req);
289 /* we have to copy the message as the caller might have it as a const */
290 msg2 = ldb_msg_copy_shallow(module, msg);
294 if (add_time_element(msg2, "whenCreated", t) != 0 ||
295 add_time_element(msg2, "whenChanged", t) != 0) {
299 /* use the new structure for the call chain below this point */
300 req->op.add.message = msg2;
301 /* go on with the call chain */
302 ret = ldb_next_request(module, req);
303 /* put back saved message */
304 req->op.add.message = msg;
305 /* free temproary compy */
311 hook modify record ops
313 static int operational_modify(struct ldb_module *module, struct ldb_request *req)
315 const struct ldb_message *msg = req->op.mod.message;
316 time_t t = time(NULL);
317 struct ldb_message *msg2;
320 if (ldb_dn_is_special(msg->dn)) {
321 return ldb_next_request(module, req);
324 /* we have to copy the message as the caller might have it as a const */
325 msg2 = ldb_msg_copy_shallow(module, msg);
329 if (add_time_element(msg2, "whenChanged", t) != 0) {
333 /* use the new structure for the call chain below this point */
334 req->op.mod.message = msg2;
335 /* go on with the call chain */
336 ret = ldb_next_request(module, req);
337 /* put back saved message */
338 req->op.mod.message = msg;
339 /* free temproary compy */
345 static int operational_request(struct ldb_module *module, struct ldb_request *req)
347 switch (req->operation) {
350 return operational_search_bytree(module, req);
353 return operational_add(module, req);
356 return operational_modify(module, req);
359 return ldb_next_request(module, req);
364 static const struct ldb_module_ops operational_ops = {
365 .name = "operational",
366 .request = operational_request
370 /* the init function */
371 struct ldb_module *operational_module_init(struct ldb_context *ldb, const char *options[])
373 struct ldb_module *ctx;
375 ctx = talloc(ldb, struct ldb_module);
379 ctx->private_data = NULL;
381 ctx->prev = ctx->next = NULL;
382 ctx->ops = &operational_ops;
384 /* setup some standard attribute handlers */
385 ldb_set_attrib_handler_syntax(ldb, "whenCreated", LDB_SYNTAX_UTC_TIME);
386 ldb_set_attrib_handler_syntax(ldb, "whenChanged", LDB_SYNTAX_UTC_TIME);
387 ldb_set_attrib_handler_syntax(ldb, "subschemaSubentry", LDB_SYNTAX_DN);
388 ldb_set_attrib_handler_syntax(ldb, "structuralObjectClass", LDB_SYNTAX_OBJECTCLASS);