Skip to main content

Tomcat and OpenLDAP, from Configuration to Application

May 31, 2005

{cs.r.title}









Contents
What Is LDAP?
OpenLDAP Setup and Configuration
JXplorer: Visual LDAP
Tomcat Configuration for OpenLDAP
Tomcat Server Configuration
Web Application Configuration
Jakarta TagLibs
Conclusion
Resources

Almost all Java web applications require some type of secured
access, and this is usually addressed via a
Lightweight Directory
Access Protocol
(LDAP) directory. As a developer, it is
advantageous to have a local OpenLDAP directory and web container
to enhance productivity. Configuring Tomcat to connect to an
OpenLDAP directory is a relatively straightforward
if the process is understood. This article details
that process, and examines the Apache Jakarta project tag library
(taglib) to demonstrate an easy way to test the security
mechanisms.

What Is LDAP?

LDAP is many things to many people. For the purposes of this
article, it is simply a common protocol to allow a user to be
authenticated via a user identifier and password. In other words,
it provides authentication. In addition, it also
allows a user to be authorized for access to specific
application areas. This is an authorization process. There
is a clear distinction between authentication and authorization.
They are not the same thing.

LDAP directories are, in theory, interchangeable. However, there
are configuration changes to be taken into account. An LDAP
directory is organized into a tree hierarchy consisting of some of the
following levels:

  • The Root (the source of the tree), which branches out
    to
  • Countries, each of which branches out to
  • Organizations, which branch out to
  • Organizational units (divisions, departments, areas,
    etc)., which branches out to (finally)
  • Individuals.

LDAP directories can import and export their contents via the
Lightweight Directory Interchange Format (LDIF) file format.
LDIF is a text file format that can be used to exchange information
between LDAP directories, or for this case, to initially configure
a directory. Since it is open source,
OpenLDAP is the directory
server of choice for many developers.

OpenLDAP Setup and Configuration

After downloading and installing OpenLDAP, the slapd.conf
file in the installation folder requires some rather important
changes.

Begin by including the InetOrgPerson schema. This schema
has many useful attributes predefined for network users, including
the uid attribute for a specific user's logon ID. The
InetOrgPerson schema has a dependency on the Cosine
schema, hence its inclusion.

[prettify]ucdata-path C:/openldap/ucdata
include C:/openldap/etc/schema/core.schema
include C:/openldap/etc/schema/cosine.schema
include C:/openldap/etc/schema/inetorgperson.schema
<i>......... snip .......</i>
database bdb
suffix dc=&quot;mycompany&quot;,dc=&quot;com&quot;
rootdn &quot;cn=Manager,dc=mycompany,dc=com&quot;
rootpw secret
directory       C:/openldap/var/openldap-data
index   objectClass     eq
[/prettify]

The second set of changes tells OpenLDAP which type of database to
use and what the root distinguished name and password are. These
are the keys to the city, so choose them as you would any other
password.

Once all changes have been made to the slapd.conf file,
it is time to start OpenLDAP. Go to a command prompt and change
directory to the install folder (c:\openldap by default).
Issue the command .\slapd -d 1 and the server should
start much like Figure 1.

Figure 1
Figure 1. OpenLDAP startup
(click for full-size image)

If the server issues an error and fails to start, check that the
schema entries (above) have been added to the slapd.conf file.
Also, if OpenLDAP was not installed in its default location, verify
that the paths are correct for this location. Once the server is
running, we can verify the installation. Open another command
prompt and issue the command

ldapsearch -x -b
"dc=mycompany,dc=com" "(objectclass=*)"
. The return should
look much like Figure 2.

Figure 2
Figure 2. OpenLDAP is working
(click for full-size image)

To populate the directory, a LDIF file will be imported into it.
The format is very unforgiving, so there is an LDIF file available for
download. To import the file, use a command prompt to execute

ldapadd -x -D "cn=Manager,dc=mycompany,dc=com" -W -f
setup.ldif
and you should be prompted for your password, as
configured in the slapd.conf file. Once the password
is entered, the results should be as they are in Figure 3.

Figure 3
Figure 3. ldapadd in action
(click for full-size image)

That was all very nice, but what did these actions accomplish?
Well, basically ldapadd added two organizational
units (people and roles), three user roles (under the roles OU) and
three users (under the people OU). These users were also assigned
to different roles. The hierarchy that is created by importing the
LDIF import follows:

  • com
    • mycompany
      • people
        • admin
        • jbloggs
        • sspecial
      • roles
        • Admin Users
          • uid=admin/ou=people/...
        • Special Users
          • uid=sspecial/ou=people/...
        • Test Users
          • uid=sspecial/ou=people/...
          • uid=jbloggs/ou=people/...
          • uid=admin/ou=people/...

You can now issue another

ldapsearch -x -b
"dc=mycompany,dc=com" "(objectclass=*)"
, and this time the
output should contain something more meaningful (like user attributes and
role memberships), as in Figure 4. While all of this command-line typing
is adequate, a rather good open source Java Swing LDAP browser
named JXplorer can help.

Figure 4
Figure 4. ldapsearch returns users and roles (click for full-size image)

JXplorer: Visual LDAP

JXplorer
is a LDAP browser. It can be used to view, add, change, and delete
any of the elements or attributes in the directory. It can even be
used to change passwords, if necessary. Install JXplorer and run
jxplorer.bat or jxplorer.sh, depending upon your
operating system. Once it's running, connect to your local OpenLDAP
directory using your root user and password, as shown in Figure
5.

JXplorer connection screen
Figure 5. JXplorer connection screen

JXplorer can be used to browse the directory and change any
values. Be careful when changing anything but user attributes (email,
password, etc.) until you are comfortable with LDAP. See Figure 6
for an example.

Figure 6
Figure 6. JXplorer browsing the LDAP directory (click for full-size image)

Tomcat Configuration for OpenLDAP

The Servlet Specification v2.3 allows an application server to
use container-managed security to connect to an existing
user repository for the purposes of authentication and
authorization. There is not, however, a standard way to
accomplish this among different containers. Each container can
(and usually does) implement this in different way. The Tomcat
container uses the notion of a Realm for this type of
security. A Realm can be described as a "repository"
of user names and passwords that uniquely identifies valid users of
a web application. There are four realm types in the Tomcat 4
container and each type stores and retrieves user data and passwords
in a different type of repository.

  • JDBCRealm uses a relational database.
  • DataSourceRealm uses a relational database via a
    JNDI connection.
  • JNDIRealm uses a JNDI connection, usually a LDAP
    directory.
  • MemoryRealm uses an in-memory file, usually
    tomcat-users.xml.

Tomcat's default Realm is a
MemoryRealm, which is configured via the
tomcat-users.xml file. Tomcat needs to be configured for a
JNDIRealm to allow authentication via a LDAP
directory.

There are several steps needed to configure Tomcat to use the
JNDIRealm.

  • Set up the realm in the server.xml file.
  • Create a JSP login form.
  • Set up any <security-constraint>s to protect
    resources in the web.xml file.
  • Set up the <login-config> to use the login
    form in the web.xml file.
  • Set up all of the <security-role> mappings in
    the web.xml file.

Tomcat Server Configuration

Each application can have the Realm set up inside
of its respective <Context> element; the realm
is available only to that application. However, the realm
can also be set up at the <Engine> or
<Host> levels. Each of these has an impact on
the behavior, or scope, of the realm. This allows easy sharing of a
single realm over several applications.

Here is a server.xml realm that will allow an application
to connect to OpenLDAP.

[prettify]&lt;Realm className=&quot;org.apache.catalina.realm.JNDIRealm&quot; 
    debug=&quot;99&quot;
    connectionName=&quot;cn=Manager,dc=mycompany,dc=com&quot;
    connectionPassword=&quot;secret&quot;
    connectionURL=&quot;ldap://localhost:389&quot;
    roleBase=&quot;ou=roles,dc=mycompany,dc=com&quot;
    roleName=&quot;cn&quot;
    roleSearch=&quot;(uniqueMember={0})&quot;
    roleSubtree=&quot;false&quot;
    userSearch=&quot;(uid={0})&quot;
    userPassword=&quot;userPassword&quot;
    userPattern=&quot;uid={0},ou=people,dc=mycompany,dc=com&quot;
/&gt;
[/prettify]

The userSearch and userPattern
attribute values are the main sources of confusion and errors when
configuring LDAP declaratively. Pay particular attention to these
attributes if you are using anything other than OpenLDAP.

Web Application Configuration

To complete the OpenLDAP configuration for Tomcat, the
application's web.xml file must be updated. The application
available for download consists of six JSP pages, three of which
are protected for the various roles set up in the LDAP directory.
The application must be configured to allow form-based
authentication and the application must be told what roles exist.
First, create a login.jsp file. When using form-based
authentication, this JSP must contain the following.

[prettify]&lt;body&gt;
    &lt;form method=&quot;POST&quot; action=&quot;j_security_check&quot;&gt;
        &lt;input type=&quot;text&quot; name=&quot;j_username&quot;&gt;
        &lt;br&gt;
        &lt;input type=&quot;password&quot; name=&quot;j_password&quot;&gt;
        &lt;br&gt;
        &lt;input type=&quot;submit&quot;&gt;
    &lt;/form&gt;
&lt;/body&gt; 
[/prettify]

The web.xml file needs to be updated to reference the new
OpenLDAP realm and to use the roles designated by it. The
application needs to allow public access to the login.jsp page;
otherwise, no user could ever log in.

To inform the application which resources these roles are
allowed to access, URL mappings in the application (or security
constraints) are used. These mappings can be either a file name
(/admin.jsp) or a path (/jsp/* will protect everything
in the jsp folder). The following XML indicates that both of the
listed .jsp files are not to be protected.

[prettify]&lt;security-constraint&gt;
    &lt;web-resource-collection&gt;
        &lt;web-resource-name&gt;Public Area&lt;/web-resource-name&gt;
        &lt;!-- Define the context-relative URL(s) to be protected --&gt;
        &lt;url-pattern&gt;/index.jsp&lt;/url-pattern&gt;
        &lt;url-pattern&gt;/login.jsp&lt;/url-pattern&gt;
    &lt;/web-resource-collection&gt;
&lt;/security-constraint&gt;
[/prettify]

Why does this code mark the .jsps as unprotected? This is due to
something that is missing, rather than something that is present.
The web.xml snippet below shows that the
user.jsp resource is protected, and which roles can
access it.

[prettify]&lt;security-constraint&gt;
    &lt;web-resource-collection&gt;
        &lt;web-resource-name&gt;Protected Area&lt;/web-resource-name&gt;
        &lt;!-- Define the context-relative URL(s) to be protected --&gt;
        &lt;url-pattern&gt;/user.jsp&lt;/url-pattern&gt;
    &lt;/web-resource-collection&gt;
    &lt;auth-constraint&gt;
    &lt;!-- Anyone with one of the listed roles may access this area --&gt;
        &lt;role-name&gt;Test Users&lt;/role-name&gt;
        &lt;role-name&gt;Special Users&lt;/role-name&gt;
        &lt;role-name&gt;Admin Users&lt;/role-name&gt;
     &lt;/auth-constraint&gt;
&lt;/security-constraint&gt;
[/prettify]

Notice that the protected resource has another section,
<auth-constraint>, that specifies which
application roles can access the resource. If an
<auth-constraint> is present, then the
resource(s) are secured. If not, they are public. Unless the entire
application is protected (i.e.,
<url-pattern>/</url-pattern>), the public
security constraint is redundant.

To configure the web.xml file to utilize the login.jsp
created earlier, add the following to the web.xml file.

[prettify]&lt;!--  uses form-based authentication --&gt;
&lt;login-config&gt;
    &lt;auth-method&gt;FORM&lt;/auth-method&gt;
    &lt;form-login-config&gt;
        &lt;form-login-page&gt;/login.jsp&lt;/form-login-page&gt;
        &lt;form-error-page&gt;/fail_login.html&lt;/form-error-page&gt;
    &lt;/form-login-config&gt;
&lt;/login-config&gt;
[/prettify]

There is one additional step left to make in web.xml:
the application needs to be informed of the roles that we are
using.

[prettify]&lt;!-- Security roles referenced by this web application --&gt;
&lt;security-role&gt;
    &lt;role-name&gt;Test Users&lt;/role-name&gt;
&lt;/security-role&gt;
&lt;security-role&gt;
    &lt;role-name&gt;Special Users&lt;/role-name&gt;
&lt;/security-role&gt;
&lt;security-role&gt;
    &lt;role-name&gt;Admin Users&lt;/role-name&gt;
&lt;/security-role&gt;
[/prettify]

Tomcat has now been configured to use OpenLDAP and our
application has been set up correctly in the web.xml file. The
first time a user navigates to a resource listed in any
<security-constraint> (other than public
resources) ,the server will automatically display the login.jsp for
the user to authenticate. If the user fails to authenticate
successfully, the page specified in the
<form-error-page> will be displayed. Should a
user be successfully authenticated, but is not authorized
for access to the resource (due to a lack of role membership, for
example), the server returns a 403 error page. Error
pages can be customized if so desired in the web.xml file, using
the <error-code> element.

Note that, the web.xml has a specific order for the
elements (defined by a DTD), so you should take a look at the
complete web.xml available in the sample code (in the
Resources section below). This is
especially true of the <login-config> and
<security-role> sections.

A user can now log in, but what about a user logging out? Code
could be written to invalidate the users' session, or we could use
the handy session taglib supplied by the Apache
Jakarta people.

Jakarta TagLibs

By adding a logout.jsp to the application and using the
session taglib for the Apache Jakarta Project, we can
invalidate our user without the need from any custom code (in a
real-world application, you may need to do some further clean up of
the user's session, so this may not suffice).

[prettify]&lt;body&gt;
    &lt;sess:invalidate/&gt;
    You are now logged out&lt;br&gt;
    &lt;a href=&quot;index.jsp&quot;&gt;Return to index&lt;/a&gt;
&lt;/body&gt;
[/prettify]

Once the JSP page with the <sess:invalidate/>
tag is displayed, the user session is removed and the user is
effectively logged out. Simply putting a link to the
logout.jsp page, and having a user navigate to it, is sufficient
for this simple application. In addition to the session taglib, we
can also utilize the request taglib to customize the content of the
JSP based on the user role or roles.

[prettify]&lt;req:isUserInRole role=&quot;Admin Users&quot;&gt;
    The remote user is in role &quot;Admin Users&quot;.&lt;br /&gt;
&lt;/req:isUserInRole&gt;
[/prettify]

The preceding JSP snippet will only display if the user is in
the required role. To verify the behavior of the role security,
JXplorer can be used to quickly add and remove users from
roles.

Conclusion

Having a LDAP directory available on a local workstation can be
a valuable resource for a developer. No more asking the network
administrators for access, or more likely, to create test accounts
for you. All of these tasks can be done in seconds instead of days
or weeks. That isn't to say LDAP is simple. It isn't; it is a
powerful and complex system. But with a little know-how and the
right tools, it is fairly straightforward to set up.

Thanks to the cross-platform nature of both Tomcat and OpenLDAP,
the continuous build servers, CruiseControl, Anthill, etc., can be
kept completely separate from any production systems. This type of
decoupling has advantages for all.

Worth noting is the fact that all LDAP servers are not equal.
For instance, OpenLDAP does not support the memberOf
attribute for role membership checking, which can be very
irritating at times. The OpenLDAP realm for Tomcat will not
necessarily work if you change Directory servers and the associated
values. That is probably the single biggest challenge when using
LDAP, but by using ldapsearch you can eventually figure
out the values needed.

Resources

width="1" height="1" border="0" alt=" " />
Darren Duke is the lead software development consultant with the company he founded Simplified Technology Solutions, Inc based in Atlanta, GA.
Related Topics >> JSP   |   Security   |