Sun Java logo     Previous      Contents      Index      Next     

Sun logo
Sun Java(TM) System Directory Server 5 2004Q2 Plug-In Developer's Guide 

Chapter 4
Working With Entries

This chapter covers plug-in API features for handling directory entries, attributes, attribute values, and Distinguished Names (DNs). It also deals with converting entries to and from LDAP Data Interchange Format (LDIF) strings, and with checking whether entries comply with LDAP schema.

Code excerpts used in this chapter can be found in the ServerRoot/plugins/slapd/slapi/examples/entries.c sample plug-in and the ServerRoot/plugins/slapd/slapi/examples/dns.c sample plug-in.

Refer to the Directory Server Plug-In Developer’s Reference for complete reference information concerning the plug-in API functions used in this chapter.


Creating Entries

This section demonstrates how to create a new entry in the directory. Create a new entry either by allocating space and creating a wholly new entry, or by duplicating an existing entry and modifying its values.

New Entries

Create a wholly new entry using slapi_entry_alloc() to allocate the memory required, as shown in Code Example 4-1.

Code Example 4-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);

}

Copies of Entries

Create a copy of an entry with slapi_entry_dup() as shown in Code Example 4-2.

Code Example 4-2  Copying an Existing Entry (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 that fills the new entry before making a copy.    */

    ecopy = slapi_entry_dup(entry);

    /* Add code that uses the Slapi_Entry structure.              */

    /* Release memory allocated for the entries.                  */

    slapi_entry_free(entry);

    slapi_entry_free(ecopy);

    return (0);

}


Converting To and From LDIF Representations

This section shows you how to use the plug-in API to convert from entries represented in LDIF to Slapi_Entry structures and from Slapi_Entry structures to LDIF strings.

LDIF files appear as a series of human-readable representations of directory entries, optionally with access control instructions as well. Entries represented in LDIF start with a line for the distinguished name, and continue with optional lines for the attributes as shown in Code Example 4-3.

Code Example 4-3  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 base-64 encoded. This makes it possible, for example, to have a line break in the midst of an attribute value.

As shown in Code Example 4-3, LDIF lines can be folded by leaving a single space at the beginning of the continued line.

Sample LDIF files can be found under ServerRoot/slapd-serverID/ldif/. Code Example 4-4 shows two entries represented in LDIF.

Code Example 4-4  LDIF Representation of Two Entries (Example-Plugin.ldif

dn: ou=People,dc=example,dc=com

objectclass: top

objectclass: organizationalUnit

ou: People

aci: (target = "ldap:///ou=People,dc=example,dc=com")

(targetattr = "userpassword")(version 3.0; acl

"Allow people to change their password";

allow(write)(userdn = "ldap:///self");)

dn: uid=yyorgens,ou=People,dc=example,dc=com

objectclass: top

objectclass: person

objectclass: organizationalPerson

objectclass: inetOrgPerson

uid: yyorgens

givenName: Yolanda

sn: Yorgenson

cn: Yolanda Yorgenson

mail: yyorgens@example.com

userPassword: yyorgens

secretary: uid=bcubbins,ou=People,dc=example,dc=com

Refer to the Directory Server Plug-In Developer’s Reference for details on LDIF syntax.

Converting an LDIF String to a Slapi_Entry Structure

This can be done using slapi_str2entry(). The function takes as its two arguments the string to convert and an int holding flags of the form SLAPI_STR2ENTRY_* in slapi-plugin.h. It returns a pointer to a Slapi_Entry if successful, NULL otherwise, as in Code Example 4-5.

Code Example 4-5  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

This can be done using slapi_entry2str(). This function takes as its two arguments the entry to convert and an int to hold the length of the string returned. It returns a char * to the LDIF if successful, NULL otherwise, as in Code Example 4-5.


Getting Entry Attributes and Attribute Values

This section demonstrates how to iterate through real attributes of an entry. Real attributes contrast with virtual attributes generated by Directory Server for advanced entry management using roles and Class of Service (CoS). You may use this technique when looking through the list of attributes belonging to an entry. If you know the entry contains a particular attribute, slapi_entry_attr_find() returns a pointer to the attribute directly.

Iteration through attribute values can be done using slapi_attr_first_value() and slapi_attr_next_value() together as shown in Code Example 4-6.

Code Example 4-6  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 * value;               /* Attribute values        */

    int         i, rc = 0;

    char        * tmp;                 /* Holder for new desc.    */

    const char  * string;              /* Holder for current desc.*/

    rc |= slapi_pblock_get(pb, SLAPI_ADD_ENTRY, &entry);

    rc |= slapi_entry_attr_find(entry, "description", &attribute);

    if (rc == 0) {                 /* Found a description, so...  */

        for (                          /* ...loop for value...    */

            i = slapi_attr_first_value(attribute, &value);

            i != -1;

            i = slapi_attr_next_value(attribute, i, &value)

        ) {                            /* ...prepend "ADD ".      */

            string = slapi_value_get_string(value);

            tmp    = slapi_ch_malloc(5+strlen(string));

            strcpy(tmp, "ADD ");

            strcat(tmp+4, string);

            slapi_value_set_string(value, tmp);

            slapi_ch_free((void **)&tmp);

        }

    }

    return 0;

}

When working with virtual attributes such as those used for CoS, you may use slapi_vattr_list_attrs() to obtain the complete list of both real and virtual attributes belonging to an entry.


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 may be able to use slapi_entry_add_string(), as in Code Example 4-7 that builds an entry by first setting the DN, then adding attributes with string values.

Code Example 4-7  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 are not strings, you may use slapi_entry_add_values_sv() to add new values to an attribute of an entry, or slapi_entry_attr_merge_sv() to add new values to the existing values of a multi-valued attribute. The function slapi_entry_add_values_sv() returns an error when duplicates of the new values already exist; slapi_entry_attr_merge_sv() does not. Code Example 4-8 demonstrates slapi_entry_attr_merge_sv().

Code Example 4-8  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 we arrive 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 using 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 using slapi_entry_check(). Its two arguments are a pointer to a parameter block and a pointer to the entry, as shown in Code Example 4-9.

Code Example 4-9  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 our 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 the parameter block pointer argument is NULL. Leave the parameter block pointer argument NULL unless the plug-in is used in a replicated environment and you do not want schema compliance verification carried out for replicated operations.


Handling Entry Distinguished Names (DNs)

This section demonstrates how to work with Distinguished Names to:

Getting the Parent and Suffix DNs

This can be done using slapi_dn_parent(). This function returns the parent, whereas slapi_dn_beparent() returns the suffix associated with the entry, as shown in Code Example 4-10.

Code Example 4-10  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 the suffix and parent DNs may be the same if the tree is shallow. For example, if the bind_DN here is uid=yyorgens,ou=People,dc=example,dc=com, then the parent is ou=People,dc=example,dc=com, and so is the suffix.

Checking Whether a Suffix is Served Locally

This can be done using slapi_dn_isbesuffix(), which takes as its two arguments a parameter block and a char * to the root suffix DN, as demonstrated in Code Example 4-11.

Code Example 4-11  Checking 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 we use slapi_dn_isbesuffix() to climb the tree. Notice also that slapi_dn_parent() keeps gnawing away at the DN until it is NULL. The parent does not necessarily correspond to an actual entry.

Getting and Setting Entry DNs

This can be done using slapi_entry_get_dn() and slapi_entry_set_dn(). Code Example 4-9 demonstrates changing the DN of a copy of an entry, using slapi_entry_set_dn(). Notice the use of slapi_ch_strdup() to ensure the old DN in the copy is not used.

Normalizing a DN

Before comparing DNs, you may want to normalize the DNs to prevent the comparison from failing due to extra white space or different capitalization. You may normalize a DN using slapi_dn_normalize(), or slapi_dn_normalize_case() as shown in Code Example 4-12.

Code Example 4-12  Normalizing a DN (dns.c

#include "slapi-plugin.h"

int

test_norm()

{

    char * test_DN;                    /* Original, not normalized */

    char * copy_DN;                    /* Copy that we normalize   */

    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.

Is This the Directory Manager?

Answer this question using 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 pre-operation bind plug-ins. Refer to Chapter 6, "Handling Authentication" for more information.



Previous      Contents      Index      Next     


Copyright 2004 Sun Microsystems, Inc. All rights reserved.