Integrating Seam and OpenLDAP

by Shane Bryzak

Article permalink: http://shane.bryzak.com/blog/articles/integrating_seam_and_openldap

Introduction

One of the new features introduced in JBoss Seam 2.1.0 is Identity Management, a standard API for managing users and roles within a Seam application. The Identity Management API works by allowing you to interface your Seam application to a backend identity (security) store. Out of the box, Seam provides support for authenticating against users stored in either a database or an LDAP directory. This article deals with the LDAP option, walking through the installation and configuration of an OpenLDAP directory server, and continuing on to describe how to configure a Seam application to talk to the directory.

Prerequisites

If you want to follow the steps detailed in this article, you'll need the following things already installed:

  • Java 1.5 JDK or greater
  • An application server, such as JBoss AS (installing this is beyond the scope of this article)
  • Apache Ant


The Seam application that we'll use to integrate with OpenLDAP will be the seamspace example, which is included in the examples directory of the Seam distribution. It is already based on multiple user roles, and includes identity management views that allow users and roles to be managed, making it perfect for demonstrating the integration with OpenLDAP.

At the time of writing the latest stable release of OpenLDAP is 2.4.11, which can be downloaded from http://www.openldap.org. We are going to be building OpenLDAP from source so we assume that you're running some flavour of *nix. If you're on Windows you can use Cygwin, available from http://www.cygwin.com (in fact this article was written using Cygwin).

Installing OpenLDAP

Let's get right into it! The first step is to extract the source archive (command: tar zxvf openldap-2.4.11.tgz), and then change into the created directory. It should contain something like this:



The building process takes a while to complete and consists of multiple steps. First we need to run the configure script - by default OpenLDAP will install into the /usr/local directory, and this article assumes that we are indeed using this default location. We start the configure script by executing './configure' in the OpenLDAP source directory:



After a while, it should complete successfully:



The next step is to run 'make depend':



This should eventually complete successfully:



Next we run 'make':



And finally, 'make install':



This last step installs all of the OpenLDAP files into the correct location. After completing, we can change into the /usr/local directory to see what has been installed:



Before we actually start the OpenLDAP service, there are a few configuration changes we need to make and some extra steps to complete. First we need to edit the OpenLDAP configuration file, which is located inside the /usr/local/etc/openldap directory:



Use your favourite text editor to open slapd.conf, and scroll down to near the bottom of the file. The last section contains a few parameters that need to be modified for your specific environment. The first two, suffix and rootdn should be modified to reflect the domain for which your directory service is running. In this example, I've modified the domain to 'dc=bryzak,dc=com'. The 'dc' in these entries stands for 'Domain Component'.

The rootdn setting contains the distinguished name (DN) of the root user, who is able to read and write to everything in the directory. You may wish to also change the default password for the root user (the default is 'secret') by modifying value of the rootpw parameter.

Once we've saved our changes to slapd.conf, we need to make some modifications to the core LDAP schema. This step may sound a little intimidating, however it turns out to be quite simple. Two changes are required, firstly to add a 'roles' attribute to the Person element so that the Identity Management API can determine which roles a user belongs to, and also to add an 'enabled' attribute to Person so that we have the ability to disable specific user accounts. Actually the 'enabled' attribute is optional, however we'll still include it in this example.

We edit the core schema by opening the file /usr/local/etc/openldap/schema/core.schema in a text editor (you may first need to make this file writable by executing 'chmod 644 core.schema'):



Scroll down to the end of the 'attributetype' entries, and create two new entries, 'roles' and 'enabled'. They should look like this:

attributetype ( 2.5.4.66 NAME 'roles'
DESC 'role memberships of a person'
SUP name )

attributetype ( 2.5.4.67 NAME 'enabled'
DESC 'enabled flag for a person'
SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 )

The big scary looking number after the SYNTAX parameter is simply the standard LDAP type for a boolean (true/false) value. If you're interested in finding out more about LDAP attribute definitions, you can refer to RFC2252 (http://tools.ietf.org/html/rfc2252), although I can't promise it will be an interesting read.

After editing, this is how the file should look after the changes:



Next, scroll down and find the 'objectclass' entry for 'person'. Once you find it, add the 'roles' and 'enabled' attributes to its list of MAY attributes:



After saving our changes, it's time to finally start the OpenLDAP service. We do this by executing slapd, located in the /usr/local/libexec directory:



The slapd service runs as a daemon process in the background. We can confirm that it's running by executing the ps command:



Before we can really use our directory, we need to create a couple of entries. Once again with your text editor, create a new file called entries.ldif in your home directory, with the following contents:

dn: dc=bryzak,dc=com
objectClass: dcObject
objectClass: organization
dc: bryzak
o: Example
description: Example

dn: cn=Manager,dc=bryzak,dc=com
objectClass:organizationalRole
cn: Manager
description: Directory Manager

Ensure that you modify the dc values to match your own domain. After saving this file, we can use ldapadd to add the entries it contains to our directory - execute '/usr/local/bin/ldapadd -D cn=Manager,dc=bryzak,dc=com -w secret -f entries.ldif' (changing the bind dn and password to suit your own environment). Executing this command will add the entries to the directory:



Once this is done, we can start to explore our directory a little. The easiest way to do this is to use a tool, of which there are many available both free and commercial. One great open source LDAP browser is JXplorer, which can be downloaded from http://www.jxplorer.org/. Using JXplorer is really straight forward - after downloading and installing, we can use it to open a connection to our new OpenLDAP server. The connection details should match those which we configured for OpenLDAP:



After connecting, we should see our (so far minimal) directory structure:



From here, we can click on each entry in the tree, edit entries, create new entries, etc. The first thing that we're going to do in our new directory is to create a couple of new entries which will act as the containers for our users and roles. Right-click on the root node for your directory (in this example it's the 'bryzak' node) and select 'New' from the context menu. Remove whatever is listed in the 'Selected Classes' box by default, and find 'organizationalUnit' in the 'Available Classes' list and double click it to add. For the RDN value enter 'ou=Person' (RDN stands for Relative Distinguished Name). So what we should end up with is something that looks like this:



After we click OK, we will be presented with the Table Editor showing us the attributes of the entry we are about to create:



If we're happy with our new entry, we can click on the 'Submit' button to commit our changes to the directory. Our new node will now appear in the directory tree:



Next we need to create another organizationalUnit entry to contain our user roles. We repeat the same process again, except this time we set the RDN to 'ou=Role' instead. Once that is done, and now that we have our container nodes, we still need to create an initial user and give him a role so that we can log into our application for the first time. To do this, we right click on the new 'Role' node and click 'New' again. This time we're going to create an 'organizationalRole' entry, so find it in the list of available classes and double click it to add it to the list of selected classes. For the RDN value, enter 'cn=admin' (cn stands for Common Name). We should now have something that looks like this:



After we click 'OK', then 'Submit', our new entry will appear under the 'Role' node:



The last thing we need to do is create a user entry so that we'll be able to log in. This is done by right clicking on the 'Person' node and clicking 'New' to create a new user entry. Our new entry is going to actually have two object classes, 'person' and 'uidObject'. The reason that it needs to be a uidObject is that we're going to store the user's username in the 'uid' attribute of uidObject. The RDN for our new user entry should be 'uid=shane' (or whatever username you wish to use), so before we click 'OK' our entry should look like this:



After clicking the OK button, we need to set a few more attribute values before saving. Specifically, we need to set the cn (common name), sn (surname), userPassword, roles, and enabled attributes. The common name can really be anything you want, in this case we'll make it the user's full name. The surname obviously should contain the user's surname, and the enabled attribute should be set to TRUE to indicate that this account is enabled and that the user is able to log in. We'll also set the password to something simple, like 'password'.

The next thing we need to add is a role. We edit the 'roles' attribute and enter 'cn=admin,ou=Role,dc=bryzak,dc=com' as the value, signifying that the user is a member of the admin role. It is possible to add multiple roles by right-clicking on the 'roles' attribute name and selecting the 'Add Another Value' option. Finally, we need to set the user's password. We do this via the user password dialog which is displayed when the value cell of the userPassword attribute is clicked. The hash algorithm should be set to MD5 as this is Seam's default password hashing algorithm.

When all the attribute values have been updated, our entry should look like this:



Once we're happy with all the settings, we click the 'Submit' button to create the user entry. At this stage, we now have enough information contained in our LDAP directory to be able to authenticate against it. The next step is to configure Seam to authenticate against LDAP.

Configuring the Seam Example

If you don't have Seam already, you can download it from http://www.seamframework.org (you need at least version 2.1.0 Beta). After extracting the distribution archive somewhere, we can start by taking a look at the seamspace example (in the examples/seamspace directory). The particular file that we're interested in is components.xml, located in the resources/WEB-INF directory. This is the main configuration file that Seam applications use to configure various Seam components. What we need to do is configure components.xml with a new identity store that will use our new LDAP directory to authenticate against. The following entry configures an LdapIdentityStore component with the necessary attribute values:

<security:ldap-identity-store name="ldapIdentityStore"
server-address="localhost"
bind-DN="cn=Manager,dc=bryzak,dc=com"
bind-credentials="secret"
user-DN-prefix="uid="
user-DN-suffix=",ou=Person,dc=bryzak,dc=com"
role-DN-prefix="cn="
role-DN-suffix=",ou=Roles,dc=bryzak,dc=com"
user-context-DN="ou=Person,dc=bryzak,dc=com"
role-context-DN="ou=Roles,dc=bryzak,dc=com"
user-role-attribute="roles"
role-name-attribute="cn"
user-object-classes="person,uidObject"
/>

The attributes listed here are explained in more detail in the Seam reference documentation, however let's examine them briefly. The name attribute simply sets the name of our component, in this case "ldapIdentityStore". This name is used to refer to this specific component elsewhere. The server-address attribute specifies the address of the LDAP server, in this example we have it running on localhost. The bind-DN and bind-credentials attributes contain the account details that are used to bind to the directory (LDAP directories must be "bound" to, i.e. authenticated against, before queries can be executed against them). If you recall from earlier, the root DN is specified in slapd.conf.

The next couple of parameters, user-DN-prefix and user-DN-suffix, are concatenated with the username that the user provides when authenticating to search for their record in the directory. For example, say that user jsmith is authenticating. This username will be concatenated with the specified prefix and suffix to produce a user DN (distinguished name) of "uid=jsmith,ou=Person,dc=bryzak,dc=com". This DN value is then used to precisely locate the user's entry. Likewise, the role-DN-prefix and role-DN-suffix values are used the same way to lookup role entries.

The user-context-DN describes the entry within the directory that contains all user entries, and likewise the role-context-DN describes where role entries are located. The user-role-attribute value specifies which attribute of the user entry contains a list of the roles that they are a member of, and the role-name-attribute specifies which attribute of the role entry contains the actual name of the role. Finally, the user-object-classes attribute contains a list of LDAP object classes for users (used to create new user entries).

Now that our LdapIdentityStore is configured, we need to hook it up to IdentityManager, which is the core component of Seam's Identity Management API. This is a piece of cake, and simply requires the following line in components.xml:

<security:identity-manager identity-store="#{ldapIdentityStore}"/>

Once this is done and any changes saved, we're ready to deploy the example. If needed, we can edit the build.properties in Seam's root directory to configure the jboss.home property (assuming we're deploying to JBoss AS) to point to where our application server is installed. All that is left is to simply run 'ant' in the examples/seamspace directory to deploy our example application to the application server.

Once deployed successfully, we can use our web browser to open the seamspace example by browsing to http://localhost:8080/seamspace. Here we're presented with the following login page - we can enter the username and password that we created in the LDAP directory and then click the 'LOGIN' button to log in:



After logging in, we'll be shown the message 'Sorry, but this member does not exist'. This message can be ignored (it's unrelated to security, the seamspace example simply can't find a database record for the logged in user because we're authenticating with LDAP now). At the top right hand corner of the page we'll now be able to see a 'security' link. After clicking this link and then the 'Manage Users' link on the next page, we'll be taken to the user manager view. Here we'll see our existing user that we created previously in the LDAP directory:



Likewise, if we click back then click the 'Manage Roles' link we'll be taken to the role manager view where we can see the role we created also:



While we're on this screen let's create a new role. Clicking on the 'new role' button takes us to the role details view where we can enter a name for our new role:



After clicking save we'll be taken back to the role manager view where we can see our new role:



We can also go back to JXPlorer and confirm that the new role was indeed created in the directory. By refreshing the 'Role' node (right click -> refresh) we can now see the new user role has been added:



Let's now add a new user - clicking on the security link again, then manage users, we're back once more in the user manager view. Clicking the 'new user' button takes us to the user detail view, where we can enter the details of our new user:



After clicking save, we can see that our new user has been created:



And if we go to our directory and refresh the 'Person' node, we can also confirm that our user has been created there:



That just about wraps things up. We've seen how to install a new OpenLDAP directory and configure it to work with Seam Security, and also seen how trivial it is to configure an Identity Store in Seam's components.xml to talk to our LDAP directory.

For more information, the Security chapter of the Seam reference documentation is a good place to start, and the Seam user forums at http://www.seamframework.org is a great place for users to ask questions.

If you would like to comment on this article, please go to
http://in.relation.to/Bloggers/IntegratingSeamSecurityWithOpenLDAP?showCommentForm=true#commentForm.