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

Chapter 5 Working With Entries Using Plug-Ins

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:

Creating Entries

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.

Creating New Entries

Create a completely new entry by using slapi_entry_alloc() to allocate the memory that is required, as shown in the following example.


Example 5–1 Creating a New Entry (entries.c)

#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);
}

Creating Copies of Entries

Create a copy of an entry with slapi_entry_dup().

Converting To and From LDIF Representations

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.


Example 5–2 LDIF Syntax Representing an Entry

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.2 Reference for details on LDIF syntax.

Converting an LDIF String to a Slapi_Entry Structure

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.


Example 5–3 Converting To and From LDIF Strings (entries.c)

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

Converting a Slapi_Entry Structure to an LDIF String

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.

Getting Entry Attributes and Attribute Values

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.


Note –

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.


Example 5–4 Iterating Through Attributes of an Entry (testpreop.c)

#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;
}

Adding and Removing Attribute Values

This section demonstrates how to add and remove attributes and their values.

Adding Attribute 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.


Example 5–5 Adding String Attribute Values (entries.c)

#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

The following example demonstrates slapi_entry_attr_merge_sv().


Example 5–6 Merging Attribute Values (entries.c)

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

Removing Attribute Values

Remove attribute values with slapi_entry_delete_string() or slapi_entry_delete_values_sv().

Verifying Schema Compliance for an Entry

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.


Example 5–7 Checking Schema Compliance (entries.c)

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

Handling Entry Distinguished Names

This section demonstrates how to work with distinguished names (DNs) to do the following:

Getting the Parent and Suffix DNs

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.


Example 5–8 Determining the Parent and Suffix of an Entry (dns.c)

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

Determining Whether a Suffix Is Served Locally

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.


Example 5–9 Determining Whether a Suffix Is Local (dns.c)

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

Getting and Setting Entry DNs

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.

Normalizing a DN

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.


Example 5–10 Normalizing a DN (dns.c)

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


Note –

Use slapi_ch_strdup() to make a copy first if you do not want to modify the original.


Is the User the Directory Manager?

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.