Sun ONE Directory Server 5.2 Plug-In API Programming Guide |
Chapter 5 Extending Client Request Handling
This chapter covers support in the plug-in API for modifying what Directory Server does before or after carrying out operations for clients.
Although related to the bind operation, authentication merits its own discussion in Chapter 6 "Handling Authentication."
Pre-Operation and Post-Operation Plug-Ins
Before and after performing processing client requests and before and after sending information to clients, Directory Server calls pre-operation and post-operation plug-in functions, respectively.
Pre-Operation Plug-Ins
Pre-operation plug-ins are called before a client request is processed. Use pre-operation plug-ins to validate attribute values, and to add to and delete from attributes provided in a request, for example.
Post-Operation Plug-Ins
Post-operation plug-ins are called after a client request has been processed, whether or not the operation completes successfully. Use post-operation plug-ins to send alerts and alarms, and to perform auditing and clean up work, for example.
Operations supporting the use of pre- and post-operation plug-in functions include abandon, add, bind, compare, delete, modify, modify RDN, search, send entry, send referral, send result, and unbind, regardless of Directory Server front end contacted by the client application.
As is the case for other plug-in functions, slapi_pblock_set() is used in the plug-in initialization function to register pre- and post-operation plug-in functions. slapi_pblock_set() is described in the Sun ONE Directory Server Plug-In API Reference.
Registration Identifiers
Pre- and post-operation plug-in registrations use IDs of the form SLAPI_PLUGIN_PRE_operation_FN or SLAPI_PLUGIN_POST_operation_FN, where operation is one of ABANDON, ADD, BIND, COMPARE, DELETE, ENTRY (sending entries to the client), MODIFY, MODRDN, REFERRAL (sending referrals to the client), RESULT (sending results to the client), SEARCH, or UNBIND.
Note Pre- and post-operation plug-ins may also register functions to run at Directory Server startup, using SLAPI_PLUGIN_START_FN, and at Directory Server shutdown, using SLAPI_PLUGIN_STOP_FN.
The Sun ONE Directory Server Plug-In API Reference describes these IDs. Refer also to ServerRoot/plugins/slapd/slapi/include/slapi-plugin.h for the complete list of IDs used at build time.
Code Example 5-1 demonstrates how the functions are registered with Directory Server using the appropriate registration IDs.
In addition to its post-operation functions, the plug-in shown in Code Example 5-1 registers a function to open a log file at startup and close the log file at shutdown. For details, refer to ServerRoot/plugins/slapd/slapi/examples/testpostop.c.
Finding Examples
Find plug-in examples under ServerRoot/plugins/slapd/slapi/examples/.
Extending the Bind Operation
This section shows how to develop functions called by Directory Server before client bind operations.
Tip Pre-bind plug-in functions are often used to handle extensions to authentication.
For details, refer to Chapter 6 "Handling Authentication."
Setting Up an Example Suffix
If you have not done so already, prepare some example data by creating a directory suffix, dc=example,dc=com, whose users we load from an LDIF file, ServerRoot/slapd-serverID/ldif/Example-Plugin.ldif. You may do this using either by using the console, or client tools on the command line. Using the command line for example:
- Prepare an LDIF file specifying the backend and suffix for Example.com.
- Add the suffix to the directory.
$ ldapmodify -a -p port -D "cn=directory manager" -w passwd -f file
Here, file is the LDIF file from Code Example 5-2.
- Stop Directory Server.
$ ServerRoot/slapd-serverID/stop-slapd
- Load ServerRoot/slapd-serverID/ldif/Example-Plugin.ldif.
$ ServerRoot/slapd-serverID/ldif2db -s "dc=example,dc=com" \
-i ServerRoot/slapd-serverID/ldif/Example-Plugin.ldif
- Start Directory Server.
$ ServerRoot/slapd-serverID/start-slapd
Logging the Authentication Method
Code Example 5-3 logs the bind authentication method. Refer to ServerRoot/plugins/slapd/slapi/examples/testpreop.c for complete example code.
This plug-in function sets the auth message based on the authentication method. It does nothing to affect the way Directory Server processes the bind.
Registering the Plug-In
If you have not already done so, build the example plug-in library and activate both plug-in informational logging and the Test Preop plug-in.
- Build the plug-in.
Hint Use ServerRoot/plugins/slapd/slapi/examples/Makefile.
- Load the plug-in configuration entry.
Hint Start with the configuration entry specified in the introductory comments of ServerRoot/plugins/slapd/slapi/examples/testpreop.c.
- Restart Directory Server.
- Turn on informational logging for plug-ins.
Refer to "Set Appropriate Log Level in the Directory Server Configuration" for details.
Generating a Bind Log Message
If you use the console, it generates messages whenever the admin user binds. For something different:
- Bind as Plinner Blinn.
$ ldapsearch -p port -b "dc=example,dc=com" \
-D "uid=pblinn,ou=People,dc=example,dc=com" -w pblinn "(uid=*)"
- Search ServerRoot/slapd-serverID/logs/errors for the resulting message from the testpreop_bind() function.
Ignoring housekeeping information for the entry, you find something like this:
Target DN: uid=pblinn,ou=people,dc=example,dc=com Authentication method: Simple authentication
For a discussion of less trivial pre-bind plug-in functions, refer to Chapter 6 "Handling Authentication."
Bypassing Bind Processing in Directory Server
When the plug-in returns 0, Directory Server continues to process the bind. To bypass Directory Server bind processing, set SLAPI_CONN_DN in the parameter block, and return a non-zero value, such as 1.
Normal Directory Server Behavior
Directory Server follows the LDAP bind model. At minimum, it authenticates the client and sends a bind response to indicate the status of authentication. Refer to RFC 2251, Lightweight Directory Access Protocol (v3), and RFC 1777, Lightweight Directory Access Protocol, for details.
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 first example discussed here logs the DN of the client requesting the search. Refer to ServerRoot/plugins/slapd/slapi/examples/testbind.c for complete example code.
Before using the plug-in function as described here, set up the example suffix and register the plug-in as described under "Setting Up an Example Suffix" and "Registering the Plug-In", loading the configuration entry from the introductory comments of testbind.c. (The plug-in named Test Bind also includes our pre-search function.) Code Example 5-4 shows a similar configuration entry.
Adjust nsslapd-pluginPath to fit how the product is installed on your system. Notice the value of nsslapd-pluginType: preoperation.
The test_search() function logs the request, as shown in Code Example 5-5.
With the plug-in active in the server, perform a search.
$ ldapsearch -D "uid=hfuddnud,ou=People,dc=example,dc=com" \
-w hfuddnud -p port -b "dc=example,dc=com" "(uid=pblinn)"Search ServerRoot/slapd-serverID/logs/errors for the resulting message. The last field of the log entry shows:
Search requested by uid=hfuddnud,ou=people,dc=example,dc=com
Breaking Down a Search Filter
The second example discussed here breaks down a search filter, logging the component parts of the filter. For complete example code, refer to ServerRoot/plugins/slapd/slapi/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 passed 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. Code Example 5-6 shows how to obtain this information. The scope, as specified in RFC 2251, Lightweight Directory Access Protocol (v3), can be the base object, which is the base DN itself, a single level below the base DN, or the whole subtree below the base DN.
In writing a plug-in mapping X.500 search targets to LDAP search targets, for example, you could choose to parse and transform the base DN for searches.
The parameter block also contains information about when aliases are resolved during the search, how much Directory Server time and resources the search is set to consume, and which attribute types are to be returned.
Peeking Into the Search Filter
Of particular interest here is the search filter you obtain from the parameter block. 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 returned through the parameter block. Refer to the Sun ONE Directory Server Plug-In API Reference for details on how to access 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, slapi_filter_get_subfilt() to break down substrings used in the filter, slapi_filter_get_type() to find the attribute type when searching for the presence of an attribute, and slapi_filter_get_ava() to obtain attribute types and values used for comparisons.
Code Example 5-7 shows a code excerpt that retrieves some information from the search filter.
Before using the plug-in function as described here, set up the example suffix and register the plug-in as described under "Setting Up an Example Suffix" and "Registering the Plug-In".
After the example suffix and plug-in have been loaded into the directory, run a search to generate some output in ServerRoot/slapd-serverID/logs/errors.
$ ldapsearch -p port -b "dc=example,dc=com" "(uid=*go*iv*)"
Ignoring the housekeeping information in the error log, the search filter breakdown happens as shown in Code Example 5-8.
When Building a Filter
Notice 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. Deallocate the filter using slapi_filter_free(). Refer to the Sun ONE Directory Server Plug-In API Reference for details.
Normal Directory Server Behavior
Directory Server gets a list of candidate entries, then iterates through the list to check which entries match the search criteria. It then puts the set of results in the parameter block. In most cases, searching from within a plug-in is best done with slapi_search_internal*() calls. Refer to Chapter 7 "Performing Internal Operations," for details.
Extending the Compare Operation
This section shows how to develop functionality called by Directory Server before a client compare operation. Code Example 5-9 logs the target DN and attribute of the entry with which to compare values. For complete example code, refer to ServerRoot/plugins/slapd/slapi/examples/testpreop.c.
The plug-in function can access the attribute value to use for the comparison as well. The attribute value in the parameter block is in a berval structure, meaning it could be binary data such as a JPEG image, and no attempt is made to write the value to the logs.
Code Example 5-10 shows the slapi_pblock_get() call used to obtain the attribute value.
Before using the plug-in function as described here, set up the example suffix and register the plug-in as described under "Setting Up an Example Suffix" and "Registering the Plug-In".
Try the example plug-in function using the ldapcompare tool from the Sun ONE Directory Server Resource Kit. Refer to "Downloading Directory Server Tools" for further information.
The log entry in ServerRoot/slapd-serverID/logs/errors results as follows, not including housekeeping information at the beginning of the log entry:
Target DN: uid=pblinn,ou=people,dc=example,dc=com Attribute type: sn
Extending the Add Operation
This section shows how to develop functionality called by Directory Server before and after a client add operation.
Prepending a String to an Attribute
The first example discussed here prepends the string "ADD " to the description attribute of the entry to be added to the directory. For complete example code, refer to ServerRoot/plugins/slapd/slapi/examples/testpreop.c.
Before using the plug-in function as described here, set up the example suffix and register the plug-in as described under "Setting Up an Example Suffix" and "Registering the Plug-In".
To try the pre-operation add function, add an entry for Bartholomew's cousin, Quentin, who recently joined Example.com. Quentin's LDIF entry, quentin.ldif, reads as shown in Code Example 5-12.
Code Example 5-13 performs the prepend.
Add Quentin's entry to the directory. For example, if the entry is in quentin.ldif:
$ ldapmodify -a -p port -D "cn=directory manager" \
-w password -f quentin.ldifAt this point, we can search the directory for Quentin's entry.
Notice the value of the description attribute.
Delete Quentin's entry so you can use it again later as an example.
$ ldapdelete -p port -D "cn=directory manager" -w password \
"uid=qcubbins,ou=People,dc=example,dc=com"Turn off the pre-operation plug-in to avoid prepending ADD to all the entries you add. Do so from the console on the Configuration tab page by selecting the node bearing the common name of the plug-in and clearing the appropriate checkbox. Restart Directory Server (Tasks tab page in the console) for the change to take effect.
Logging the Entry to Add
The second example discussed here logs the entry to add. For complete example code, refer to ServerRoot/plugins/slapd/slapi/examples/testpostop.c.
Before using the plug-in function as described here, set up the example suffix and register the plug-in as described under "Extending the Bind Operation", loading the configuration entry from the introductory comment of testpostop.c. Code Example 5-15 shows that configuration entry.
Adjust nsslapd-pluginPath to fit how the product is installed on your system. Notice the value of nsslapd-pluginType: postoperation.
The testpostop_add() function logs the DN of the added entry and also writes the entry to a log created by the plug-in, called changelog.txt. The location of changelog.txt depends on the platform, as shown in Code Example 5-16. The changelog.txt file managed by the plug-in is not related to other change logs managed by Directory Server.
After activating the plug-in, add Quentin's entry as specified in "Prepending a String to an Attribute".
$ ldapmodify -a -p port -D "cn=directory manager" \
-w password -f quentin.ldifSearch ServerRoot/slapd-serverID/logs/errors for the log message. Not including housekeeping information:
Added entry (uid=qcubbins,ou=people,dc=example,dc=com)
Notice also the entry logged to changelog.txt, which resembles the LDIF added, except that it has time stamps and the clear text password attached for internal use as shown in Code Example 5-17.
Extending the Modify Operation
This section shows how to develop functionality called by Directory Server after a client modify operation. A pre-operation plug-in, not demonstrated here, can be found in ServerRoot/plugins/slapd/slapi/examples/testpreop.c. Refer to ServerRoot/plugins/slapd/slapi/examples/testpostop.c for the source code discussed here.
Before using the plug-in function as described here, set up Directory Server as described under "Logging the Entry to Add", making sure to add Quentin's entry.
The testpostop_mod() function logs the DN of the modified entry and also writes the entry to a log managed by the plug-in, called changelog.txt. The location of changelog.txt depends on the platform, as shown in the source code.
Code Example 5-18 performs the logging.
First, check that Quentin's entry is in the directory. Quentin's entry is specified in "Prepending a String to an Attribute".
After the plug-in is activated in Directory Server, modify Quentin's mail address.
Search ServerRoot/slapd-serverID/logs/errors for the log message. Minus housekeeping information:
Modified entry (uid=qcubbins,ou=people,dc=example,dc=com)
Notice also the information logged to changelog.txt as shown in Code Example 5-21.
Extending the Rename Operation
This section demonstrates functionality called by Directory Server after a client modify RDN operation. A pre-operation plug-in, not demonstrated here, can be found in ServerRoot/plugins/slapd/slapi/examples/testpreop.c. Refer to ServerRoot/plugins/slapd/slapi/examples/testpostop.c for the source code discussed here.
Before using the plug-in function as described here, set up Directory Server as described under "Logging the Entry to Add", making sure to add Quentin's entry.
The testpostop_modrdn() function logs the DN of the modified entry and also writes the entry to a log managed by the plug-in, called changelog.txt. The location of changelog.txt depends on the platform, as shown in the source code.
Code Example 5-22 performs the logging.
Check that Quentin's entry is in the directory as shown in Code Example 5-19.
Last weekend, Quentin decided to change his given name to Fred. His user ID is now fcubbins, so his entry must be renamed. With this plug-in activated in Directory Server, modify the entry.
Search ServerRoot/slapd-serverID/logs/errors for the log message. Not including housekeeping information:
Modrdn entry (uid=qcubbins,ou=people,dc=example,dc=com)
Notice also the information logged to changelog.txt as shown in Code Example 5-24.
Code Example 5-24    Sample changelog.txt Entry after Rename
time: 20020506184432
dn: uid=qcubbins,ou=people,dc=example,dc=com
changetype: modrdn
newrdn: uid=fcubbins
deleteoldrdn: 1
Extending the Delete Operation
This section shows how to develop functionality called by Directory Server after a client delete operation. A pre-operation plug-in, not demonstrated here, can be found in ServerRoot/plugins/slapd/slapi/examples/testpreop.c. Refer to ServerRoot/plugins/slapd/slapi/examples/testpostop.c for the source code discussed here.
Before using the plug-in function as described here, set up Directory Server as described under "Logging the Entry to Add", making sure to add Quentin's entry.
The testpostop_modrdn() function logs the DN of the modified entry and also writes the entry to a log managed by the plug-in, called changelog.txt. The location of changelog.txt depends on the platform, as shown in the source code.
Code Example 5-25 performs the logging.
First, check that Quentin's entry is in the directory as shown in Code Example 5-19.
Quentin's name may be Fred if you have renamed it as described in "Extending the Rename Operation".
Imagine last Thursday, Quentin shouted copious verbal abuse at a key customer, embarrassing his brother Bartholomew. As a result Quentin has just been fired from Example.com. With this plug-in activated in Directory Server, delete his entry.
$ ldapdelete -p port -D "cn=directory manager" -w password \
"uid=qcubbins,ou=People,dc=example,dc=com"Search ServerRoot/slapd-serverID/logs/errors for the log message. Not including housekeeping information:
Deleted entry (uid=qcubbins,ou=people,dc=example,dc=com)
Notice also the information logged to changelog.txt as shown in Code Example 5-26.
Code Example 5-26    Sample changelog.txt Entry after Deletion
time: 20020506185404
dn: uid=qcubbins,ou=people,dc=example,dc=com
changetype: delete
Intercepting Information Sent to the Client
This section demonstrates functionality called by Directory Server before sending a result code, a referral, or an entry to the client application. Refer to ServerRoot/plugins/slapd/slapi/examples/testpreop.c for the source code discussed here.
Code Example 5-27 logs the operation number and user DN.
Operation identifiers are defined in the plug-in header file ServerRoot/plugins/slapd/slapi/include/slapi-plugin.h. Search for SLAPI_OPERATION_*.
Before using the plug-in function as described here, set up the example suffix and register the plug-in as described under "Setting Up an Example Suffix" and "Registering the Plug-In".
With the plug-in activated in Directory Server, perform a search as Yolanda Yorgenson:
$ ldapsearch -L -p port -b "dc=example,dc=com" \
-D "uid=yyorgens,ou=people,dc=example,dc=com" -w yyorgens \
"(uid=bcubbins)"Search <>ServerRoot/slapd-serverID/logs/errors for the log messages. Minus housekeeping information, the first message reflects the bind result:
Operation: 1 User: uid=yyorgens,ou=people,dc=example,dc=com
The next message reflects the search:
Operation: 4 User: uid=yyorgens,ou=people,dc=example,dc=com
Inside plug-in functions, use slapi_op_get_type() to determine the type of an operation. Refer to the Sun ONE Directory Server Plug-In API Reference documentation on slapi_op_get_type() or to the plug-in header file ServerRoot/plugins/slapd/slapi/include/slapi-plugin.h for a list of operation types.