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 is described independently in Chapter 7, Handling Authentication Using Plug-Ins.
This chapter cover the following topics:
When processing client requests and when sending information to clients, Directory Server calls preoperation and postoperation plug-in functions.
Preoperation plug-ins are called before a client request is processed. Use preoperation plug-ins to validate attribute values, and to add to and delete from attributes that are provided in a request, for example.
Postoperation plug-ins are called after a client request has been processed and all results have been sent to the client, whether or not the operation completes successfully. Use postoperation plug-ins to send alerts, or to send alarms. Also use postoperation plug-ins to perform auditing work, or to perform cleanup work.
Directory Server calls preoperation and postoperation plug-ins only when an external client is involved. Internally, Directory Server also performs operations that no external client has requested.
To make this possible, Directory Server distinguishes between external operations and internal operations. External operations respond to incoming client requests. Internal operations are actions that are performed without a corresponding client request.
Several operations support the use of preoperation and postoperation plug-in functions. Operations include abandon, add, bind, compare, delete, modify, modify RDN, search, send entry, send referral, send result, and unbind. The plug-ins function regardless of the 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 preoperation and postoperation plug-in functions. slapi_pblock_set() is described in Part II, Directory Server Plug-In API Reference.
Preoperation plug-in registrations use IDs of the form SLAPI_PLUGIN_PRE_operation_FN. Postoperation registrations use IDs of the form SLAPI_PLUGIN_POST_operation_FN. Here, 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.
Preoperation and postoperation plug-ins can also register functions to run at Directory Server startup, using SLAPI_PLUGIN_START_FN, and at Directory Server shutdown, using SLAPI_PLUGIN_STOP_FN.
Part II, Directory Server Plug-In API Reference describes these IDs. Refer also to install-path/include/slapi-plugin.h for the complete list of IDs used at build time.
The following example demonstrates how the functions are registered with Directory Server by using the appropriate registration IDs.
#include "slapi-plugin.h" Slapi_PluginDesc postop_desc = { "test-postop", /* plug-in identifier */ "Sun Microsystems, Inc.", /* vendor name */ "6.0", /* plug-in revision number */ "Sample post-operation plug-in" /* plug-in description */ }; static Slapi_ComponentId * postop_id; /* Used to set log */ /* Register the plug-in with the server. */ #ifdef _WIN32 __declspec(dllexport) #endif int testpostop_init(Slapi_PBlock * pb) { int rc = 0; /* 0 means success */ rc |= slapi_pblock_set( /* Plug-in API version */ pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_CURRENT_VERSION ); rc |= slapi_pblock_set( /* Plug-in description */ pb, SLAPI_PLUGIN_DESCRIPTION, (void *) &postop_desc ); rc |= slapi_pblock_set( /* Open log at startup */ pb, SLAPI_PLUGIN_START_FN, (void *) testpostop_set_log ); rc |= slapi_pblock_set( /* Post-op add function */ pb, SLAPI_PLUGIN_POST_ADD_FN, (void *) testpostop_add ); rc |= slapi_pblock_set( /* Post-op modify function */ pb, SLAPI_PLUGIN_POST_MODIFY_FN, (void *) testpostop_mod ); rc |= slapi_pblock_set( /* Post-op delete function */ pb, SLAPI_PLUGIN_POST_DELETE_FN, (void *) testpostop_del ); rc |= slapi_pblock_set( /* Post-op modrdn function */ pb, SLAPI_PLUGIN_POST_MODRDN_FN, (void *) testpostop_modrdn ); rc |= slapi_pblock_set( /* Close log on shutdown */ pb, SLAPI_PLUGIN_CLOSE_FN, (void *) testpostop_free_log ); /* Plug-in identifier is required for internal search. */ rc |= slapi_pblock_get(pb, SLAPI_PLUGIN_IDENTITY, &postop_id); return (rc); }
In addition to its postoperation functions, the plug-in that is shown in the preceding example registers a function. The plug-in opens a log file at startup. The plug-in closes the log file at shutdown. For details, refer to install-path/examples/testpostop.c.
Plug-in examples are located in install-path/examples/.
This section shows how to develop functions called by Directory Server before client bind operations.
Pre-bind plug-in functions are often used to handle extensions to authentication. Yet, you might have to account for special cases such as binds by the directory superuser and anonymous users. Sometimes, you have to account for multiple calls to the same preoperation or postoperation plug-in function.
If you have not done so already, set up a directory instance with a suffix, dc=example,dc=com, containing data loaded from a sample LDIF file, install-path/ds6/ldif/Example.ldif.
Create a new Directory Server instance.
For example:
$ dsadm create /local/ds Choose the Directory Manager password: Confirm the Directory Manager password: $ |
Start the new Directory Server instance.
For example:
$ dsadm start /local/ds Server started: pid=4705 $ |
Create a suffix called dc=example,dc=com.
For example, with long lines folded for the printed page:
$ dsconf create-suffix -h localhost -p 1389 dc=example,dc=com Enter "cn=directory manager" password: Certificate "CN=defaultCert, CN=hostname:1636" presented by the server is not trusted. Type "Y" to accept, "y" to accept just once, "n" to refuse, "d" for more details: Y $ |
Load the sample LDIF.
For example, with long lines folded for the printed page:
$ dsconf import -h localhost -p 1389 \ /opt/SUNWdsee/ds6/ldif/Example.ldif dc=example,dc=com Enter "cn=directory manager" password: New data will override existing data of the suffix "dc=example,dc=com". Initialization will have to be performed on replicated suffixes. Do you want to continue [y/n] ? y ## Index buffering enabled with bucket size 16 ## Beginning import job... ## Processing file "/opt/SUNWdsee/ds6/ldif/Example.ldif" ## Finished scanning file "/opt/SUNWdsee/ds6/ldif/Example.ldif" (160 entries) ## Workers finished; cleaning up... ## Workers cleaned up. ## Cleaning up producer thread... ## Indexing complete. ## Starting numsubordinates attribute generation. This may take a while, please wait for further activity reports. ## Numsubordinates attribute generation complete. Flushing caches... ## Closing files... ## Import complete. Processed 160 entries in 5 seconds. (32.00 entries/sec) Task completed (slapd exit code: 0). $ |
You can use Directory Service Control Center to perform this task. For more information, see the Directory Service Control Center online help.
The following example logs the bind authentication method. Refer to install-path/examples/testpreop.c for complete example code.
#include "slapi-plugin.h" int testpreop_bind(Slapi_PBlock * pb) { char * auth; /* Authentication type */ char * dn; /* Target DN */ int method; /* Authentication method */ int connId, opId, rc = 0; long msgId; /* Get target DN for bind and authentication method used. */ rc |= slapi_pblock_get(pb, SLAPI_BIND_TARGET, &dn); rc |= slapi_pblock_get(pb, SLAPI_BIND_METHOD, &method); 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); if (rc == 0) { switch (method) { case LDAP_AUTH_NONE: auth = "No authentication"; break; case LDAP_AUTH_SIMPLE: auth = "Simple authentication"; break; case LDAP_AUTH_SASL: auth = "SASL authentication"; break; default: auth = "Unknown authentication method"; break; } } else { return (rc); } /* Log target DN and authentication method info. */ slapi_log_info_ex( SLAPI_LOG_INFO_AREA_PLUGIN, SLAPI_LOG_INFO_LEVEL_DEFAULT, msgId, connId, opId, "testpreop_bind in test-preop plug-in", "Target DN: %s\tAuthentication method: %s\n", dn, auth ); return (rc); }
This plug-in function sets the auth message based on the authentication method. The function does nothing to affect the way Directory Server processes the bind.
If you have not already done so, build the example plug-in library and activate both plug-in informational logging and the example plug-in.
Build the plug-in.
Hint Use install-path/examples/Makefile or install-path/examples/Makefile64.
Configure Directory Server to log plug-in informational messages and load the plug-in.
Hint Use the commands specified in the comments at the outset of the plug-in source file.
Restart Directory Server.
$ dsadm restart instance-path |
Bind as Kirsten Vaughan (for example).
$ ldapsearch -h localhost -p 1389 -b "dc=example,dc=com" \ -D "uid=kvaughan,ou=people,dc=example,dc=com" -w bribery "(uid=*)" |
Search instance-path/logs/errors for the resulting message from the testpreop_bind() function.
If you ignore housekeeping information for the entry, output similar to this appears:
Target DN: uid=kvaughan,ou=people,dc=example,dc=com Authentication method: Simple authentication |
For a discussion of less trivial pre-bind plug-in functions, refer to Chapter 7, Handling Authentication Using Plug-Ins.
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 positive value, such as 1.
Directory Server follows the LDAP bind model. At minimum, the server authenticates the client. The server also sends a bind response to indicate the status of authentication. Refer to RFC 1777, Lightweight Directory Access Protocol, and RFC 45111, Lightweight Directory Access Protocol (v3), for details.
Lightweight Directory Access Protocol (v3) is the preferred protocol because Lightweight Directory Access Protocol (v2) is obsolete.
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.
This section shows how to develop functionality called by Directory Server before a client compare operation. The following example logs the target DN and attribute of the entry with which to compare values. For complete example code, refer to install-path/examples/testpreop.c.
#include "slapi-plugin.h" int testpreop_cmp(Slapi_PBlock * pb) { char * dn; /* Target DN */ char * attr_type; /* Type of attr to compare */ /* Attribute value could be lots of things, even a binary file. * Here, do not try to retrieve the value to compare. */ int connId, opId, rc = 0; long msgId; rc |= slapi_pblock_get(pb, SLAPI_COMPARE_TARGET, &dn); rc |= slapi_pblock_get(pb, SLAPI_COMPARE_TYPE, &attr_type); 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); if (rc == 0) { slapi_log_info_ex( SLAPI_LOG_INFO_AREA_PLUGIN, SLAPI_LOG_INFO_LEVEL_DEFAULT, msgId, connId, opId, "testpreop_cmp in test-preop plug-in", "Target DN: %s\tAttribute type: %s\n", dn, attr_type ); } return (rc); }
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. Thus, the value could be binary data such as a JPEG image. No attempt is made to write the value to the logs.
The following example shows the slapi_pblock_get() call used to obtain the attribute value.
#include "slapi-plugin.h" int my_compare_fn(Slapi_PBlock * pb) { int rc = 0; struct berval * attr_val; /* Obtain the attribute value from the parameter block */ rc |= slapi_pblock_get(pb, SLAPI_COMPARE_VALUE, &attr_val); if (rc != 0) { rc = LDAP_OPERATIONS_ERROR; slapi_send_ldap_result(pb, rc, NULL, NULL, 0, NULL); return 0; } /* Do something with the value here. */ return 0; }
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.
Try the example plug-in function using the ldapcompare tool installed with the Directory Server Resource Kit.
$ ldapcompare sn:Jensen uid=bjensen,ou=people,dc=example,dc=com comparing type: "sn" value: "Jensen" in entry "uid=bjensen,ou=people,dc=example,dc=com" compare TRUE |
The log entry in instance-path/logs/errors shows the following results, not including housekeeping information at the beginning of the log entry:
Target DN: uid=bjensen,ou=people,dc=example,dc=com Attribute type: sn
This section shows how to develop functionality called by Directory Server before and after a client add operation.
Example 39–10 prepends the string ADD to the description attribute of the entry to be added to the directory. For complete example code, refer to instance-path/examples/testpreop.c.
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.
To try the preoperation add function, add an entry for Quentin Cubbins who recently joined Example.com. Quentin’s LDIF entry, quentin.ldif, reads as shown in the following example.
dn: uid=qcubbins,ou=People,dc=example,dc=com objectclass: top objectclass: person objectclass: organizationalPerson objectclass: inetOrgPerson uid: qcubbins givenName: Quentin sn: Cubbins cn: Quentin Cubbins mail: qcubbins@example.com userPassword: qcubbins secretary: uid=bjensen,ou=People,dc=example,dc=com description: Entry for Quentin Cubbins |
The following example performs the prepend.
#include "slapi-plugin.h" int testpreop_add(Slapi_PBlock * pb) { Slapi_Entry * entry; /* Entry to add */ Slapi_Attr * attribute; /* Entry attributes */ Slapi_Value ** values; /* Modified, duplicate vals*/ int connId, opId, rc = 0; long msgId; /* Get the entry. */ rc |= slapi_pblock_get(pb, SLAPI_ADD_ENTRY, &entry); 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); /* Prepend ADD to values of description attribute. */ rc |= slapi_entry_attr_find(entry, "description", &attribute); if (rc == 0) { /* Found a description, so... */ int nvals, i, count = 0; const Slapi_Value * value; slapi_attr_get_numvalues(attribute, &nvals); values = (Slapi_Value **) slapi_ch_malloc((nvals+1)*sizeof(Slapi_Value *)); for ( /* ...loop for value... */ i = slapi_attr_first_value_const(attribute, &value); i != -1; i = slapi_attr_next_value_const(attribute, i, &value) ) { /* ...prepend "ADD ". */ const char * string; char * tmp; values[count] = slapi_value_dup(value); string = slapi_value_get_string(values[count]); tmp = slapi_ch_malloc(5+strlen(string)); strcpy(tmp, "ADD "); strcpy(tmp+4, string); slapi_value_set_string(values[count], tmp); slapi_ch_free((void **)&tmp); ++count; } values[count] = NULL; } else { /* entry has no desc. */ slapi_log_info_ex( SLAPI_LOG_INFO_AREA_PLUGIN, SLAPI_LOG_INFO_LEVEL_DEFAULT, msgId, connId, opId, "testpreop_add in test-preop plug-in", "Entry has no description attribute.\n" ); } rc = slapi_entry_attr_replace_sv(entry, "description", values); if (rc != 0) { slapi_log_info_ex( SLAPI_LOG_INFO_AREA_PLUGIN, SLAPI_LOG_INFO_LEVEL_DEFAULT, msgId, connId, opId, "testpreop_add in test-preop plug-in", "Description attribute(s) not modified.\n" ); } slapi_valuearray_free(&values); return 0; }
Add Quentin’s entry to the directory. For example, if the entry is in quentin.ldif, type:
$ ldapmodify -h localhost -p 1389 -a -f quentin.ldif \ -D uid=kvaughan,ou=people,dc=example,dc=com -w bribery adding new entry uid=qcubbins,ou=People,dc=example,dc=com $ |
At this point, search the directory for Quentin’s entry.
$ ldapsearch -h localhost -p 1389 -b dc=example,dc=com uid=qcubbins version: 1 dn: uid=qcubbins,ou=People,dc=example,dc=com objectClass: top objectClass: person objectClass: organizationalPerson objectClass: inetOrgPerson uid: qcubbins givenName: Quentin sn: Cubbins cn: Quentin Cubbins mail: qcubbins@example.com secretary: uid=bjensen,ou=People,dc=example,dc=com description: ADD Entry for Quentin Cubbins $ |
Notice the value of the description attribute.
Delete Quentin’s entry so you can use it again later as an example.
$ ldapdelete -D uid=kvaughan,ou=people,dc=example,dc=com -w bribery \ uid=qcubbins,ou=people,dc=example,dc=com |
Turn off the preoperation plug-in to avoid prepending ADD to all the entries that you add.
$ dsconf disable-plugin -h localhost -p 1389 "Test Preop" $ dsadm restart instance-path |
Logging the Entry to Add logs the entry to add. For complete example code, refer to instance-path/examples/testpostop.c.
Before using the plug-in function as described here, set up the example suffix and register the plug-in. See Extending the Bind Operation.
The testpostop_add() function logs the DN of the added entry and also writes the entry to a log created by the plug-in, changelog.txt. The location of changelog.txt depends on the platform, as shown in the following example. The changelog.txt file managed by the plug-in is not related to other change logs managed by Directory Server.
#include "slapi-plugin.h" int testpostop_add(Slapi_PBlock * pb) { char * dn; /* DN of entry to add */ Slapi_Entry * entry; /* Entry to add */ int is_repl = 0; /* Is this replication? */ int connId, opId, rc = 0; long msgId; rc |= slapi_pblock_get(pb, SLAPI_ADD_TARGET, &dn); rc |= slapi_pblock_get(pb, SLAPI_ADD_ENTRY, &entry); 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_IS_REPLICATED_OPERATION, &is_repl); if (rc != 0) return (rc); slapi_log_info_ex( SLAPI_LOG_INFO_AREA_PLUGIN, SLAPI_LOG_INFO_LEVEL_DEFAULT, msgId, connId, opId, "testpostop_add in test-postop plug-in", "Added entry (%s)\n", dn ); /* In general, do not interfere in replication operations. */ /* Log the DN and the entry to the change log file. */ if (!is_repl) write_changelog(_ADD, dn, (void *) entry, 0); return (rc); }
After activating the plug-in, add Quentin’s entry:
$ ldapmodify -a -D uid=kvaughan,ou=people,dc=example,dc=com \ -h localhost -p 1389 -w bribery -f quentin.ldif adding new entry uid=qcubbins,ou=People,dc=example,dc=com $ |
Search instance-path/logs/errors for the log message. If you ignore housekeeping information, you get the following message:
Added entry (uid=qcubbins,ou=people,dc=example,dc=com)
Notice also the entry logged to changelog.txt. The entry resembles the LDIF that was added, except that the change log has time stamps attached for internal use as shown in the following example.
time: 21120506172445 dn: uid=qcubbins,ou=people,dc=example,dc=com changetype: add objectClass: top objectClass: person objectClass: organizationalPerson objectClass: inetOrgPerson uid: qcubbins givenName: Quentin sn: Cubbins cn: Quentin Cubbins mail: qcubbins@example.com secretary: uid=bjensen,ou=People,dc=example,dc=com userPassword: qcubbins creatorsName: cn=Directory Manager modifiersName: cn=Directory Manager createTimestamp: 21120506152444Z modifyTimestamp: 21120506152444Z
This section shows how to develop functionality called by Directory Server after a client modify operation. A preoperation plug-in, not demonstrated here, can be found in install-path/examples/testpreop.c. Refer to install-path/examples/testpostop.c for the source code discussed here.
Before using the plug-in function as described here, set up Directory Server as described in 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, changelog.txt. The location of changelog.txt depends on the platform, as shown in the source code.
The following example shows the code that performs the logging.
#include "slapi-plugin.h" int testpostop_mod(Slapi_PBlock * pb) { char * dn; /* DN of entry to modify */ LDAPMod ** mods; /* Modifications to apply */ int is_repl = 0; /* Is this replication? */ int connId, opId, rc = 0; long msgId; rc |= slapi_pblock_get(pb, SLAPI_MODIFY_TARGET, &dn); rc |= slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &mods); 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_IS_REPLICATED_OPERATION, &is_repl); if (rc != 0) return (rc); slapi_log_info_ex( SLAPI_LOG_INFO_AREA_PLUGIN, SLAPI_LOG_INFO_LEVEL_DEFAULT, msgId, connId, opId, "testpostop_mod in test-postop plug-in", "Modified entry (%s)\n", dn ); /* In general, do not interfere in replication operations. */ /* Log the DN and the modifications made to the change log file. */ if (!is_repl) write_changelog(_MOD, dn, (void *) mods, 0); return (rc); }
First, check that Quentin’s entry is in the directory.
$ ldapsearch -h localhost -p 1389 -b dc=example,dc=com uid=qcubbins version: 1 dn: uid=qcubbins,ou=People,dc=example,dc=com objectClass: top objectClass: person objectClass: organizationalPerson objectClass: inetOrgPerson uid: qcubbins givenName: Quentin sn: Cubbins cn: Quentin Cubbins mail: qcubbins@example.com secretary: uid=bjensen,ou=People,dc=example,dc=com |
After the plug-in is activated in Directory Server, modify Quentin’s mail address.
$ ldapmodify -h localhost -p 1389 \ -D uid=kvaughan,ou=people,dc=example,dc=com -w bribery dn: uid=qcubbins,ou=People,dc=example,dc=com changetype: modify replace: mail mail: quentin@example.com ^D |
Search instance-path/logs/errors for the log message. If you ignore housekeeping information, you get the following message:
Modified entry (uid=qcubbins,ou=people,dc=example,dc=com)
Notice also the information logged to changelog.txt as shown in the following example.
time: 21120506181305 dn: uid=qcubbins,ou=people,dc=example,dc=com changetype: modify replace: mail mail: quentin@example.com - replace: modifiersname modifiersname: cn=Directory Manager - replace: modifytimestamp modifytimestamp: 21120506161305Z -
This section demonstrates functionality called by Directory Server after a client modify RDN operation. A preoperation plug-in, not demonstrated here, can be found in install-path/examples/testpreop.c. Refer to install-path/examples/testpostop.c for the source code discussed here.
Before using the plug-in function as described here, set up Directory Server as described in 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, changelog.txt. The location of changelog.txt depends on the platform, as shown in the source code.
The following example shows the code that performs the logging.
#include "slapi-plugin.h" int testpostop_modrdn( Slapi_PBlock *pb ) { char * dn; /* DN of entry to rename */ char * newrdn; /* New RDN */ int dflag; /* Delete the old RDN? */ int is_repl = 0; /* Is this replication? */ int connId, opId, rc = 0; long msgId; rc |= slapi_pblock_get(pb, SLAPI_MODRDN_TARGET, &dn); rc |= slapi_pblock_get(pb, SLAPI_MODRDN_NEWRDN, &newrdn); rc |= slapi_pblock_get(pb, SLAPI_MODRDN_DELOLDRDN, &dflag); 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_IS_REPLICATED_OPERATION, &is_repl); if (rc != 0) return (rc); slapi_log_info_ex( SLAPI_LOG_INFO_AREA_PLUGIN, SLAPI_LOG_INFO_LEVEL_DEFAULT, msgId, connId, opId, "testpostop_modrdn in test-postop plug-in", "Modrdn entry (%s)\n", dn ); /* In general, do not interfere in replication operations. */ /* Log the DN of the renamed entry, its new RDN, and the flag * indicating whether the old RDN was removed to the change log. */ if (!is_repl) write_changelog(_MODRDN, dn, (void *) newrdn, dflag); return (rc); }
Check that Quentin’s entry is in the directory as shown in Extending the Modify Operation.
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.
$ ldapmodify -D uid=kvaughan,ou=people,dc=example,dc=com -w bribery \ -h localhost -p 1389 dn: uid=qcubbins,ou=People,dc=example,dc=com changetype: modify replace: givenName givenName: Fred dn: uid=qcubbins,ou=People,dc=example,dc=com changetype: modify replace: mail mail: fcubbins@example.com dn: uid=qcubbins,ou=People,dc=example,dc=com changetype: modify replace: cn cn: Fred Cubbins dn: uid=qcubbins,ou=People,dc=example,dc=com changetype: modrdn newrdn: uid=fcubbins deleteoldrdn: 1 ^D |
Search instance-path/logs/errors for the log message. If you ignore housekeeping information, you get the following message:
Modrdn entry (uid=qcubbins,ou=people,dc=example,dc=com)
Notice also the information that is logged to changelog.txt.
time: 21120506184432 dn: uid=qcubbins,ou=people,dc=example,dc=com changetype: modrdn newrdn: uid=fcubbins deleteoldrdn: 1 |
This section shows how to develop functionality called by Directory Server after a client delete operation. A preoperation plug-in, not demonstrated here, can be found in install-path/examples/testpreop.c. Refer to install-path/examples/testpostop.c for the source code discussed here.
Before using the plug-in function as described here, set up Directory Server as described in 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, changelog.txt. The location of changelog.txt depends on the platform, as shown in the source code.
The following example shows the code that performs the logging.
#include "slapi-plugin.h" int testpostop_del( Slapi_PBlock *pb ) { char * dn; /* DN of entry to delete */ int is_repl = 0; /* Is this replication? */ int connId, opId, rc = 0; long msgId; rc |= slapi_pblock_get(pb, SLAPI_DELETE_TARGET, &dn); 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_IS_REPLICATED_OPERATION, &is_repl); if (rc != 0) return (rc); slapi_log_info_ex( SLAPI_LOG_INFO_AREA_PLUGIN, SLAPI_LOG_INFO_LEVEL_DEFAULT, msgId, connId, opId, "testpostop_del in test-postop plug-in", "Deleted entry (%s)\n", dn ); /* In general, do not interfere in replication operations. */ /* Log the DN of the deleted entry to the change log. */ if (!is_repl) write_changelog(_DEL, dn, NULL, 0); return (rc); }
First, check that Quentin’s entry is in the directory as shown in Extending the Modify Operation.
Quentin’s name might be Fred if you have renamed the entry as described in Extending the Rename Operation.
Imagine that Quentin shouted copious verbal abuse at a key customer causing Quentin to be fired from Example.com. With this plug-in activated in Directory Server, delete his entry.
$ ldapdelete -D uid=kvaughan,ou=people,dc=example,dc=com -w bribery \ uid=qcubbins,ou=People,dc=example,dc=com
Search instance-path/logs/errors for the log message. If you ignore housekeeping information, you get the following message:
Deleted entry (uid=qcubbins,ou=people,dc=example,dc=com)
Notice also the information logged to changelog.txt as shown in the following example.
time: 21120506185404 dn: uid=qcubbins,ou=people,dc=example,dc=com changetype: delete
This section demonstrates functionality called by Directory Server before sending a result code, a referral, or an entry to the client application. Refer to install-path/examples/testpreop.c for the source code discussed here.
The following example shows the code that logs the operation number and user DN.
#include "slapi-plugin.h" int testpreop_send(Slapi_PBlock * pb) { Slapi_Operation * op; /* Operation in progress */ char * connDn; /* Get DN from connection */ int connId, opId, rc = 0; long msgId; rc |= slapi_pblock_get(pb, SLAPI_OPERATION, &op); rc |= slapi_pblock_get(pb, SLAPI_CONN_DN, &connDn); rc |= slapi_pblock_get(pb, SLAPI_CONN_ID, &connId); rc |= slapi_pblock_get(pb, SLAPI_OPERATION_MSGID, &msgId); rc |= slapi_pblock_get(pb, SLAPI_OPERATION_ID, &opId); if (rc == 0) { slapi_log_info_ex( SLAPI_LOG_INFO_AREA_PLUGIN, SLAPI_LOG_INFO_LEVEL_DEFAULT, msgId, connId, opId, "testpreop_send in test-preop plug-in", "Operation: %d\tUser: %s\n", slapi_op_get_type(op), connDn ); } slapi_ch_free_string(&connDn); return (rc); }
Operation identifiers are defined in the plug-in header file install-path/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. See Extending the Bind Operation and “To register the Plug-in”, as described previously.
With the plug-in activated in Directory Server, perform a search as Kirsten Vaughan:
$ ldapsearch -h localhost -p 1389 -b dc=example,dc=com \ -D uid=kvaughan,ou=people,dc=example,dc=com -w bribery \ uid=bcubbins |
Search instance-path/logs/errors for the log messages. Minus housekeeping information, the first message reflects the bind result:
Operation: 1 User: uid=kvaughan,ou=people,dc=example,dc=com
The next message reflects the search:
Operation: 4 User: uid=kvaughan,ou=people,dc=example,dc=com
Inside plug-in functions, use slapi_op_get_type() to determine the type of an operation. Refer to Part II, Directory Server Plug-In API Reference for info about slapi_op_get_type(), or see the plug-in header file install-path/include/slapi-plugin.h for a list of operation types.