ONLamp.com    
 Published on ONLamp.com (http://www.onlamp.com/)
 See this if you're having trouble printing code examples


Building an Address Book with OpenLDAP

by Dustin Puryear
03/27/2003

LDAP Comes to the Rescue, Again

There has been a lot of buzz in the past few years about using the Lightweight Directory Access Protocol (LDAP) for centralizing system and network information, providing cross-platform user account databases, and even for creating a single repository of printer definitions and configuration information. All of these uses are truly incredible, and they only go to show you the level of flexibility available with LDAP. Most of these examples neglect an important and obvious example--using LDAP for a company-wide address book.

Why is a centralized address book important, and how can it be used? For starters, I think just about every consultant has walked onto a site--even a large one--where everyone has contact information stored locally in their contact management software. What's wrong with that? Nothing, if you don't mind losing the ability to update contact information effortlessly across the entire company.

The only requirement here is for contact software or email clients that support LDAP. Most email software does, including UNIX- and Linux-based software, such as Netscape Mail and Evolution, and Windows clients, such as Outlook. The trick is usually not in finding the right software, but in ensuring that you use the right attributes that will be supported universally by those clients. This article will suggest appropriate attributes for your address book and will provide examples of configuring your email clients to support your LDAP-based directory.

You must also have a working LDAP server for this project. I will be using OpenLDAP 2.0.25 under FreeBSD for the examples, although any LDAP server should do. (For information on the license governing OpenLDAP--the OpenLDAP Public License--please refer to the OpenLDAP web site.)

Please note that this article is not an in-depth review of LDAP, nor does it cover OpenLDAP configuration extensively. For that, I would suggest you read "An Introduction to LDAP," by Luke Kanies, and Aeleen Frisch's LDAP article in "Top Five Open Source Packages for System Administrators." To ensure common ground, I assume you have slapd.conf configured similar to the following:

include     /usr/local/etc/openldap/schema/core.schema
include     /usr/local/etc/openldap/schema/cosine.schema
include     /usr/local/etc/openldap/schema/inetorgperson.schema
pidfile     /var/run/slapd.pid
argsfile    /var/run/slapd.args
database    ldbm
suffix      "dc=example, dc=com"
rootdn      "dc=example, dc=com"
rootpw      secret
directory   /var/db/openldap-ldbm
index       objectClass eq

This configuration includes cosine.schema (the core schema used by OpenLDAP) and inetorgperson.schema, which include attribute definitions needed by our directory. All of these files are included with OpenLDAP version 2--when writing this article, I took pains to ensure that you do not need to create any custom schemas for use with OpenLDAP. While custom schemas make LDAP highly extensible, they're unnecessary for a basic address book.

Related Reading

LDAP System Administration
By Gerald Carter

Creating the Directory Layout for an Address Book

Our first goal is to create our base Distinguished Name (DN), the root of our address book's subtree. Our example will use domain components to establish a (hopefully) globally unique base DN for our company. Ensuring uniqueness across organizations isn't actually necessary unless you will later need to integrate your directory with another, or if the directory will be available globally via some referral service, but it's nice to go ahead and plan these things out properly.

Our sample company, Conglomo, Inc., has purchased the domain example.com to be used for all operations, much as IBM uses ibm.com. Notice that we already defined this base DN using the suffix keyword in slapd.conf:

suffix "dc=example, dc=com"

We need to create an actual entry for this in the LDAP directory itself using an LDIF-formatted file and ldapadd. Create a file named directory.ldif containing:

dn:     dc=example, dc=com
objectClass:    top
objectClass:    dcObject
objectClass:    organization
dc:     example
o:      Conglomo, Inc.

Be sure to not leave any trailing spaces, as they may confuse ldapadd.

Pretty simple so far. Next, create an organizational unit in the directory under dc=example, dc=com to serve as a container for our address book entries:

dn:     ou=addressbook, dc=example, dc=com
objectClass:    top
objectClass:    organizationalUnit
ou:     addressbook

Many people create an organizational unit named people rather than addressbook, but addressbook will work fine for our purposes.

This is as far as we need to go in defining our directory layout. Note, however, that the layout is actually a bit simplistic. In many situations, you may find that you want to create individual entries for departments and then create address books below those departmental organizational units:

dn:     ou=addressbook, ou=accounting, dc=example, dc=com
objectClass:    top
objectClass:    organizationalUnit
ou:     addressbook

Alternatively, you could do the opposite and create a central address book that contains each of your departments as organizational units:

dn:     ou=accounting, ou=addressbook, dc=example, dc=com
objectClass:    top
objectClass:    organizationalUnit
ou:     accounting

This last method has the advantage that the organizational unit definition actually matches the department rather than being addressbook. Most LDAP clients will consider the ou as the department, so this may be important for you.

How you eventually create the directory structure really depends on the various services you will be supporting with LDAP, and not just the address book. For now, the layout presented will be more than sufficient to support a simple address book.

Now that you have the directory layout in directory.ldif, import the LDIF entries into the directory using ldapadd:

$ ldapadd -D 'dc=example, dc=com' -f directory.ldif -W
Enter LDAP Password: secret

Assuming everything went well, OpenLDAP should now have imported the entries. To verify this did indeed occur, use ldapsearch to dump your directory by specifying objectclass=*:

# ldapsearch -b 'dc=example, dc=com' 'objectclass=*'
version: 2

#
# filter: objectclass=*
# requesting: ALL
#

# example, com
dn: dc=example, dc=com
objectClass: top
objectClass: dcObject
objectClass: organization
dc: example
o: Conglomo, Inc.

# addressbook, example, com
dn: ou=addressbook, dc=example, dc=com
objectClass: top
objectClass: organizationalUnit
ou: addressbook

# search result
search: 2
result: 0 Success

# numResponses: 3
# numEntries: 2

At this point, you are ready to roll. You have imported your directory layout and are now ready to add contact information to the directory. Before you do that, we need to discuss the attributes you will use for the contact entries in the directory.

Defining the Contact Attributes

One of the first goals in creating contacts is to decide what information to store for each entry. Once we know that, we can map our needs to the right LDAP attributes.

Let's consider a typical contact entry. Obviously, we will want to know the contact's name and address, her phone number, and her email address. Read Table 1 to review these real-world attributes and the LDAP attributes to which they map. From now on, we will use a combination of real-world attribute names and LDAP attribute names, so refer to this table as needed.

Table 1. Common LDAP Attributes used for Contact Entries

AttributeObjectClassMeaning
commonName, cnpersonIndividual's full name
givenName, gninetOrgPersonIndividual's first name
surname, snpersonIndividual's last name
physicalDeliveryOfficeNameorganizationalPersonDepartment or delivery office name
mailinetOrgPersonEmail address
postalAddressorganizationalPersonStreet mailing address
lorganizationalPersonCity
storganizationalPersonState
postalCodeorganizationalPersonPostal (ZIP) code
telephoneNumberorganizationalPersonWork number
facsimileTelephoneNumberorganizationalPersonFax number
pagerinetOrgPersonPager number
mobileinetOrgPersonMobile phone number
homePhoneinetOrgPersonHome phone number

Any entry in our directory requires a DN. For this article, we will use a contact's full name to establish the uniqueness of each DN. The full name is specified using the commonName (cn) attribute. Let's create an example entry with a fictitious employee of Conglomo, Inc. named Jane Doe in a file named contact.ldif:

dn: cn=Jane Doe, ou=addressbook, dc=example, dc=com

Now that the DN is defined, we can go ahead and start defining the LDAP attributes that we want. Begin by defining the commonName (cn, givenName (gn), and surname (sn) attributes:

cn: Jane Doe
gn: Jane
sn: Doe

All of these attributes require objectClass person, so we need to define that, as well:

objectClass: person

Next, let's define the email address for our contact using the mail attribute:

mail: jane.doe@example.com

The mail attribute requires objectClass inetOrgPerson, which belongs to organizationalPerson, so let's use those object classes:

objectClass: organizationalPerson
objectClass: inetOrgPerson

The next attribute we will define is physicalDeliveryOfficeName. It's required for two reasons. First, the attribute allows you to specify the name of the office to where mail should be sent. Also, since we are using the organizationalUnit attribute to define our addressbook container, we can't really define a department name, as the department name is defined by the organizationUnit attribute. This is a bit contorted, but that's how LDAP-aware email clients use it.

Let's go ahead and define these attributes:

physicalDeliveryOfficeName: Conglomo, Inc., Financial Services

Most LDAP-aware email clients recognize an additional company attribute. It defines the company name; in this case, Conglomo, Inc. Unfortunately, this attribute is not standard, and requires that you use a custom schema. (Search for more information by looking for microsoft.schema.)

Now we are free to define Jane's mailing address:

postalAddress: PO BOX 55555
l: Baton Rouge
st: LA
postalCode: 70555

With this information and the physicalDeliveryOfficeName, LDAP clients will see the following when requesting Jane's physical address:

Jane Doe
Conglomo, Inc., Financial Services
PO BOX 77831
Baton Rouge, LA 70879

Next, specify Jane's phone information for her work phone, fax, pager, mobile phone, and home phone number:

telephoneNumber: 555-555-5555
facsimileTelephoneNumber: 555-555-5556
pager: 555-555-5557
mobile: 555-555-5558
homePhone: 555-555-5559

Finally, we need to define the organizational unit:

ou: addressbook

At this point we are finished creating Jane's LDIF-formatted entry. You should now have a file named contact.ldif with the following information:

dn: cn=Jane Doe, ou=addressbook, dc=example, dc=com
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
cn: Jane Doe
gn: Jane
sn: Doe
mail: jane.doe@example.com
physicalDeliveryOfficeName: Conglomo, Inc., Financial Services
postalAddress: PO BOX 55555
l: Baton Rouge
ou: addressbook
st: LA
postalCode: 70555
telephoneNumber: 555-555-5555
facsimileTelephoneNumber: 555-555-5556
pager: 555-555-5557
mobile: 555-555-5558
homePhone: 555-555-5559

You should notice that I moved the objectClass attributes to the top of the entry and added an objectClass:

objectClass: top

This isn't actually necessary, but I'm a sucker for completeness, and I typically fully define all object classes used when creating an entry.

Import our example entry into the directory with ldapadd:

$ ldapadd -D 'dc=example, dc=com' -f contact.ldif -W
Enter LDAP Password: secret

After ldapadd is done, you will have your first contact entry in your directory. Again, you can use ldapsearch to dump the entire directory, or, as shown below, to perform a more specific lookup:

$ ldapsearch -b 'ou=addressbook, dc=example, dc=com' '(objectclass=*)'

Configuring Netscape 7 and Outlook 2002 as LDAP Clients

Now that we have an LDAP server set up and ready to go, our next step is to configure our LDAP clients to use the directory. This article uses Netscape 7 under Linux and Microsoft Outlook 2002 under, not surprisingly, Windows. Let's begin with Netscape 7.

In my opinion, Netscape has a much better interface for LDAP-based directories than Outlook. To some extent, that is understandable. Outlook is built to work with Exchange as both a mail client and groupware application. How is Netscape better? For one thing, Netscape Address Book can import all of the entries in an LDAP directory into your address book and keep those entries synchronized with the directory. Essentially, you can disconnect from the local network and use the LDAP-based address book even if you no longer have access to the actual LDAP server. Now that's nifty.

Let's configure Netscape Address Book to use our LDAP server.

  1. Begin by opening Netscape Address Book, either directly or from Netscape Communicator.
  2. Choose File->New->LDAP Directory.
  3. Netscape Address Book will open up the properties page for an LDAP directory. First, enter a friendly name in the Name field, such as "Company LDAP Directory."
  4. In the Hostname field, enter either the LDAP server's hostname or IP address.
  5. The Base DN is simply the base search path specified for ldapsearch with the -b option, so enter ou=addressbook, dc=example, dc=com.
  6. Choose OK.
  7. Restart Netscape, and you should see the new LDAP entry in the Address Books pane.

That's all there is to it. To test the search feature, type "Jane" into the search field labeled "Name or Email contains:," and then press Enter. Jane Doe's listing should come up. Select that listing to see all of the properties we defined for Jane that the Netscape Address Book recognizes. To look up an LDAP contact when composing an email message, do the following:

  1. Choose Compose.
  2. Select the Address icon.
  3. In the "Look in:" drop-down menu, choose your LDAP server entry defined earlier in the Netscape Address Book.
  4. Enter "Jane" in the text field labeled "for:" and press Enter.

Next, let's configure Microsoft Outlook 2002 to use our LDAP server:

  1. Start Outlook and then select Tools->Email Accounts.
  2. Choose "Add a new directory or address book" under the "Directory" label and then choose Next.
  3. Choose "Internet Directory Service (LDAP)" as the address book type and then choose Next.
  4. For "Server Name" specify the IP address or the hostname of the LDAP server.
  5. Choose "More Settings" and then select the "Search" tab. Here you need to specify the base search path, which we also specified to ldapsearch using the -b option. Type ou=addressbook, dc=example, dc=com in the text field labeled "Search base" and then choose OK.
  6. Choose Next.
  7. Outlook will present a congratulations screen. Choose Finish to close the Wizard.
  8. Restart Outlook to be able to use the LDAP directory you just specified.

There are two ways to test Outlook's LDAP directory access. First, let's try the fast and easy way:

  1. Click the New Mail icon to bring up the New Mail window.
  2. In the To: field, enter "Jane". (Outlook may try to auto-complete Jane's name or address if you have ever emailed another Jane before. Be sure to not use this entry, as that will short-circuit the LDAP lookup.)
  3. You can now either tab to the next field or enter Ctrl-K to force an address lookup. If you do not enter Ctrl-K, then Outlook will perform the lookup while you are doing another operation, such as entering the text of the message.

Related Reading

Essential System Administration
Tools and Techniques for Linux and Unix Administration
By Æleen Frisch

At this point, Outlook should have filled in "Jane Doe" for you in the To: field. Note that for some older Outlook clients, such as Outlook 97, you may need to specify that Outlook always automatically perform an LDAP lookup, using the Outlook Options screen.

The second method of searching the LDAP directory is to use the Outlook Find tool from the New Mail screen:

  1. Click the New Mail icon to bring up the new mail window.
  2. Click the To: icon.
  3. In the "Choose Names from the" field, choose your LDAP server entry.
  4. Choose Find.
  5. Enter "Jane" in the "Display Name" field, and then choose OK.

To see all of the contact's attributes, simply double-click the entry in the To: field. Alternatively, you can always use the Start->Search->Using Microsoft Outlook tool instead of being forced to load Outlook every time you want to call a contact. Netscape Address Book has a better interface for this, but Outlook is certainly usable.

Concluding Remarks

OpenLDAP continues to make inroads in small and medium-sized businesses as an easy, cost-effective way to manage data. This article gave just one small example of how you can use OpenLDAP, and indeed any LDAP server, to fine-tune the level of control you have over the information required by your business and by your users.

I'd like to say thank you to Howard Chu of the OpenLDAP team for helping to debug this article.

Additional Resources

Dustin Puryear is a consultant providing expertise in managing and integrating UNIX and Windows systems and services, with a strong focus on open source.


Return to ONLamp.com.

Copyright © 2009 O'Reilly Media, Inc.