| <?xml version="1.0"?> |
| <!-- |
| Licensed to the Apache Software Foundation (ASF) under one or more |
| contributor license agreements. See the NOTICE file distributed with |
| this work for additional information regarding copyright ownership. |
| The ASF licenses this file to You under the Apache License, Version 2.0 |
| (the "License"); you may not use this file except in compliance with |
| the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| --> |
| <!DOCTYPE document [ |
| <!ENTITY project SYSTEM "project.xml"> |
| ]> |
| <document url="fs-jndi-realm.html"> |
| |
| &project; |
| |
| <properties> |
| <author email="craigmcc@apache.org">Craig McClanahan</author> |
| <title>JNDIRealm</title> |
| <revision>$Id$</revision> |
| </properties> |
| |
| <body> |
| |
| |
| <section name="Overview"> |
| |
| |
| <subsection name="Introduction"> |
| |
| <p>The purpose of the <strong>JNDIRealm</strong> implementation is to |
| provide a mechanism by which Tomcat 5 can acquire information needed |
| to authenticate web application users, and define their security roles, |
| from a directory server or other service accessed via JNDI APIs. For |
| integration with Catalina, this class must implement the |
| <code>org.apache.catalina.Realm</code> interface.</p> |
| |
| <p>This specification reflects a combination of functionality that is |
| already present in the <code>org.apache.catalina.realm.JNDIRealm</code> |
| class, as well as requirements for enhancements that have been |
| discussed. Where appropriate, requirements statements are marked |
| <em>[Current]</em> and <em>[Requested]</em> to distinguish them.</p> |
| |
| <p>The current status of this functional specification is |
| <strong>PROPOSED</strong>. It has not yet been discussed and |
| agreed to on the TOMCAT-DEV mailing list.</p> |
| |
| <p>The code in the current version of <code>JNDIRealm</code>, and the |
| ideas expressed in this functional specification, are the results of |
| contributions from many individuals, including (alphabetically):</p> |
| <ul> |
| <li>Holman, John <j.g.holman@qmw.ac.uk></li> |
| <li>Lockhart, Ellen <elockhart@home.com></li> |
| <li>McClanahan, Craig <craigmcc@apache.org></li> |
| </ul> |
| |
| </subsection> |
| |
| |
| <subsection name="External Specifications"> |
| |
| <p>The implementation of this functionality depends on the following |
| external specifications:</p> |
| <ul> |
| <li><a href="http://java.sun.com/products/jndi/">Java Naming and |
| Directory Interface</a> (version 1.2.1 or later)</li> |
| </ul> |
| |
| </subsection> |
| |
| |
| <subsection name="Implementation Requirements"> |
| |
| <p>The implementation of this functionality shall conform to the |
| following requirements:</p> |
| <ul> |
| <li>Be realized in one or more implementation classes.</li> |
| <li>Implement the <code>org.apache.catalina.Realm</code> interface. |
| [Current]</li> |
| <li>Implement the <code>org.apache.catalina.Lifecycle</code> |
| interface. [Current]</li> |
| <li>Subclass the <code>org.apache.catalina.realm.RealmBase</code> |
| base class.</li> |
| <li>Live in the <code>org.apache.catalina.realm</code> package. |
| [Current]</li> |
| <li>Support a configurable debugging detail level. [Current]</li> |
| <li>Log debugging and operational messages (suitably internationalized) |
| via the <code>getContainer().log()</code> method. [Current]</li> |
| </ul> |
| |
| </subsection> |
| |
| |
| </section> |
| |
| |
| <section name="Dependencies"> |
| |
| |
| <subsection name="Environmental Dependencies"> |
| |
| <p>The following environmental dependencies must be met in order for |
| JNDIRealm to operate correctly:</p> |
| <ul> |
| <li>The desire to utilize JNDIRealm must be registered in |
| <code>$CATALINA_HOME/conf/server.xml</code>, in a |
| <code><Realm></code> element that is nested inside a |
| corresponding <code><Engine></code>, <code><Host></code>, |
| or <code><Context></code> element.</li> |
| <li>If the <em>Administrator Login</em> operational mode is selected, |
| the configured administrator username and password must be configured |
| in the corresponding directory server.</li> |
| <li>If the <em>Username Login</em> operational mode is selected, |
| the corresponding directory server must be configured to accept |
| logins with the username and password that will be passed to |
| <code>JNDIRealm</code> by the appropriate <code>Authenticator</code>. |
| </li> |
| </ul> |
| |
| </subsection> |
| |
| |
| <subsection name="Container Dependencies"> |
| |
| <p>Correct operation of JNDIRealm depends on the following |
| specific features of the surrounding container:</p> |
| <ul> |
| <li>Interactions with <code>JNDIRealm</code> will be initiated by |
| the appropriate <code>Authenticator</code> implementation, based |
| on the login method that is selected.</li> |
| <li><code>JNDIRealm</code> must have the JNDI API classes available |
| to it. For a JDK 1.2 container, that means <code>jndi.jar</code> |
| and the appropriate implementation (such as <code>ldap.jar</code>) |
| must be placed in the <code>server/lib</code> directory.</li> |
| </ul> |
| |
| </subsection> |
| |
| |
| </section> |
| |
| |
| <section name="Functionality"> |
| |
| |
| <subsection name="Operational Modes"> |
| |
| <p>The completed <code>JNDIRealm</code> must support two major operational |
| modes in order to support all of the required use cases. For the purposes |
| of this document, the modes are called <em>administrator login</em> and |
| <em>Username Login</em>. They are described further in the following |
| paragraphs.</p> |
| |
| <p>For <em>Administrator Login</em> mode, <code>JNDIRealm</code> will be |
| configured to establish one or more connections (using a connection pool) |
| to an appropriate directory server, using JNDI APIs, under a "system |
| administrator" username and password. This is similar to the approach |
| normally used to configure <code>JDBCRealm</code> to access authentication |
| and access control information in a database. It is assumed that the |
| system administrator username and password that are configured provide |
| sufficient privileges within the directory server to read (but not modify) |
| the username, password, and assigned roles for each valid user of the |
| web application associated with this <code>Realm</code>. The password |
| can be stored in cleartext, or in one of the digested modes supported by |
| the <code>org.apache.catalina.realm.RealmBase</code> base class.</p> |
| |
| <p>For <em>Username Login</em> mode, <code>JNDIRealm</code> does not |
| normally remain connected to the directory server. Instead, whenever a |
| user is to be authenticated, a connection to the directory server |
| (using the username and password received from the authenticator) is |
| attempted. If this connection is successful, the user is assumed to be |
| successfully authenticated. This connection is then utilized to read |
| the corresponding security roles associated with this user, and the |
| connection is then broken.</p> |
| |
| <p><strong>NOTE</strong> - <em>Username Login</em> mode cannot be used |
| if you have selected login method <code>DIGEST</code> in your web |
| application deployment descriptor (<code>web.xml</code>) file. This |
| restriction exists because the cleartext password is never available |
| to the container, so it is not possible to bind to the directory server |
| using the user's username and password.</p> |
| |
| <p>Because these operational modes work so differently, the functionality |
| for each mode will be described separately. Whether or not both modes |
| are actually supported by a single class (versus a class per mode) is |
| an implementation detail left to the designer.</p> |
| |
| <p><strong>NOTE</strong> - The current implementation only implements |
| part of the <em>Administrator Lookup</em> mode requirements. It does |
| not support the <em>Username Lookup</em> mode at all, at this point.</p> |
| |
| </subsection> |
| |
| |
| <subsection name="Administrator Login Mode Functionality"> |
| |
| |
| <h3>Configurable Properties</h3> |
| |
| <p>The implementation shall support the following properties |
| that can be configured with JavaBeans property setters:</p> |
| <ul> |
| <li><code>connectionURL</code> - URL of the directory server we will |
| be contacting.</li> |
| <li><code>contextFactory</code> - Fully qualified class name of the JNDI |
| context factory used to retrieve our InitialContext. |
| [com.sun.jndi.ldap.LdapCtxFactory]</li> |
| <li>Additional configuration properties required to establish the |
| appropriate connection. [Requested]</li> |
| <li>Connection pool configuration properties. [Requested]</li> |
| <li>Configuration properties defining how a particular user is |
| authenticated. The following capabilities should be supported: |
| <ul> |
| <li>Substitute the specified username into a string. [Requested]</li> |
| <li>Retrieve the distinguished name (DN) of an authorized user via an |
| LDAP search string with a replacement placeholder for the |
| username, and comparison of the password to a configurable |
| attribute retrieved from the search result. [Current]</li> |
| </ul></li> |
| <li>Configuration properties defining how the roles associated with a |
| particular authenticated user can be retrieved. The following |
| approaches should be supported: |
| <ul> |
| <li>Retrieve a specified attribute (possibly multi-valued) |
| from an LDAP search expression, |
| with a replacement placeholder for the DN of the user. |
| [Current]</li> |
| <li>Retrieve a set of role names that are defined implicitly (by |
| selecting principals that match a search pattern) rather than |
| explicitly (by finding a particular attribute value). |
| [Requested]</li> |
| </ul></li> |
| </ul> |
| |
| <h3>Lifecycle Functionality</h3> |
| |
| <p>The following processing must be performed when the <code>start()</code> |
| method is called:</p> |
| <ul> |
| <li>Establish a connection to the configured directory server, using the |
| configured system administrator username and password. [Current]</li> |
| <li>Configure and establish a connection pool of connections to the |
| directory server. [Requested]</li> |
| </ul> |
| |
| <p>The following processing must be performed when the <code>stop()</code> |
| method is called:</p> |
| <ul> |
| <li>Close any opened connections to the directory server.</li> |
| </ul> |
| |
| |
| <h3>Method authenticate() Functionality</h3> |
| |
| <p>When <code>authenticate()</code> is called, the following processing |
| is required:</p> |
| <ul> |
| <li>Acquire the one and only connection [Current] or acquire a connection |
| from the connection pool [Requested].</li> |
| <li>Authenticate the user by retrieving the user's Distinguished Name, |
| based on the specified username and password.</li> |
| <li>If the user was not authenticated, release the allocated connection |
| and return <code>null</code>.</li> |
| <li>Acquire a <code>List</code> of the security roles assigned to the |
| authenticated user.</li> |
| <li>Construct a new instance of class |
| <code>org.apache.catalina.realm.GenericPrincipal</code>, passing as |
| constructor arguments: this realm instance, the authenticated |
| username, and a <code>List</code> of the security roles associated |
| with this user.</li> |
| <li><strong>WARNING</strong> - Do not attempt to cache and reuse previous |
| <code>GenericPrincipal</code> objects for a particular user, because |
| the information in the directory server might have changed since the |
| last time this user was authenticated.</li> |
| <li>Return the newly constructed <code>GenericPrincipal</code>.</li> |
| </ul> |
| |
| |
| <h3>Method hasRole() Functionality</h3> |
| |
| <p>When <code>hasRole()</code> is called, the following processing |
| is required:</p> |
| <ul> |
| <li>The <code>principal</code> that is passed as an argument SHOULD |
| be one that we returned (instanceof class |
| <code>org.apache.catalina.realm.GenericPrincipal</code>, with a |
| <code>realm</code> property that is equal to our instance.</li> |
| <li>If the passed <code>principal</code> meets these criteria, check |
| the specified role against the list returned by |
| <code>getRoles()</code>, and return <code>true</code> if the |
| specified role is included; otherwise, return <code>false</code>.</li> |
| <li>If the passed <code>principal</code> does not meet these criteria, |
| return <code>false</code>.</li> |
| </ul> |
| |
| </subsection> |
| |
| |
| <subsection name="Username Login Mode Functionality"> |
| |
| <h3>Configurable Properties</h3> |
| |
| <p>The implementation shall support the following properties |
| that can be configured with JavaBeans property setters:</p> |
| <ul> |
| <li><code>connectionURL</code> - URL of the directory server we will |
| be contacting.</li> |
| <li><code>contextFactory</code> - Fully qualified class name of the JNDI |
| context factory used to retrieve our InitialContext. |
| [com.sun.jndi.ldap.LdapCtxFactory]</li> |
| <li>Additional configuration properties required to establish the |
| appropriate connection. [Requested]</li> |
| <li>Connection pool configuration properties. [Requested]</li> |
| <li>Configuration properties defining if and how a user might be looked |
| up before binding to the directory server. The following approaches |
| should be supported: |
| <ul> |
| <li>No previous lookup is required - username specified by the user |
| is the same as that used to authenticate to the directory |
| server.</li> |
| <li>Substitute the specified username into a string.</li> |
| <li>Search the directory server based on configured criteria to |
| retrieve the distinguished name of the user, then attempt to |
| bind with that distinguished name.</li> |
| </ul></li> |
| <li>Configuration properties defining how the roles associated with a |
| particular authenticated user can be retrieved. The following |
| approaches should be supported: |
| <ul> |
| <li>Retrieve a specified attribute (possibly multi-valued) |
| from an LDAP search expression, |
| with a replacement placeholder for the DN of the user. |
| [Current]</li> |
| </ul></li> |
| </ul> |
| |
| <h3>Lifecycle Functionality</h3> |
| |
| <p>The following processing must be performed when the <code>start()</code> |
| method is called:</p> |
| <ul> |
| <li>None required.</li> |
| </ul> |
| |
| <p>The following processing must be performed when the <code>stop()</code> |
| method is called:</p> |
| <ul> |
| <li>None required.</li> |
| </ul> |
| |
| |
| <h3>Method authenticate() Functionality</h3> |
| |
| <p>When <code>authenticate()</code> is called, the following processing |
| is required:</p> |
| <ul> |
| <li>Attempt to bind to the directory server, using the username and |
| password provided by the user.</li> |
| <li>If the user was not authenticated, release the allocated connection |
| and return <code>null</code>.</li> |
| <li>Acquire a <code>List</code> of the security roles assigned to the |
| authenticated user.</li> |
| <li>Construct a new instance of class |
| <code>org.apache.catalina.realm.GenericPrincipal</code>, passing as |
| constructor arguments: this realm instance, the authenticated |
| username, and a <code>List</code> of the security roles associated |
| with this user.</li> |
| <li><strong>WARNING</strong> - Do not attempt to cache and reuse previous |
| <code>GenericPrincipal</code> objects for a particular user, because |
| the information in the directory server might have changed since the |
| last time this user was authenticated.</li> |
| <li>Return the newly constructed <code>GenericPrincipal</code>.</li> |
| </ul> |
| |
| |
| <h3>Method hasRole() Functionality</h3> |
| |
| <p>When <code>hasRole()</code> is called, the following processing |
| is required:</p> |
| <ul> |
| <li>The <code>principal</code> that is passed as an argument SHOULD |
| be one that we returned (instanceof class |
| <code>org.apache.catalina.realm.GenericPrincipal</code>, with a |
| <code>realm</code> property that is equal to our instance.</li> |
| <li>If the passed <code>principal</code> meets these criteria, check |
| the specified role against the list returned by |
| <code>getRoles()</code>, and return <code>true</code> if the |
| specified role is included; otherwise, return <code>false</code>.</li> |
| <li>If the passed <code>principal</code> does not meet these criteria, |
| return <code>false</code>.</li> |
| </ul> |
| |
| </subsection> |
| |
| </section> |
| |
| |
| <section name="Testable Assertions"> |
| |
| <p>In addition the the assertions implied by the functionality requirements |
| listed above, the following additional assertions shall be tested to |
| validate the behavior of <code>JNDIRealm</code>:</p> |
| <ul> |
| </ul> |
| |
| </section> |
| |
| |
| </body> |
| |
| </document> |