Using OpenLdap as a User Repository with JBoss 4.3.x

Introduction

This article continues our discussion of setting up J2EE Security in a JBoss 4.3.x container. In the last article, we completed the setup of an OpenLdap database that can be used as a User Repository.

User Repository

A User Repository isn’t explicitly defined in the J2EE Specification; however, the concept exists in all major J2EE Containers. Furthermore, the J2EE specification doesn’t explicitly require support for any particular security technology. This is the Java/J2EE community leave-the-details-to-the-creativity-of-the-implementer mantra shining through again. But, there are several high-level concepts that the spec does define.

Note, these are not all of the concepts introduced in Chapter 2 of the J2EE Specification, it is a subset that the author felt provided a good overview to get the point across.

Principal

A Principal "is an entity that can be authenticated by an authentication protocol in a security service that is deployed in an enterprise."[1]. A Prinicipal is "identified using a principal name and authenticated using authentication data."[1] The principal name is generally a string that is given meaning by how an application uses it. Authentication data could be a password, a randomly generated number from a hardware device, a client certificate, or anything else the industry can dream up.

In our example, the authentication mechanism will be an LDAP database and the authentication data is a password.

The term Principal has come up before on Thinkmiddleware.com during our JAAS discussion. Recall, the JAAS framework was defined after the J2EE Spec (and, thus J2EE Security); JAAS provides a framework that container vendors can use to build their security sub-system. Several major vendors do this very thing–including JBoss.

Security Policy Domain/Security Domain/Realm

A Realm "is a scope over which a common security policy is defined and enforced by the security administrator of the security service."[1] In other words, a Realm is "a collection of users that are controlled by the same authentication policy."[2]

Think of this "authentication policy" as the same thing as an "authentication mechanism". We’ve identified that our "authentication mechanism" is an LDAP database.

Implementations often refer to this as a Realm, but the specification uses the other two terms, interchangeably. All of the major vendors refer to this idea as a "Security Realm" or Realm. Naming the Realm is often a configuration step.

The scope of the security realm is entirely implementation dependent. It could be an application, applications, a single container, a cluster, a collection of clusters, or an administrative unit defined by the vendor.

Security Attributes

Security Attributes are associated with Principals. "A set of security attributes is associated with every principal".[1] Security Attributes are given meaning by the container security subsystem or application that is using them.

The J2EE Spec doesn’t define the format or use of any Security Attributes; once again, details are left up to the implementers.

In JBoss, the Security Subsystem is based upon JAAS. In particular, all of the security related details are stored in Principal objects in the JAAS Subject. JBoss doesn’t put public or private Credential objects in the JAAS subject. So for example, there is a Principal that contains the principal name or username. There is also a Principal that contains LDAP groups and J2EE Roles that a user belongs too. So, in our example, the Security Attributes would be these Groups and Roles.

Credential

A credential "contains or references information (security attributes) used to authenticate a principal for J2EE product services."[1] The J2EE specification "does not specify the contents or the format of a credential."[1]

In our example, the Credentials would be the JAAS Principal object that contains the J2EE Roles and LDAP Groups and the JAAS Principal that contains the principal name (or username). Although, JBoss uses a very simple model for representing Principals and their Security Attributes.

Note, a JAAS Credential (public or private) and a J2EE Credential are different. JAAS Principals, JAAS Private Credentials, or JAAS Public Credentials are all examples of J2EE Credentials in a container where JAAS is used to implement the security subsystem.  But, don’t let all of this scare you.  Remember, for our purposes, in JBoss, a JAAS Subject contains Principal objects, which represent users, J2EE Roles, and LDAP Groups.

Role

The J2EE Role concept is described in the Servlet Specification [3] and EJB Specification (outside the scope of this discussion). The Section SRV.12.4 of the Servlet Specification defines a Role as "a logical grouping of users defined by the Application Developer or Assembler." [3]

The Servlet Spec refers to these Roles as Security Roles. This series of articles will use the term "J2EE Role" to differentiate them from other Role concepts that come up elsewhere.
Note, LDAP Groups will always be referred to as LDAP Groups in this discussion. Do not confuse the two concepts.

Role Mapping

Section J2EE.3.3.6.1 of the J2EE Specification describes Role Mapping. Like everything else, it is very high-level and vague. It offers a couple of use cases. One use case describes mapping a J2EE Role to a user group in the operational environment. Another describes mapping a J2EE Role to a principal name in a security policy domain. The Servlet specification describes these same to scenarios, but describes them as the two ways that Role Mapping can occur in a Servlet Container.
Role Mapping is the process of mapping a J2EE Role to a concrete user or group of users in a User Repository.

These examples hint at the very thing we want JBoss to do. We want to take a J2EE Role defined in our web application, map it to a group of users in our User Repository, and then have our User Repository map that group to a set of users.
The Role Mapping concept will be revisited in a future article in this series.

Bringing It All Together

Several concepts have been presented here. Although, the Specifications remain at a fairly high-level, the additional comments made in each section hopefully show how an LDAP database provides the functionality that is needed (or, at least hinted at) for our purposes of a User Repository.

In our example, the User Repository will be an OpenLdap server that has users and groups defined as described in the first two articles in this series. This allows our LDAP database to describe users, groups, user to group mappings, and authenticate users based upon a password. This satisfies all the criteria that have been given for a User Repository.

LdapExtLoginModule JAAS Login Module for JBoss

JAAS LoginModules are configured via $SERVER_HOME/conf/login-config.xml in JBoss servers. Several JAAS LoginModules ship with JBoss. To communicate with an OpenLdap server the LdapExtLoginModule should be used.

For this example, the following must be added to login-config.xml:

<application-policy name="subject">
<authentication>
<login-module
code="org.jboss.security.auth.spi.LdapExtLoginModule" flag="required">
<module-option name="java.naming.factory.initial">com.sun.jndi.ldap.LdapCtxFactory</module-option>
<module-option name="java.naming.provider.url">ldap://localhost:389/</module-option>
<module-option name="java.naming.security.authentication">simple</module-option>

<module-option name="com.sun.jndi.ldap.connect.pool">true</modle-option>
<module-option name="bindDN">cn=Manager,dc=thinkmiddleware,dc=com</module-option>
<module-option name="bindCredential">secret</module-option>
<module-option name="rolesCtxDN">ou=Groups,dc=thinkmiddleware,dc=com</module-option>
<module-option name="roleFilter">(member={1})</module-option>
<module-option name="roleAttributeID">cn</module-option>
<module-option name="baseCtxDN">ou=Users,dc=thinkmiddleware,dc=com</module-option>
<module-option name="baseFilter">(cn={0})</module-option>
</login-module>
</authentication>
</application-policy>

Note, this configuration makes the assumption that slapd is running on the same machine as the JBoss server. Also, the bind user’s password is stored as plain text.

The "flag=required" parameter tells JBoss that this Login Module must be successful in order for the user to become authenticated. Since this is the only Login Module that can potentially authenticate a user, this configuration makes sense. In a more sophisticated configuration, there might be multiple Login Modules that could potentially authenticate a user.

The first couple of module-options are given directly to JNDI to create an LDAP Connection Factory:

         java.naming.factory.initial
         java.naming.provider.url
         java.naming.security.authentication

These parameters define an Initial Context Factory, the remote LDAP database URL, and an authentication mechanism, respectively. The first two are straightforward enough. The third parameter tells JNDI that a username and password will be given to bind to LDAP.

The bindDN & bindCredential parameters define a username and password, respectively, that JNDI will use to bind to OpenLdap. In LDAP terminology, the act of binding to a database server means establishing a connection to the database server.

The baseCtxDN provides the LDAP tree node from which to start searching for users (this is sometimes called a base group). In this examle, all users are kept under "ou=Users,dc=thinkmiddleware,dc=com".

The baseFilter is a search filter used to locate the context of the user to authenticate. The username is obtained from a callback and substituted anywhere "{0}" is placed. In our case, a search filter of "cn={0}" is being used. This example uses the ‘cn’ attribute in the DN.

The rolesCtxDN does the same as baseCtxDN, but for Group searches (note, JBoss uses the term Role for LDAP Groups here). The roleFilter parameter is used in the LDAP Search for a user’s groups. The userDN will be substituted in "(member={1}" for "{1}"–similar to how "{0}" is replaced for baseFilter.

The roleAttributeID is the inetorgperson object’s attribute which maps to the name of the the role (LDAP Group).

JNDI Under the covers

The JBoss LdapExtLoginModule uses JNDI under the covers to communicate with OpenLdap. This means that JBoss relies on JNDI System Properties to tune connection pooling and other performance related parameters.

The jboss_start.sh script presented in the original JBoss setup article should be modified to pass the following System Properties:

-Dcom.sun.jndi.ldap.connect.pool.debug=true
-Dcom.sun.jndi.ldap.connect.pool.initsize=1
-Dcom.sun.jndi.ldap.connect.pool.prefsize=3
-Dcom.sun.jndi.ldap.connect.pool.maxsize=5
-Dcom.sun.jndi.ldap.connect.pool.timeout=3600000

This will enable JNDI LDAP Connection Pooling debugging, set an initial pool size of one, set a preferred pool size of three, set a maximum pool size of five, and keep pooled connections open for one hour.

Also, note, that the Login Module entry must be configured to use connection pooling.  The “com.sun.jndi.ldap.connect.pool” property must be set to true in the Login Module configuration in login-config.xml

The new start script will look like:

#!/bin/bash
JAVA_HOME=PATH_TO_JAVA
PATH=$JAVA_HOME/bin:$PATH
export JAVA_HOME PATH CLASSPATH
SERVER_NAME=$1
if [ -d "../server/${SERVER_NAME}/log" ];
then
nohup ./run.sh -Dcom.sun.jndi.ldap.connect.pool.debug=true -Dcom.sun.jndi.ldap.connect.pool.initsize=1 -Dcom.sun.jndi.ldap.connect.pool.prefsize=3 -Dcom.sun.jndi.ldap.connect.pool.maxsize=5 -Dcom.sun.jndi.ldap.connect.pool.timeout=3600000 -c ${SERVER_NAME} > ../server/${SERVER_NAME}/log/out.log 2>../server/${SERVER_NAME}/log/err.log &
else
echo "Not a valid path."
fi
exit 0

Closing

A Security Domain or Security Realm called Subject has been setup in our JBoss container. However, nothing is actually using it yet.
The next article will create a Web Application with a single servlet that is protected by J2EE Security using form-based authentication and the Subject Security Realm that we have just created.

References:

[1] http://java.sun.com/j2ee/j2ee-1_4-fr-spec.pdf
[2] http://java.sun.com/j2ee/tutorial/1_3-fcs/doc/Security9.html#71882
[3] http://jcp.org/aboutJava/communityprocess/final/jsr154/index.html
[4] http://www.jboss.org/community/docs/DOC-11287
[5] http://www.jboss.org/community/docs/DOC-10760
[6] http://thinkmiddleware.com/blog01/2008/11/26/jndi-java-naming-directory-interface/
[7] http://java.sun.com/products/jndi/tutorial/ldap/connect/config.html