Sun Java System Directory Server Enterprise Edition 6.0 Developer's Guide

Extending the Search Operation

This section shows how to develop functionality called by Directory Server before LDAP search operations.

Logging Who Requests a Search

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.


Example 6–3 Getting the DN of the Client Requesting a Search (testbind.c)

#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

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.

LOG Macros for Compact Code

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().

Parameter Block Contents

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:


Example 6–4 Logging Base and Scope for a Search (testpreop.c)

#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:

Search Filter Info

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.


Example 6–5 Retrieving Filter Information (testpreop.c)

#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.


Example 6–6 Search Filter Breakdown

*** 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 ***

Building a Filter Info

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.

Normal Directory Server Search Behavior

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.