The LDAP protocol has been around for quite a while. Today it is mainly used for authentication but you could use it to make almost any kind of information available in your network.
In this article I’ll show you how to set up your own LDAP server using the open source Java based Apache DS server.

A short introduction to LDAP

LDAP servers store their data in the form of a tree., called the DIT (Directory Information Tree). Nodes are called ‘entries’ and contain data as key – value pairs.

Each entry is identified by its distinguished name (DN). A distinguished name is a string which consists of one or more comma-separated key-value pairs which together uniquely identify the node. An example: ‘dc=foo,dc=com’

The name of a key and the form and type of the data in its value depends on its attribute type.
Object classes define a set of required and optional attribute types.
Entries inherit from one or more object classes: And finally a collection of objectclasses and their allowed attribute types is called a schema.

LDAP schema diagram:

Ldap Schema

Predefined schema’s

An advantage of using a predefined schema to represent your data is that external clients won’t have any trouble understanding what your object classes and attribute types mean.
Some examples are DHCP and Microsoft Address Book.

Want to read more about LDAP? Here’s a nice link: http://www.zytrax.com/books/ldap/

Apache DS

Apache DS is not so well known and fully featured as some other LDAP servers: Most noticably Microsoft Active Directory and OpenLDAP.
But it has a few nice advantages: It is written in Java and therefore runs on many platforms. And the project contains a well structured client LDAP Api which can also be used to connect to other LDAP servers.

Apart from its stand-alone LDAP server, Apache DS also provides an administration gui called Apache DS Studio.
Apache DS Studio can also function as a LDAP server and it has an LDAP schema IDE which is based on the Eclipse framework.

Installing Apache DS Studio

Because we are going to write our own schema in this blog we’ll install DS Studio.

Requirements:
You only need to have Java 5 or higher installed.

Download DS Studio 1.5 from here and follow the instructions to install it.

Our schema: A book catalogue

We are going to define a simple schema which will enable us to store information about books in our LDAP server.
Our schema's only object clas is the Book class, with these attributes:

  • author
  • title
  • synopsis

Now let's do the work.
Open Apache DS Studio and create a new project.
Open the 'schema editor' perspective.
Right-click somewhere in the schema panel and choose 'new schema'. Let's call our schema 'books'.
We will define our attributes and make synopsis an optional attribute.
Right-click our schema and choose 'new attribute type'.
The first thing you'll have to fill in is the OID. OID stands for Globally unique object identifier. To be sure that your OID is really globally unique, you should have your set of id's assigned by the IANA, ANSI or BSI. Obviously we're not going to do that here, so just choose something like '1.1.1.1'.
The alias field will contain the name of your attribute, so author, title or synopsis.
Click 'next' to fill in the superclass (superior) and syntax. All attribute types need a superior; here we'll just choose 'name'. Choose 'directory string' for your syntax.

When you try to add our attribute type 'title' you'll notice that Apache DS does not allow it. It turns out that in the predefined schema organizationPerson there exists an attribute type already with alias title.
Although we don’t use this schema ourselves, Apache DS doesn’t like it. So instead of using title, we'll use 'subject' for our attribute's alias.

When you're done with defining the attributes, define the object class book. It's type is a structural class.
Just like in Java, there exist abstract classes in LDAP as well. A node must have at least one structural class to be able to be instantiated.

When you're done with the book class as well, click on the 'save' icon in the DS Studio's menu bar.
Our schema is now complete.

To be able to use it in our server we need to export it. Right-click the schema and choose 'export as LDIF/ schema for Apache DS’.
And we need to import it into our server, which we are going to create now:
Open the LDAP perspective, right-click the servers tab and choose 'new server'. Give it the name of your choice and start it.
Open the connections tab, and right-click it to create a new connection to our server.
Choose localhost as the host and Apache DS' default port, 10389, as the port.

In the authentication tab, configure 'simple authentication'. As the username, fill in: uid=admin, ou=system.
The username is really a DN as well. It identifies the user object in the server's DIT, where it is located under the node 'system'.
For the password, fill in 'secret'. These are the default username and password used by Apache DS. They can be edited in the configuration of the server.
Finally choose Connect. This will show the DIT of our server in the LDAP Browser panel.

Import our schema into server by right-clicking the DIT and choosing ‘import’, and LDIF as the format.
When you are done you can check your imported schema in the DIT under the node ‘ou=schema’.

Create a domain

For this purpose we’ll use the example.com domain that comes with the DS Studio.
Right-click the DIT root node, choose ‘new conext entry’, choose ‘domain’ as the class and click ok.
There should be a new node in the tree now called dc=example,dc=com

Here's what DS Studio now should look like, showing our 'books' schema and the example.com domain:

DS Studio showing our schema and domain

Add a node

To test our schema and our server we will add a context node.
Open ‘new conext entry’. First click the 'Refresh' icon to refresh the list of classes. Choose our 'books' schema. Notice that when we did that, we got the ‘top’ for free. This is because all classes in the DIT have to inherit from top.
Next you have to fill in the distinguished name (DN). The DN should uniquely identify our node in the DIT tree.
Since our node will fall under the example.com domain, the nodes for the domain have to be part of the DN.
In addition for that we should specify an attribute that makes our node unique in our domain: let’s take the subject attribute.
LDAP lists the nodes in the DN in reverse order, so our DN becomes: subject=test,dc=example,dc=com

In the next screen we fill in our attributes.
Click ‘add node’ to add the optional synopsis node.

Connecting to our server using the Apache DS Client API

In this blog we’re going to connect to our server using the Apache DS Api. Alternatively, it is also possible to connect using JNDI
The Apache client Api makes it possible to search nodes, add them, update them or delete them.

First you’ll need to have the Apache DS client jar in your classpath. Here’s a Maven pom snippet that does that:


    org.apache.directory.shared
    shared-ldap-client-all
    1.0.0-M12

Then our client: here is a simple client tool which expects the title of a book as a parameter and will then search it and print out its details.

import org.apache.directory.ldap.client.api.LdapConnection;
import org.apache.directory.ldap.client.api.LdapNetworkConnection;
import org.apache.directory.shared.ldap.model.cursor.EntryCursor;
import org.apache.directory.shared.ldap.model.entry.Attribute;
import org.apache.directory.shared.ldap.model.entry.Entry;
import org.apache.directory.shared.ldap.model.exception.LdapException;
import org.apache.directory.shared.ldap.model.message.SearchScope;

import java.io.IOException;
import java.util.Iterator;

public class LdapBookClient {
  private final String host;
  private final int port;
  private final String userName;
  private final String password;

  public LdapBookClient(String host, int port, String userName, String password) {
    this.host = host;
    this.port = port;
    this.userName = userName;
    this.password = password;
  }

  public LdapConnection getConnection() throws LdapException, IOException {
    LdapConnection connection = new LdapNetworkConnection(host, port);
    connection.bind("uid=" + userName + ",ou=system", password);
    return connection;
  }

  public void closeConnection(LdapConnection connection) throws LdapException, IOException {
    connection.unBind();
    connection.close();
  }

  public Iterator search(LdapConnection connection, String baseDn, String attributeName, String attributeValue) throws LdapException {
    String filter = "(" + attributeName + "=" + attributeValue + ")";
    SearchScope searchScope = SearchScope.SUBTREE;
    EntryCursor cursor = connection.search(baseDn, filter, searchScope);
    return cursor.iterator();
  }

  public static void main(String[] args) throws Exception {
    if (args.length == 0) {
      throw new IllegalArgumentException("Specify the title of the book to be searched.");
    }
    LdapBookClient client = new LdapBookClient("localhost", 10389, "admin", "secret");
    LdapConnection connection = client.getConnection();
    Iterator searchResults = client.search(connection, "dc=example,dc=com", "subject", args[0]);
    if (searchResults.hasNext()) {
      printBookDetails(searchResults.next());
    } else {
      System.out.println("No book found with title: " + args[0]);
    }
    client.closeConnection(connection);
  }

  private static void printBookDetails(Entry book) {
    System.out.println("Title: " + book.get("subject").get().toString());
    System.out.println("Author: " + book.get("author").get().toString());
    Attribute synopsis = book.get("synopsis");
    if (synopsis != null) {
      System.out.println("Synopsis: " + synopsis.get().toString());
    }
  }
}

Notice that we first have to connect to our server, and then we still have to bind. Only after that we can do searches, updates or deletes.

This was just a small sample of what is possible using Apache DS. Hopefully it's enough to get you started, otherwise there is more documentation to be found on the Apache DS website.