This chapter covers plug-in API features for handling directory entries, attributes, attribute values, and distinguished names (DNs). This chapter also deals with converting entries to and from LDAP Data Interchange Format (LDIF) strings, and checking whether entries comply with LDAP schema.
Code excerpts used in this chapter can be found in the install-path/examples/entries.c sample plug-in and the install-path/examples/dns.c sample plug-in.
See Chapter 16, Function Reference, Part I for information about the plug-in API functions used in this chapter.
This chapter covers the following topics:
This section demonstrates how to create an entry in the directory. Create a new entry by allocating space, and creating a completely new entry. Alternatively, create a new entry by duplicating an existing entry and modifying its values.
Create a completely new entry by using slapi_entry_alloc() to allocate the memory that is required, as shown in the following example.
#include "slapi-plugin.h" int test_ldif() { Slapi_Entry * entry = NULL; /* Entry to hold LDIF */ /* Allocate the Slapi_Entry structure. */ entry = slapi_entry_alloc(); /* Add code that fills the Slapi_Entry structure. */ /* Add code that uses the Slapi_Entry structure. */ /* Release memory allocated for the entry. */ slapi_entry_free(entry); return (0); }
Create a copy of an entry with slapi_entry_dup().
This section shows how to use the plug-in API to convert entries that are represented in LDIF to Slapi_Entry structures. This section also shows how to convert Slapi_Entry structures to LDIF strings.
LDIF files appear as a series of human-readable representations of directory entries, and optionally access control instructions. Entries that are represented in LDIF start with a line for the distinguished name. The LDIF representation continues with optional lines for the attributes. The syntax is shown in the following example.
dn:[:] dn-value\n [attribute:[:] value\n] [attribute:[:] value\n] [single-space continued-value\n]*
A double colon, ::, indicates that the value is base64-encoded. Base64-encoded values can, for example, have a line break in the midst of an attribute value.
As shown in the preceding example, LDIF lines can be folded by leaving a single space at the beginning of the continued line.
Sample LDIF files can be found in install-path/ldif/.
Refer to Chapter 13, Directory Server LDIF and Search Filters, in Sun Java System Directory Server Enterprise Edition 6.3 Reference for details on LDIF syntax.
You can achieve this type of conversion by using slapi_str2entry(). The function takes as its two arguments the string to convert and an int holding flag of the form SLAPI_STR2ENTRY_* in slapi-plugin.h. The function returns a pointer to a Slapi_Entry if successful, NULL otherwise, as shown in the following example.
#include "slapi-plugin.h" #define LDIF_STR "dn: dc=example,dc=com\nobjectclass: \ top\nobjectclass: domain\ndc: example\n" int test_ldif() { char * ldif = NULL; /* Example LDIF string */ Slapi_Entry * entry = NULL; /* Entry to hold LDIF */ char * str = NULL; /* String to hold entry */ int len; /* Length of entry as LDIF */ /* LDIF to Slapi_Entry */ entry = slapi_entry_alloc(); ldif = slapi_ch_strdup(LDIF_STR); entry = slapi_str2entry(ldif, SLAPI_STR2ENTRY_ADDRDNVALS); slapi_ch_free_string(&ldif); if (entry == NULL) return (-1); /* Slapi_Entry to LDIF */ str = slapi_entry2str(entry, &len); if (str == NULL) return (-1); slapi_log_info_ex( SLAPI_LOG_INFO_AREA_PLUGIN, SLAPI_LOG_INFO_LEVEL_DEFAULT, SLAPI_LOG_NO_MSGID, SLAPI_LOG_NO_CONNID, SLAPI_LOG_NO_OPID, "test_ldif in test-entries plug-in", "\nOriginal entry:\n%sEntry length: %d\n", str, len ); slapi_entry_free(entry); return (0); }
Here, SLAPI_STR2ENTRY_ADDRDNVALS adds any missing relative distinguished name (RDN) values, as specified in slapi-plugin.h where supported flags for slapi_str2entry() are listed.
You can achieve this type of conversion by using slapi_entry2str(). This function takes as its two arguments the entry to convert and an int to hold the length of the string that is returned. The function returns a char * to the LDIF if successful, NULL otherwise, as shown in Example 38–3.
This section demonstrates how to iterate through real attributes of an entry. You can use this technique when looking through the list of attributes that belong to an entry. If you know that the entry contains a particular attribute, slapi_entry_attr_find() returns a pointer to the attribute directly.
Real attributes contrast with virtual attributes generated by Directory Server for advanced entry management with roles and Class of Service (CoS).
When working with virtual attributes such as attributes for CoS, you can use slapi_vattr_list_attrs(). This function provides the complete list of real attributes as well as virtual attributes that belong to an entry.
Iteration through attribute values can be done using slapi_attr_first_value_const() and slapi_attr_next_value_const() together as shown in the following example.
#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; }
This section demonstrates how to add and remove attributes and their values.
If the attribute name and value are strings, you might be able to use slapi_entry_add_string(). The following example builds an entry first by setting the DN, then by adding attributes with string values.
#include "slapi-plugin.h" int test_create_entry() { Slapi_Entry * entry = NULL; /* Original entry */ entry = slapi_entry_alloc(); slapi_entry_set_dn(entry, slapi_ch_strdup("dc=example,dc=com")); slapi_entry_add_string(entry, "objectclass", "top"); slapi_entry_add_string(entry, "objectclass", "domain"); slapi_entry_add_string(entry, "dc", "example"); /* Add code using the entry you created..... */ slapi_entry_free(entry); return (0); }
When working with values that already exist or with values that are not strings, you can use the following functions
Use slapi_entry_add_values_sv() to add new values to an attribute of an entry. This function returns an error when duplicates of the new values already exist.
Use slapi_entry_attr_merge_sv() to add new values to the existing values of a multivalued attribute. This function does not return an error when duplicates of the new values already exist.
The following example demonstrates slapi_entry_attr_merge_sv().
#include "slapi-plugin.h" int test_ldif() { Slapi_Entry * entry = NULL; /* Entry to hold LDIF */ Slapi_Value * values[2]; /* Attribute values */ entry = slapi_entry_alloc(); /* Add code to transform an LDIF representation into an entry.*/ /* Add a description by setting the value of the attribute. * Although this is overkill when manipulating string values, * it can be handy when manipulating binary values. */ values[0] = slapi_value_new_string("Description for the entry."); values[1] = NULL; if (slapi_entry_attr_merge_sv(entry,"description",values) != 0) /* Merge did not work if processing arrives here. */ ; slapi_entry_free(entry); return (0); }
If the attribute exists in the entry, slapi_entry_attr_merge_sv() merges the specified values into the set of existing values. If the attribute does not exist, slapi_entry_attr_merge_sv() adds the attribute with the specified values.
Remove attribute values with slapi_entry_delete_string() or slapi_entry_delete_values_sv().
This section demonstrates how to check that an entry is valid with respect to the directory schema known to Directory Server. Verify schema compliance for an entry with slapi_entry_check(). The two arguments of the function are a pointer to a parameter block and a pointer to the entry, as shown in the following example.
#include "slapi-plugin.h" int test_create_entry() { Slapi_Entry * entry = NULL; /* Original entry */ Slapi_Entry * ecopy = NULL; /* Copy entry */ entry = slapi_entry_alloc(); /* Add code to fill the entry, setting the DN and attributes. */ ecopy = slapi_entry_dup(entry); slapi_entry_set_dn(ecopy, slapi_ch_strdup("dc=example,dc=org")); slapi_entry_add_string(ecopy, "description", "A copy of the orig."); /* Does the resulting copy comply with the schema? */ if (slapi_entry_schema_check(NULL, ecopy) == 0) { /* Resulting entry does comply. */ ; } else { /* Resulting entry does not comply. */ ; } slapi_entry_free(entry); slapi_entry_free(ecopy); return (0); }
Notice that the parameter block pointer argument is NULL. Leave the parameter block pointer argument NULL in most cases. When the plug-in is used in a replicated environment, you can use the argument to prevent schema compliance verification for replicated operations.
This section demonstrates how to work with distinguished names (DNs) to do the following:
Determine the parent DN of a given DN
Determine the suffix DN with which the entry DN is associated
Identify whether a root suffix is served by the back end
Set the DN of the entry
Normalize the DN of the entry for comparison with other entries
Determine whether the entry DN is the DN of the directory superuser
Use the slapi_dn_parent() function to return the parent and slapi_dn_beparent() to return the suffix associated with the entry, as shown in the following example.
#include "slapi-plugin.h" int test_dns(Slapi_PBlock * pb) { char * bind_DN; /* DN being used to bind */ char * parent_DN; /* DN of parent entry */ char * suffix_DN; /* DN of suffix entry */ int connId, opId, rc = 0; long msgId; rc |= slapi_pblock_get(pb, SLAPI_BIND_TARGET, &bind_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); if (rc != 0) return (-1); /* Get the parent DN of the DN being used to bind. */ parent_DN = slapi_dn_parent(bind_DN); slapi_log_info_ex( SLAPI_LOG_INFO_AREA_PLUGIN, SLAPI_LOG_INFO_LEVEL_DEFAULT, msgId, connId, opId, "test_dns in test-dns plug-in", "Parent DN of %s: %s\n", bind_DN, parent_DN ); /* Get the suffix DN of the DN being used to bind. */ suffix_DN = slapi_dn_beparent(pb, bind_DN); if (suffix_DN != NULL) { slapi_log_info_ex( SLAPI_LOG_INFO_AREA_PLUGIN, SLAPI_LOG_INFO_LEVEL_DEFAULT, msgId, connId, opId, "test_dns in test-dns plug-in", "Suffix for user (%s) is (%s).\n", bind_DN, suffix_DN ); } return rc; }
Notice that the suffix and parent DNs can be the same if the tree is not complex. For example, if the bind_DN is uid=bjensen,ou=People,dc=example,dc=com, the parent is ou=People,dc=example,dc=com. In this case, the parent is the same as the suffix.
Use slapi_dn_isbesuffix() function which takes as its two arguments a parameter block and a char * to the root suffix DN, as demonstrated in the following example.
#include "slapi-plugin.h" int test_dns(Slapi_PBlock * pb) { char * bind_DN; /* DN being used to bind */ char * parent_DN; /* DN of parent entry */ char * suffix_DN; /* DN of suffix entry */ slapi_pblock_get(pb, SLAPI_BIND_TARGET, &bind_DN); /* Get the suffix DN of the DN being used to bind. */ suffix_DN = slapi_dn_beparent(pb, bind_DN); /* Climb the tree to the top suffix and check if it is local. */ while (suffix_DN != NULL && !slapi_dn_isbesuffix(pb, suffix_DN)) { suffix_DN = slapi_dn_parent(suffix_DN); } if (suffix_DN != NULL) { /* Suffix is served locally. */ ; } else { /* Suffix is not served locally. */ ; } return 0; }
Notice the use of slapi_dn_isbesuffix() to move up the tree. Notice also that slapi_dn_parent() keeps working away at the DN until the DN that is left is NULL. The parent does not necessarily correspond to an actual entry.
To get and set entry DNs, use slapi_entry_get_dn() and slapi_entry_set_dn(), respectively. Verifying Schema Compliance for an Entry demonstrates how to use slapi_entry_set_dn() to change the DN of a copy of an entry. Notice the use of slapi_ch_strdup() to ensure that the old DN in the copy is not used.
Before comparing DNs, you can normalize the DNs to prevent the comparison from failing due to extra white space or different capitalization. You can normalize a DN with slapi_dn_normalize() and slapi_dn_normalize_case(), as shown in the following example.
#include "slapi-plugin.h" int test_norm() { char * test_DN; /* Original, not normalized */ char * copy_DN; /* Copy that is normalized. */ test_DN = "dc=Example, dc=COM";/* Prior to normalization...*/ /* When normalizing the DN with slapi_dn_normalize() and * slapi_dn_normalize_case(), the DN is changed in place. * Use slapi_ch_strdup() to work on a copy. */ copy_DN = slapi_ch_strdup(test_DN); copy_DN = slapi_dn_normalize_case(copy_DN); return 0; }
The function slapi_dn_normalize_case() works directly on the char * DN passed as an argument. This prepares the string for “case ignore matching” as specified, but not defined, for X.500.
Use slapi_ch_strdup() to make a copy first if you do not want to modify the original.
Answer this question with slapi_dn_isroot(). The bind for the directory superuser, however, as for anonymous users, is handled as a special case. For such users, the server front end handles the bind before calling preoperation bind plug-ins.