4 Copyright (C) Andrew Tridgell 2005
5 Copyright (C) Simo Sorce 2006
7 ** NOTE! The following LGPL license applies to the ldb
8 ** library. This does NOT imply that all of Samba is released
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.
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.
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/>.
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,$SCHEMADN
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?
76 #include "ldb_includes.h"
79 construct a canonical name from a message
81 static int construct_canonical_name(struct ldb_module *module, struct ldb_message *msg)
84 canonicalName = ldb_dn_canonical_string(msg, msg->dn);
85 if (canonicalName == NULL) {
88 return ldb_msg_add_steal_string(msg, "canonicalName", canonicalName);
92 a list of attribute names that should be substituted in the parse
93 tree before the search is done
98 } parse_tree_sub[] = {
99 { "createTimestamp", "whenCreated" },
100 { "modifyTimestamp", "whenChanged" }
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
108 static const struct {
111 int (*constructor)(struct ldb_module *, struct ldb_message *);
113 { "createTimestamp", "whenCreated", NULL },
114 { "modifyTimestamp", "whenChanged", NULL },
115 { "structuralObjectClass", "objectClass", NULL },
116 { "canonicalName", "distinguishedName", construct_canonical_name }
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
125 static int operational_search_post_process(struct ldb_module *module,
126 struct ldb_message *msg,
127 const char * const *attrs)
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) {
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) {
143 } else if (ldb_msg_copy_attr(msg,
144 search_sub[i].replace,
145 search_sub[i].attr) != 0) {
149 /* remove the added search attribute, unless it was asked for
151 if (search_sub[i].replace == NULL ||
152 ldb_attr_in_list(attrs, search_sub[i].replace) ||
153 ldb_attr_in_list(attrs, "*")) {
157 ldb_msg_remove_attr(msg, search_sub[i].replace);
164 ldb_debug_set(module->ldb, LDB_DEBUG_WARNING,
165 "operational_search_post_process failed for attribute '%s'\n",
172 hook search operations
175 struct operational_context {
177 struct ldb_module *module;
179 int (*up_callback)(struct ldb_context *, void *, struct ldb_reply *);
181 const char * const *attrs;
184 static int operational_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
186 struct operational_context *ac;
188 if (!context || !ares) {
189 ldb_set_errstring(ldb, "NULL Context or Result in callback");
193 ac = talloc_get_type(context, struct operational_context);
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) {
203 return ac->up_callback(ldb, ac->up_context, ares);
207 return LDB_ERR_OPERATIONS_ERROR;
210 static int operational_search(struct ldb_module *module, struct ldb_request *req)
212 struct operational_context *ac;
213 struct ldb_request *down_req;
214 const char **search_attrs = NULL;
219 ac = talloc(req, struct operational_context);
221 return LDB_ERR_OPERATIONS_ERROR;
225 ac->up_context = req->context;
226 ac->up_callback = req->callback;
227 ac->attrs = req->op.search.attrs;
229 down_req = talloc_zero(req, struct ldb_request);
230 if (down_req == NULL) {
231 return LDB_ERR_OPERATIONS_ERROR;
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;
239 /* FIXME: I hink we should copy the tree and keep the original
241 /* replace any attributes in the parse tree that are
242 searchable, but are stored using a different name in the
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);
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) {
258 search_attrs = ldb_attr_list_copy(req, ac->attrs);
259 if (search_attrs == NULL) {
260 return LDB_ERR_OPERATIONS_ERROR;
263 search_attrs[a] = search_sub[i].replace;
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;
272 down_req->controls = req->controls;
274 down_req->context = ac;
275 down_req->callback = operational_callback;
276 ldb_set_timeout_from_prev_req(module->ldb, req, down_req);
278 /* perform the search */
279 ret = ldb_next_request(module, down_req);
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;
290 static int operational_init(struct ldb_module *ctx)
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);
304 return ldb_next_init(ctx);
307 static const struct ldb_module_ops operational_ops = {
308 .name = "operational",
309 .search = operational_search,
310 .init_context = operational_init
313 int ldb_operational_init(void)
315 return ldb_register_module(&operational_ops);