Introduction
Implementation of Realm that works with an LDAP directory server
accessed
via the JAVA LDAP SDK APIs. Requires use of IPlanet/Sun Java LDAP SDK
(mozilla
released code). It does not require the use of a particular LDAP server
just the Java SDK from Iplanet/Sun.
The following constraints are imposed on the data structure in the underlying directory server:
Each user that can be authenticated is represented by an individual
element in the top level DirContext that is accessed via the
connectionURL
property. This element has the following characteristics:
The userid
attribute
of this element contains the username that is being presented for
authentication
and is unique. If the DN can be derived from the username being
presented
for authentication then it is possible to locate the record
immediately.
This however will not always be possible and a search is needed. Both
methods
are supported.
The
distinguished
name (lookup) can be represented by a pattern passed to an instance of
MessageFormat, where the string "{0}" in the pattern is replaced by the
username being presented. The search method requires an attribute in
the
directory to locate the username being presented.
The element
for this user contains an attribute named by the userPassword property.
The value of this attribute is retrieved for use in authentication, or
the user is authenticated by attempting to bind with the given
password.
Either method can be used, but many directory administrators do not
make
the userPassword attribute readable so binding as the user is the
preferred
method.
Also-the cost
of computing the encrypted password can be high for an application
server
(tomcat) under load.
If using the
reading the password and comparing method which is not
recommended
the following applies:
The value of
the user password attribute is either a cleartext String, or the result
of passing a cleartext String through the
RealmBase.digest()
method (using the standard digest support included in RealmBase).
The user is
considered to be authenticated if the presented credentials (after
being
passed through RealmBase.digest()) are
equal to the
retrieved value for the user password attribute.
Realm role membership
Each group of users that has been assigned
a particular role is represented by an individual element in the top
level
DirContext that is accessed via the connectionURL property. This
element
has the following characteristics:
The set of all
possible groups of interest can be selected by a search pattern
configured
by the roleSearch property.
The roleSearch
pattern optionally includes pattern replacements "{0}" for the
distinguished
name, and/or "{1}" for the username, of the authenticated user for
which
roles will be retrieved. The roleBase property can be set to the
element
that is the base of the search for matching roles. If not specified,
the
entire context will be searched.
The roleSubtree
property can be set to false if you wish to search only the current
level
of directory context. The default value of true requests a search of
the
whole subtree. The element includes an attribute (whose name is
configured
by the roleName property) containing the name of the role represented
by
this element.
Many directory
administrators also represent group membership by attaching the groups
to the user's record. This eliminates the maintenance of large groups,
and instead allows quick group determination. This method is also
supported.
The requirement here is that the attribute be present in the user's
entry
and contain the names of the roles. An example is given under the the
setRealmRoleType
method. Note that the standard <security-role-ref> element in the
web
application deployment descriptor allows applications to refer to roles
programmatically by names other than those used in the directory server
itself.
Quick Start
To set up Tomcat to use LDAPRealm, you will need to follow these steps:
1. Make sure your directory server is configured with a
schema that matches the requirements listed above.
2. Configure a username and password for use by Tomcat, that has
at least read only access to the information described above. (Tomcat
will not write changes to the directory.) Note that if you are reading
the password from the directory to compare against an entered password
you must have rights to read this field in the directory. This is not a
recommended method of handling authentication.
3. Place a copy of the Iplanet LDAPSDK version 4.17 or later
driver inside (typically ldapjdk.jar) inside the
$CATALINA_HOME/server/lib directory (if you do not need it visible to
web applications) or $CATALINA_HOME/common/lib (if it will be used both
by Tomcat 4 and by your apps-recommended).
4. Place the ldaprealm.jar file in $CATALINA_HOME/server/lib
5. If using ssl to communicate with your LDAP server place the
sun JSSE libraries (jsse.jar, jnet.jar, jcert.jar) in
$CATALINA_HOME/common/lib (only if using jdk 1.3.x or earlier-ssl
support is built in to jdk 1.4.x).
6. Set up a <Realm> element, as described below, in your
$CATALINA_HOME/conf/server.xml file.
7. If using Mbeans with Tomcat version 4.1.18 or higher then
locate the Listener xml entry in your server.xml file and add/modify
the entry (shown in bold) as follows:
<Listener
className="org.apache.catalina.mbeans.ServerLifecycleListener"
descriptors="/org/apache/catalina/realm/LDAPRealm_mbean.xml"
debug="0"/>
7. Restart Tomcat 4 if it is already running.
Realm Element Attributes
To configure LDAPRealm, you will create a <Realm> element and nest it in your CATALINA_HOME/conf/server.xml file, as described above. The following attributes are supported by this implementation:
Attribute | Description |
---|---|
authType | The type of authentication to perform for users.
Choices here
are user (for authenticate against the server), or digest
(retrieve password and compare locally). It is preferred to let the
ldap
server validate authentication credentials using the user
option.
This also does not require the program to retrieve the password
attribute and perform the calculations to build a match string. Default value is "user". |
className | The fully qualified Java class name of this Realm implementation. You MUST specify the value "org.apache.catalina.realm.LDAPRealm" here. |
connectionHost | The connection hostname for the LDAP server. This realm implementation takes advantage of setup of an HA environment for LDAP. The host attribute can contain a single hostname or a series of hostnames seperated by spaces. The syntax for the hostname list is the following: "hostname1 hostname2 hostname3" etc.... You may also specify different ports for each hostname using the following syntax: "hostname1 hostname2:393 hostname3:400" etc.... At least one host should not have a port included as the connectionPort option is required and will be attached to that host. So in the above example the port specified in the connectionPort attribute will be attached to hostname1. Default value for this is "localhost". |
connectionFailOverType | If specifying multiple hostnames for HA, this attribute
allows specifying
how to do failover if a host is determined to be unreachable. Here are
the options:
"serial"-try each host in turn until one is available, this can take as long as 45 seconds or more depending on TCP network settings in your OS. "0-N" seconds-simultaneously try all hosts until one comes back (which is based on which one comes back first and mark it as the one that is active), this option will set a delay between the threads to wait before trying the next. 0 (zero) means try all at once. For an HA environment a good choice is to use a value of between 5-10 seconds for this depending on needs. Default is "serial". |
connectionName | The directory server distinguished username used to establish an LDAP connection, an e.g. might be "cn=admin,o=mycompany.com" |
connectionPassword | The directory server password used to establish a LDAP connection. |
connectionPort | The directory server port number to establish the LDAP connection. Default value is "389". |
connectionType | This value should be either "ldap" or "ldaps" depending on standard LDAP protocol or ssl. Default value is "ldap". |
debug | The level of debugging detail logged by this Realm to the associated Logger. Higher numbers generate more detailed output. If not specified, the default debugging detail level is zero (0). |
digest | The digest algorithm used to store passwords in non-plaintext formats. Valid values are those accepted for the algorithm name by the java.security.MessageDigest class. See Digested Passwords for more information. If not specified, passwords are stored in clear text. |
poolMin | Initial size of the LDAP connection pool desired. Minimum value of 1, maximum cannot be greater than poolMax below. Default is "5" initial pooled connections. |
poolMax | Maximum size the connection pool can grow to. Value must be equal to or greater than poolMin. Default is "20". If you see repeated messages in the log about pool waiting for connections to free you should increase this value. |
realmRoleType | This value should be "user" if you wish to have realm roles determined from the user's ldap record. The value should be "list" if another record contains what roles a user is in. For large numbers of users many directory administrators have found it better to place role membership data directly in each user's record and attach it to a multivalued attribute like memberOf. Default value for this is "user". |
roleAutoFormat | This allows a role to be assigned automatically for any user that succeeds authentication. Where this is useful is for instance you want to have a role auto assigned just to allow access control within your application. Use {0} in a messageformat patter to substitute the full DN, or {1} for the userid. For instance a value here of "role{1}" would cause a automatic role to be created of "roletony" when tony authenticates in. One use I have used is to use "{1}" for allowing a small list of users in the web.xml for specific pieces of the application. Default for this is "". |
roleBase | The location within the directory tree to begin searches for role membership. This only needs to be set if realmRoleType is "list". If not set it will default to the same location as the searchRoot. As an example this can be set to "o=mycompany.com". |
roleName | The name of the directory server attribute containing the role name. For realmRoleType of user this should contain the attribute name in a user's record to determine role membership (e.g. memberOf). For realmRoleType of "list" this should contain the attribute within the role record that gives the actual name of the role (e.g. cn). |
roleSearch | An LDAP search pattern for selecting roles in this Realm, following the syntax supported by the java.text.MessageFormat class. Use {0} to substitute in the distinguished name of the user you want roles for, and/or {1} to substitute in the username of the user you want roles for. An example is "uniquemember={0}" would match all records that have this to find the realmname, the realmname would be derived from the attribute specified in roleName. This attribute should only be set when realmRoleType is "list". |
roleSubtree | Set to true if you want role searches to search subtrees of the element selected by roleBase. Specify false to cause only the top level element to be searched. The default value is "true". |
searchType | The method to match passed values and find them in the LDAP
directory.
Two methods are supported: A value of "search" indicates to
build
a search string using the userPattern string and doing a search for the
value. A value of "buildDN" indicates to build a valid DN
from the passed value by also utilizing the userPattern and trying to
read
the record directly.
BuildDN is the faster method, but not all directory setups have the values necessary to locate the user record directly based just on a passed userid. For example a DN may be first,last name and contain a unique attribute of uid for the record. This kind of setup requires a search. If on the other hand your DN looks like this uid=doe,dc=mycompany,dc=com then this will work for the buildDN strategy. The value chosen here along with the userPattern determine how records will be located within the LDAP directory. Whatever method is chosen you need to ensure that given the value passed by a user that only ONE record is eturned from the the LDAP server. If multiple records are returned then the authentication method will fail the authentication. Default value is "search" however if you can build a dn directly with a userid then use "buildDN". |
searchRoot | The location within the directory tree to begin searches for users. As an example this can be set to "o=mycompany.com". |
sslTrustStore | Specify the full path to the truststore for the server certs CA. No value indicates trust all SSL server connections implicitly. If you are using SSL you can store the server root CA here for validation during ssl connects. Default value for this "" which is to not validate the root ca, but trust it automatically. |
sslTrustStorePassword | Specify the password for the truststore specified in sslTrustStore. If not using a trust store then set this to "". |
userPassword | The name of the directory server attribute (in the user element) that contains the cleartext or digested user password (depending on the setting of the digest attribute). If using authType of digest this value must be specified. Also, the user that is binding to the directory, specified in connectionName must have the authority to read this attribute. It is not recommended to use this method of authentication as it burdens the application server for tasks it should not have to perform. |
userPattern | An LDAP search pattern for selecting users in this Realm, following the syntax supported by the java.text.MessageFormat class. Use {0} to substitute in the distinguished name of the user you want to select. An example is "uid={0}" if using a searchType of user. If you need to construct the full DN based on the user value then you may need to use something of the sort "uid={0},o=mycompany.com". |
Examples
Perhaps the best way to understand proper configuration of the LDAPRealm module is through some examples. Here is a directory schema and then we will follow this with two example setups using it.
Sample realm definition:
debug="0"
connectionHost="ldapserver.host.name"
authType="user"
connectionPort="389"
poolMin="10"
poolMax="20"
searchType="search"
searchRoot="o=mycompany.com"
userPattern="uid={0}"
roleName="uid"
/>
The roleAutoFormat parameter means every user that
authenticates will be automatically placed in a role called autheduser.
This provides a very easy way to force all users to authenticate
without having to setup roles for all users.