Setting up your own LDAP server with Apache DS

Misja Alma

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.

Comments (7)

  1. Simon - Reply

    September 23, 2012 at 7:02 pm

    But why? LDAP as protocol to Active Director makes sense; you have AD and you need some app to get authorisation details from it. But to setup a standalone server containing books when you would have that in a relational database seems too contrived.

  2. Misja Alma - Reply

    September 23, 2012 at 10:25 pm

    You're right, there are easier ways to setup a books server than with LDAP. I chose books only to demonstrate a simple schema in the example setup of Apache DS.

    If you want a real life example of where you'd need a standalone LDAP server, here's one: In my last project we needed to interface a phonenumber administration system to a couple of legacy systems, and those systems were expecting LDAP as the communication protocol.
    Active Directory would have been a good alternative there, but we chose for Apache DS because it gave us the opportunity to develop the application on a non-Windows platform (OSX).
    Also the DS Studio was really handy to develop our schema and smoke test your LDAP server.

  3. Prerana - Reply

    October 9, 2012 at 8:37 am

    Hi,
    I have 2 queries. Please can help or direct me regarding these: We are currently using WSO2 4.0 M8. We created some users and were using Apache Studio to view the Apache DS schema embedded in the WSO2 identity server
    We were able to see the user details, claims, uid etc. But, we are not able to see the gid (POSIX GID - Group id).
    And we were not able to add it as a new attribute as it is not displayed in the attribute Type list (drop down box). But the Schema Browser tab shows gidNumber attribute type in it.

    We want to view the GiDs of the users. Please do tell us a way to add gid as a new attribute so I can view the user’s Gid.

    2. Furthermore I also want to add PAM as an "ou" and use PAM for authentication and Mapping LDAP users to Linux user groups how can I go about this.
    Thanks in advance.

  4. Misja Alma - Reply

    October 10, 2012 at 9:24 am

    1. So your problem is that you have a user class which does not contain a Gid attribute and you are not able to add the Gid attribute in the schema editor.
    Could it be that your user class is a predefined one? In that case I think you cannot edit it. What you could do is define your own class and add all the required attrributes including the Gid, and then use that to create your users.

    2. I don't know the answer to this one, this looks more like a question about authentication and Linux user groups.

  5. Prerana - Reply

    October 15, 2012 at 8:49 am

    Hi,
    Thank You for the reply.

    1. The nis.schema that defines the posixAccount attributes was diabled. I had to set "m-disabled" attribute of the schema to FALSE. After which I was able to add a gidNumber and set values to it. I hope this is the right way though.
    2. yes sort of. I want to mapping LDAP users to Linux user group. For which as per my current understanding, using PAM & NSS should to the trick. But still need to figure out how.

    Please do provide your suggestions.

  6. Abhishek - Reply

    January 30, 2013 at 8:23 am

    How can i set password and userName and bind them together when writing A CUSTOM server

  7. vineesh - Reply

    April 13, 2015 at 10:53 pm

    I have a requirement. My application is load balanced with two servers and so I will have Apace DS in both. But the entries should be commonly accessed by both the servers, How to achieve it? Therefore there is no need to replicate data in both the servers.

Add a Comment