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
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.
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
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
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
Pretty simple so far. Next, create an organizational unit in the directory
dc=example, dc=com to serve as a container for our address
dn: ou=addressbook, dc=example, dc=com objectClass: top objectClass: organizationalUnit ou: addressbook
Many people create an organizational unit named
addressbook will work fine for
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
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
import the LDIF entries into the directory using
$ 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
# 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.
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.
|Individual's full name|
|Individual's first name|
|Individual's last name|
|Department or delivery office name|
|Street mailing address|
|Postal (ZIP) code|
|Mobile phone number|
|Home 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
cn) attribute. Let's
create an example entry with a fictitious employee of Conglomo, Inc. named Jane
Doe in a file named
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
cn: Jane Doe gn: Jane sn: Doe
All of these attributes require
objectClass person, so we need
to define that, as well:
Next, let's define the email address for our contact using the
inetOrgPerson, which belongs to
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
addressbook container, we can't really define a department name, as
the department name is defined by the
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
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
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
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:
At this point we are finished creating Jane's LDIF-formatted entry. You
should now have a file named
contact.ldif with the following
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: firstname.lastname@example.org 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
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 -D 'dc=example, dc=com' -f contact.ldif -W Enter LDAP Password: secret
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=*)'
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.
-boption, so enter
ou=addressbook, dc=example, dc=com.
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:
Next, let's configure Microsoft Outlook 2002 to use our LDAP server:
ou=addressbook, dc=example, dc=comin the text field labeled "Search base" and then choose OK.
There are two ways to test Outlook's LDAP directory access. First, let's try the fast and easy way:
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:
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.
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.
Dustin Puryear integrates Windows and Unix as the Principal Consultant of Puryear Information Technology.
oreillynet.com Copyright © 2003 O'Reilly & Associates, Inc.