Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Web Development

Understanding LDAP


Mar99: Understanding LDAP

Basit is an Internet security architect at The Technical Resource Connection. He can be contacted at [email protected].


Directory services, which store and locate information about a system's services, users, and applications, are an essential part of any operating system. Information needed for day-to-day operation is available from such a repository. Unfortunately, every operating system provides its own mechanism of managing and retrieving information from its directory services. The Lightweight Directory Assistance Protocol (LDAP) is an Internet standard that has been embraced on UNIX and Windows NT and has won support from major vendors. Essentially, LDAP is a subset of the International Telecommunication Union's (ITU) X.500 standard. In this article, I'll examine LDAP and present examples of how it can be used.

Primarily intended as a front end to the X.500 directory services, the LDAP evolved from work at the University of Michigan (see http://www.umich.edu/~dirsvcs/ldap/index.html) and was eventually taken over by the Internet Engineering Task Force (IETF). At this writing, the current release of LDAP is Version 3, which is defined in RFC 2251 and several supporting RFCs. Most of the available implementations of LDAP are still based on Version 2, which is defined in RFC 1777.

LDAP addresses supply the same set of services currently provided by Novell's NDS, Solaris's NIS+, and Microsoft's Active Directory Server. It can also be used as a repository for such services as an enhanced security server that requires strong authentication on the part of the users. Under such conditions, LDAP servers can be used to store the user's digital certificates.

LDAP Directories

The basic unit of information inside the LDAP database is an entry. The complete LDAP database can be classified as a collection of entries. An entry is comprised of a Distinguished Name and a collection of name-value pairs. The Distinguished Name is unique throughout the database and serves as the exclusive pointer for that specific entry. The name-value pairs are used to store the information of interest for that entry. However, to explain the composition of an entry in more detail, it is essential to explore the two building blocks of the LDAP schemata -- attributes and object classes.

Attributes and object classes are defined in two schema files provided with the directory server. The attributes file provides the names of the different attributes that eventually reside inside the LDAP entries. An attribute is classified as one of five types:

  • Case Insensitive String (cis).
  • Case Exact String (ces).
  • Binary Data (bin).
  • Telephone Numbers (tel).
  • Distinguished Name (dn).

The types are self explanatory. The majority of the attributes belong to cis types. The case can be important under certain conditions, for which the less frequently used Case Exact String (ces) type is used. Binary Data (or bin) may be comprised of encrypted passwords, digital certificates, and JPEG photographs. Telephone numbers require special attention because spaces, parentheses, and hyphens are not supposed to change the meaning of the content. The dn attribute is the same as the one explained previously.

The second schema file defines the objectclasses that the directory understands. Each objectclass is comprised of several related attributes. The objectclass definition classifies its component attributes as either required or optional. The required attributes must be assigned a value if an entry expects to utilize an objectclass. The optional attributes can be included or omitted on an entry-by-entry basis.

An LDAP entry contains a Distinguished Name and a collection of name-value pairs. The name of the name-value pair comes from the schema file, while the value is populated by assigning it content. For example, a popular attribute is the common name, also called cn, that can be assigned the value of "Bill Merchant" and represented as cn: Bill Merchant to construct the name-value pair. An attribute can have more than one value assigned to it. Both cn: Bill Merchant and cn: William Merchant are legal within the same entry, for example.

The Distinguished Names, or the locators for any entry, are defined using a tree-like hierarchy. A typical Distinguished Name consists of three or four components, although that is not a restriction. Each component of the Distinguished Name is known as a "Relatively Distinguished Name." An example of a Distinguished Name might be dn: cn=Bill Merchant, ou=System Administration, o=MicroWidgets Inc, c=US.

The root of the tree is to the right and is represented by the c attribute that defines the country. At that point, a number of branches can exist, each representing an organization (the o attribute). The tree can branch off at that point to represent several organization units (the ou attribute) and each organizational unit can contain a number of persons identified by their common name (cn). The aforementioned example depicts just one possible form of information organization.

LDAP Operations

The majority of the operations conducted on an LDAP directory server are comprised of information searches. Other operations include adding new directory entries, modifying existing entries, and deleting directory entries. Listing One is an excerpt of test data that demonstrates this. (The complete test data file is available electronically; see "Resource Center," page 5.) Listing Two is source code for searching, adding, deleting, and modifying entries. The code requires the LDAP Java SDK from Netscape, available at http://developer.netscape.com/tech/ directory/ software/dirifc/ljdkl0b3.exe. A directory server is also needed, and a trial version can be downloaded from the University of Michigan or Netscape.

Listing One defines test data needed to populate the directory server. The data is formatted using LDIF (LDAP Data Interchange Format). The consecutive entries are separated by a blank line. The root Distinguished Name of the tree is defined as o=MicroWidgets Inc, c=US. Further entries define the organizational units and people who belong to them. The first entry requires extra attention, as it defines the people that are allowed to perform the modification operations. Usually, these permissions can be set in a more user-friendly manner through a user interface provided with the directory server. To test the source code, a new directory server needs to be created with a blank database and the LDIF file imported into it. This prepares the directory server for the operations performed by the source code.

Before compiling and running Listing Two, the attributes ldapHost and ldapPort must be updated to reflect the server host and port. A total of four methods are provided that follow a pattern similar to connect-authenticate-operate-disconnect. To maintain the flow of the person perusing the source code, no additional private methods were created for connection and authentication. The main() method can be used to call each one of the operations in the order shown.

The searchEntries() method connects to the LDAP server and performs a search to locate all the entries belonging to the organizational unit of Application Development. No authentication is required for this operation, as it does not alter any data. The search will retrieve a total of three entries (one for the ou and two for the persons belonging to it). It will print the Distinguished Name and all the attributes (name-value pairs) as a single string on the standard output. This is followed by the disconnection operation.

The addEntry() method connects and authenticates itself as a Distinguished Name that is authorized to modify the LDAP database. This is followed by defining a new Distinguished Name and constructing a number of new attributes. The attributes are successively added to an attribute set. The attribute set object is combined with the Distinguished Name to form the LDAPEntry object, which is added to the directory. If the searchEntries() method is executed again at this point, it will show the new entry.

The modifyEntry() method operates on an existing entry and alters two of its attributes. It adds a new attribute street and alters the value of an existing attribute (l) to Siberia (initially Yukon). The deleteEntry() method is the simplest of all. It deletes an entry from the directory server by referencing it with its Distinguished Name.

Enhanced Services

The schema provided with the directory server is sufficient for the base functionality expected from a directory server. However, it can be extended to contain user-defined attributes and objectclasses if needed by the applications. To extend the schema, one must modify the attributes file to contain the new attributes and their types. The new objectclasses that expect to utilize these attributes are added to the objectclass file. Any LDAP entry can utilize the extended schema at this point by including the new objectclass as part of its name-value pairs.

Replication is not defined as part of the LDAP protocol, but it is supported by most LDAP vendors. This is a useful feature, as it provides fault tolerance against the failure of a single directory server. Different LDAP servers can be set up as suppliers or consumers of either the complete LDAP database or any of the subtrees contained therein. The replication service is triggered at predefined intervals throughout the week in order to synchronize the directory servers working together. Other advantages of replication include local control of subtrees within an organization and faster response time from search operations.

Referral is an LDAP service that redirects an LDAP client to an LDAP server that is different from the one initially handling the request. This can happen when the client tries to modify an entry that has been replicated from another server. Since the source of such an entry is not the local server, the modification has to take place at the supplier before it can be reflected locally. A redirection can also take place if the client requests an entry that does not belong to the root Distinguished Name stored by the current tree.

DDJ

Listing One

dn: o=MicroWidgets Inc, c=USobjectclass: top
objectclass: organization
o: MicroWidgets Inc.
subtreeaci: +(&(privilege=write)(target=ldap:///self))
subtreeaci: +(&(privilege=write)(clientGroup=ldap:///cn=Directory 
                                 Administrators, o=MicroWidgets Inc, c=US))
subtreeaci: +(privilege=compare)
subtreeaci: +(|(privilege=search)(privilege=read))


</p>
dn: ou=System Administration, o=MicroWidgets Inc, c=US
objectclass: top
objectclass: organizationalunit
ou: System Administration


</p>
dn: ou=Application Development, o=MicroWidgets Inc, c=US
objectclass: top
objectclass: organizationalunit
ou: Application Development


</p>
dn: ou=Project Administration, o=MicroWidgets Inc, c=US
objectclass: top
objectclass: organizationalunit
ou: Project Administration


</p>
dn: cn=Directory Administrators, o=MicroWidgets Inc, c=US
cn: Directory Administrators
objectclass: top
objectclass: groupofuniquenames
uniquemember: cn=Bill Merchant, ou=System Administration, 
                                       o=MicroWidgets Inc, c=US
uniquemember: cn=David Builder, ou=Application Development, 
                                       o=MicroWidgets Inc, c=US


</p>
dn: cn=Bill Merchant, ou=System Administration, o=MicroWidgets Inc, c=US
cn: Bill Merchant
sn: Merchant 
givenname: Bill
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
ou: System Administration
l: Yukon
uid: bmerchant
mail: [email protected]
telephonenumber: +1 999 000 6084
facsimiletelephonenumber: +1 999 000 6138
userpassword: hackit
  .
  .
  .

Back to Article

Listing Two

package ldap.directory;

</p>
import java.lang.*;
import java.util.*;
import java.io.*;
import netscape.ldap.*;
    catch (LDAPException e){
        System.err.println("Not connected");
    }
}
public void addEntry()
{
    LDAPAttributeSet lds = null;
    LDAPEntry lde = null;
    LDAPConnection ldc = new LDAPConnection();
    try{
        ldc.connect(ldapHost, ldapPort);
    }
    catch (LDAPException e){
        System.err.println("Connection Error");
    }
    try{
        ldc.authenticate(authDN,authPW);
    }
    catch (LDAPException e){
        System.err.println(e.toString());
    }
    try{
        String newdn = "cn=Bobby Windsor, ou=Application Development, 
                   o=Microwidgets Inc, c=US";
        String[] attrlist= {"cn","sn","ou","objectclass","objectclass",
                   "objectclass","objectclass","uid","userpassword","mail"};
        String[] vallist={"Bobby Windsor","Windsor","Application Development",
                    "top","person","organizationalPerson","inetOrgPerson",
                    "bwindsor","tackit","[email protected]"};
        lds = new LDAPAttributeSet();
        for (int i=0;i<attrlist.length;i++){
            lds.add(new LDAPAttribute(attrlist[i],vallist[i]));
        }
        lde = new LDAPEntry(newdn,lds);
        ldc.add(lde);
    }
    catch (LDAPException e){
        System.err.println(e.toString());
    }
    try {
        ldc.disconnect();
    }
    catch (LDAPException e){
        System.err.println("Not connected");
    }
}
public void modifyEntry(){
    LDAPConnection ldc = new LDAPConnection();
    try{
        ldc.connect(ldapHost, ldapPort);
    }


</p>
public class DirectoryTests {
    
private String authDN = "cn=Bill Merchant,ou=System Administration, 
                                                o=Microwidgets Inc,c=US";
private String authPW = "hackit";
private String ldapHost = "r2d2.microwidgets.com";
private int ldapPort = 1389;


</p>
public void searchEntries(){
    LDAPSearchResults ldr = null;
    LDAPAttributeSet lds = null;
    LDAPEntry lde = null;
   
    LDAPConnection ldc = new LDAPConnection();
    try{
        ldc.connect(ldapHost, ldapPort);
    }
    catch (LDAPException e){
        System.err.println("Connection error");
    }
    try{
        ldr= ldc.search("o=MicroWidgets Inc, c=US",LDAPv2.SCOPE_SUB,
                          "(ou=Application Development)",null,false);
    }
    catch (LDAPException e){
        System.err.println(e.toString());
    }
    try{
        while (ldr.hasMoreElements()){
            lde=ldr.next();
            String ndn = lde.getDN();
            lds=lde.getAttributeSet();
            System.out.println("\n\n"+ndn+"\n");
            System.out.println(lds.toString());
        }
    }
    catch (LDAPReferralException e){
            //process referrals here
    }
    try {
        ldc.disconnect();
    }
    catch (LDAPException e){
        System.out.println("Error connecting");
    }
    try{
        ldc.authenticate(authDN,authPW);
    }
    catch (LDAPException e){
        System.err.println(e.toString());
    }
    try{
        String modDN= "cn=Bill Merchant,ou=System Administration,
                                              o=Microwidgets Inc,c=US";
        LDAPModificationSet ldm = new LDAPModificationSet();
        LDAPAttribute lda = new LDAPAttribute("l","Siberia");
        ldm.add(LDAPModification.REPLACE,lda);
        lda = new LDAPAttribute("street","Turnbury");
        ldm.add(LDAPModification.ADD,lda);
        ldc.modify(modDN,ldm);
    }
    catch (LDAPException e){
        System.err.println(e.toString());
    }
    try {
        ldc.disconnect();
    }
    catch (LDAPException e){
        System.err.println("Not connected");
    }
}
public void deleteEntry(){
    String deldn = "cn=Bobby Windsor, ou=Application Development, 
                                               o=Microwidgets Inc, c=US";
    LDAPConnection ldc = new LDAPConnection();
    try{
        ldc.connect(ldapHost, ldapPort);
   }
    catch (LDAPException e){
        System.err.println("Error connecting");
    }
    try{
        ldc.authenticate(authDN,authPW);
    }
    catch (LDAPException e){
        System.err.println(e.toString());
    }
    try{
        ldc.delete(deldn);
    }
    catch (LDAPException e){
        System.err.println(e.toString());
    }    
    try {
        ldc.disconnect();
        System.out.println("Deletion successful");
    }
    catch (LDAPException e){
        System.err.println("Not connected");
    }
}
public static void main(String[] args){
    GatewayTests gt = new GatewayTests();
    gt.searchEntries();
    //gt.addEntry();
    //gt.modifyEntry();
    //gt.deleteEntry();
    }
}

Back to Article

DDJ


Copyright © 1999, Dr. Dobb's Journal

Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.