This section shows how to develop functionality called by Directory Server before LDAP search operations.
The following example logs the DN of the client that requests the search. Refer to install-path/examples/testbind.c for complete example code.
Before using the plug-in function as shown in this section, set up the example suffix and register the plug-in. See Extending the Bind Operation and “To register the Plug-in”, as described previously. The plug-in, Test Bind, also includes the pre-search function.
The test_search() function logs the request, as shown in the following example.
#include "slapi-plugin.h" int test_search(Slapi_PBlock * pb) { char * requestor_dn; /* DN of client searching */ int is_repl; /* Is this replication? */ int is_intl; /* Is this an internal op? */ int connId, opId, rc = 0; long msgId; rc |=slapi_pblock_get(pb, SLAPI_OPERATION_MSGID, &msgId); rc |=slapi_pblock_get(pb, SLAPI_CONN_ID, &connId); rc |=slapi_pblock_get(pb, SLAPI_OPERATION_ID, &opId); rc |=slapi_pblock_get(pb, SLAPI_REQUESTOR_DN, &requestor_dn); rc |=slapi_pblock_get(pb, SLAPI_IS_REPLICATED_OPERATION, &is_repl); rc |=slapi_pblock_get(pb, SLAPI_IS_INTERNAL_OPERATION, &is_intl); if (rc != 0) return (rc); /* Do not interfere with replication and internal operations. */ if (is_repl || is_intl) return 0; if (requestor_dn != NULL && *requestor_dn != '\0') { slapi_log_info_ex( SLAPI_LOG_INFO_AREA_PLUGIN, SLAPI_LOG_INFO_LEVEL_DEFAULT, msgId, connId, opId, "test_search in test-bind plug-in", "Search requested by %s\n", requestor_dn ); } else { slapi_log_info_ex( SLAPI_LOG_INFO_AREA_PLUGIN, SLAPI_LOG_INFO_LEVEL_DEFAULT, msgId, connId, opId, "test_search in test-bind plug-in", "Search requested by anonymous client.\n" ); } return (rc); }
After activating the plug-in in the server, perform a search.
$ ldapsearch -D uid=kvaughan,ou=people,dc=example,dc=com -w bribery \ -h localhost -p 1389 -b dc=example,dc=com uid=bjensen |
Search instance-path/logs/errors for the resulting message. The last field of the log entry shows the following:
With the plug-in activated in Directory Server, perform a search as Kirsten Vaughan:
Authenticated: uid=kvaughan,ou=people,dc=example,dc=com
Breaking Down a Search Filter breaks down a search filter, logging the component parts of the filter. For complete example code, refer to install-path/examples/testpreop.c.
The code for the SLAPI_PLUGIN_PRE_SEARCH_FN function, testpreop_search(), is preceded by three macros. Search testpreop.c for #define LOG1(format). The purpose of these macros is only to render the subsequent logging statements more compact, making the code easier to decipher. The digits in LOG1, LOG2, and LOG3 reflect the number of parameters each macro takes. The actual number of parameters that you pass to the log functions varies, as the log functions let you format strings in the style of printf().
The parameter block passed to the pre-search function contains information about the target and scope of the search. The following example shows how to obtain this information. The scope, as specified in RFC 4511, Lightweight Directory Access Protocol (v3), can include one of the following:
The base object, which is the base DN entry
single level under the base DN
The whole subtree under the base DN
#include "slapi-plugin.h" int testpreop_search(Slapi_PBlock * pb) { char * base = NULL;/* Base DN for search */ int scope; /* Base, 1 level, subtree */ int rc = 0; rc |= slapi_pblock_get(pb, SLAPI_SEARCH_TARGET, &base); rc |= slapi_pblock_get(pb, SLAPI_SEARCH_SCOPE, &scope); if (rc == 0) { switch (scope) { case LDAP_SCOPE_BASE: LOG2("Target DN: %s\tScope: LDAP_SCOPE_BASE\n", base); break; case LDAP_SCOPE_ONELEVEL: LOG2("Target DN: %s\tScope: LDAP_SCOPE_ONELEVEL\n", base); break; case LDAP_SCOPE_SUBTREE: LOG2("Target DN: %s\tScope: LDAP_SCOPE_SUBTREE\n", base); break; default: LOG3("Target DN: %s\tScope: unknown value %d\n", base, scope); break; } } /* Continue processing... */ return 0; }
In writing a plug-in mapping X.500 search targets to LDAP search targets, you could choose to parse and transform the base DN for searches.
The parameter block also contains the following information:
When aliases are resolved during the search
The Directory Server resources that the search is set to consume
Which attribute types are to be returned
The search filter that you obtain from the parameter block must be considered. testpreop_search() gets the filter from the parameter block both as a Slapi_Filter structure, filter, and as a string, filter_str. Depending on what kind of search filter is passed to Directory Server and retrieved in the Slapi_Filter structure, the plug-in breaks the filter down in different ways. If the function were post-search rather than pre-search, you would likely want to access the results that are returned through the parameter block. See Part II, Directory Server Plug-In API Reference for instructions on accessing the results.
The plug-in API offers several functions for manipulating the search filter. testpreop_search() uses slapi_filter_get_choice() to determine the kind of filter used. The preoperation search function also uses slapi_filter_get_subfilt() to break down substrings that are used in the filter. The preoperation search function further uses slapi_filter_get_type() to find the attribute type when searching for the presence of an attribute. The preoperation search function uses slapi_filter_get_ava() to obtain attribute types and values used for comparisons.
The following example shows a code excerpt that retrieves information from the search filter.
#include "slapi-plugin.h" int testpreop_search(Slapi_PBlock * pb) { char * attr_type = NULL;/* For substr and compare */ char * substr_init= NULL;/* For substring filters */ char * substr_final=NULL; char ** substr_any = NULL; int i, rc =0; Slapi_Filter * filter; rc |= slapi_pblock_get(pb, SLAPI_SEARCH_FILTER, &filter); if (rc == 0) { filter_type = slapi_filter_get_choice(filter); switch (filter_type) { case LDAP_FILTER_AND: case LDAP_FILTER_OR: case LDAP_FILTER_NOT: LOG1("Search filter: complex boolean\n"); break; case LDAP_FILTER_EQUALITY: LOG1("Search filter: LDAP_FILTER_EQUALITY\n"); break; case LDAP_FILTER_GE: LOG1("Search filter: LDAP_FILTER_GE\n"); break; case LDAP_FILTER_LE: LOG1("Search filter: LDAP_FILTER_LE\n"); break; case LDAP_FILTER_APPROX: LOG1("Search filter: LDAP_FILTER_APPROX\n"); break; case LDAP_FILTER_SUBSTRINGS: LOG1("Search filter: LDAP_FILTER_SUBSTRINGS\n"); slapi_filter_get_subfilt( filter, &attr_type, &substr_init, &substr_any, &substr_final ); if (attr_type != NULL) LOG2("\tAttribute type: %s\n", attr_type); if (substr_init != NULL) LOG2("\tInitial substring: %s\n", substr_init); if (substr_any != NULL) for (i = 0; substr_any[i] != NULL; ++i) { LOG3("\tSubstring# %d: %s\n", i, substr_any[i]); } if (substr_final != NULL) LOG2("\tFinal substring: %s\n", substr_final); break; case LDAP_FILTER_PRESENT: LOG1("Search filter: LDAP_FILTER_PRESENT\n"); slapi_filter_get_attribute_type(filter, &attr_type); LOG2("\tAttribute type: %s\n", attr_type); break; case LDAP_FILTER_EXTENDED: LOG1("Search filter: LDAP_FILTER_EXTENDED\n"); break; default: LOG2("Search filter: unknown type %d\n", filter_type); break; } } return (rc); }
Before using the plug-in function as described here, set up the example suffix and register the plug-in. See Extending the Bind Operation and “To register the Plug-in”, as described previously.
After the example suffix and plug-in have been loaded into the directory, run a search to generate output in instance-path/logs/errors.
$ ldapsearch -h localhost -p 1389 -b "dc=example,dc=com" "(uid=*go*iv*)" |
When you ignore the housekeeping information in the error log, the search filter breakdown occurss as shown in the following example.
*** PREOPERATION SEARCH PLUG-IN - START *** Target DN: dc=example,dc=com Scope: LDAP_SCOPE_SUBTREE Dereference setting: LDAP_DEREF_NEVER Search filter: LDAP_FILTER_SUBSTRINGS Attribute type: uid Substring# 0: go Substring# 1: iv String representation of search filter: (uid=*go*iv*) *** PREOPERATION SEARCH PLUG-IN - END ***
When building a filter, notice that the plug-in API provides slapi_str2filter() to convert strings to Slapi_Filter data types, and slapi_filter_join() to join simple filters with boolean operators AND, OR, or NOT. Free the filter with slapi_filter_free(). Refer to Part II, Directory Server Plug-In API Reference for details.
Directory Server gets a list of candidate entries, then iterates through the list to check which entries match the search criteria. The plug-in then puts the set of results in the parameter block. In most cases, searching within a plug-in is typically performed with slapi_search_internal*() calls.