yoavs       2004/09/21 16:01:50

  Modified:    catalina/src/share/org/apache/catalina/realm Tag: TOMCAT_5_0
                        JAASCallbackHandler.java JAASRealm.java
                        LocalStrings.properties
               webapps/docs Tag: TOMCAT_5_0 changelog.xml
  Log:
  Bugzilla 28631: JAASRealm enhancements to allow custom user and group principal 
classes (and use commons-logging)
  
  Revision  Changes    Path
  No                   revision
  No                   revision
  1.3.2.1   +42 -11    
jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/realm/JAASCallbackHandler.java
  
  Index: JAASCallbackHandler.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/realm/JAASCallbackHandler.java,v
  retrieving revision 1.3
  retrieving revision 1.3.2.1
  diff -u -r1.3 -r1.3.2.1
  --- JAASCallbackHandler.java  29 Feb 2004 12:38:47 -0000      1.3
  +++ JAASCallbackHandler.java  21 Sep 2004 23:01:50 -0000      1.3.2.1
  @@ -25,25 +25,38 @@
   import javax.security.auth.callback.PasswordCallback;
   import javax.security.auth.callback.UnsupportedCallbackException;
   
  +import org.apache.catalina.util.StringManager;
  +import org.apache.commons.logging.Log;
  +import org.apache.commons.logging.LogFactory;
   
   /**
  - * <p>Implementation of the JAAS <strong>CallbackHandler</code> interface,
  + * <p>Implementation of the JAAS <code>CallbackHandler</code> interface,
    * used to negotiate delivery of the username and credentials that were
    * specified to our constructor.  No interaction with the user is required
    * (or possible).</p>
    *
  + * <p>This <code>CallbackHandler</code> will pre-digest the supplied
  + * password, if required by the <code>&lt;Realm&gt;</code> element in 
  + * <code>server.xml</code>.</p>
  + * <p>At present, <code>JAASCallbackHandler</code> knows how to handle callbacks of
  + * type <code>javax.security.auth.callback.NameCallback</code> and
  + * <code>javax.security.auth.callback.PasswordCallback</code>.</p>
  + *
    * @author Craig R. McClanahan
  + * @author Andrew R. Jaquith
    * @version $Revision$ $Date$
    */
   
   public class JAASCallbackHandler implements CallbackHandler {
  -
  +    private static Log log = LogFactory.getLog(JAASCallbackHandler.class);
   
       // ------------------------------------------------------------ Constructor
   
   
       /**
        * Construct a callback handler configured with the specified values.
  +     * Note that if the <code>JAASRealm</code> instance specifies digested 
passwords,
  +     * the <code>password</code> parameter will be pre-digested here.
        *
        * @param realm Our associated JAASRealm instance
        * @param username Username to be authenticated with
  @@ -55,13 +68,27 @@
           super();
           this.realm = realm;
           this.username = username;
  -        this.password = password;
   
  +        if (realm.hasMessageDigest()) {
  +            this.password = realm.digest(password);
  +            if (log.isDebugEnabled()) {
  +                log.debug(sm.getString("jaasCallback.digestpassword", password, 
this.password));
  +            }
  +        }
  +        else {
  +            this.password = password;
  +        }
       }
   
   
       // ----------------------------------------------------- Instance Variables
   
  +    /**
  +     * The string manager for this package.
  +     */
  +    protected static final StringManager sm =
  +        StringManager.getManager(Constants.Package);
  +
   
       /**
        * The password to be authenticated with.
  @@ -85,11 +112,11 @@
   
   
       /**
  -     * Retrieve the information requested in the provided Callbacks.  This
  -     * implementation only recognizes <code>NameCallback</code> and
  +     * Retrieve the information requested in the provided <code>Callbacks</code>.
  +     * This implementation only recognizes <code>NameCallback</code> and
        * <code>PasswordCallback</code> instances.
        *
  -     * @param callbacks The set of callbacks to be processed
  +     * @param callbacks The set of <code>Callback</code>s to be processed
        *
        * @exception IOException if an input/output error occurs
        * @exception UnsupportedCallbackException if the login method requests
  @@ -101,12 +128,16 @@
           for (int i = 0; i < callbacks.length; i++) {
   
               if (callbacks[i] instanceof NameCallback) {
  -                if (realm.getDebug() >= 3)
  -                    realm.log("Returning username " + username);
  +                if (log.isDebugEnabled()) {
  +                    log.debug(sm.getString("jaasCallback.username", username));
  +                }
  +
                   ((NameCallback) callbacks[i]).setName(username);
               } else if (callbacks[i] instanceof PasswordCallback) {
  -                if (realm.getDebug() >= 3)
  -                    realm.log("Returning password " + password);
  +                if (log.isDebugEnabled()) {
  +                    log.debug(sm.getString("jaasCallback.password", password));
  +                }
  +
                   final char[] passwordcontents;
                   if (password != null) {
                       passwordcontents = password.toCharArray();
  
  
  
  1.6.2.3   +171 -50   
jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/realm/JAASRealm.java
  
  Index: JAASRealm.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/realm/JAASRealm.java,v
  retrieving revision 1.6.2.2
  retrieving revision 1.6.2.3
  diff -u -r1.6.2.2 -r1.6.2.3
  --- JAASRealm.java    20 Sep 2004 16:11:47 -0000      1.6.2.2
  +++ JAASRealm.java    21 Sep 2004 23:01:50 -0000      1.6.2.3
  @@ -22,7 +22,10 @@
   import java.security.acl.Group;
   import java.util.ArrayList;
   import java.util.Enumeration;
  +import java.util.HashMap;
   import java.util.Iterator;
  +import java.util.List;
  +import java.util.Map;
   
   import javax.security.auth.Subject;
   import javax.security.auth.login.AccountExpiredException;
  @@ -62,7 +65,7 @@
    * this Realm:</p>
    * <ul>
    * <li>The JAAS <code>LoginModule</code> is assumed to return a
  - *     <code>Subject with at least one <code>Principal</code> instance
  + *     <code>Subject</code> with at least one <code>Principal</code> instance
    *     representing the user himself or herself, and zero or more separate
    *     <code>Principals</code> representing the security roles authorized
    *     for this user.</li>
  @@ -87,10 +90,40 @@
    * <li>It is a configuration error for the JAAS login method to return a
    *     validated <code>Subject</code> without a <code>Principal</code> that
    *     matches the "user classes" list.</li>
  + * <li>By default, the enclosing Container's name serves as the
  + *     application name used to obtain the JAAS LoginContext ("Catalina" in
  + *     a default installation). Tomcat must be able to find an application
  + *     with this name in the JAAS configuration file. Here is a hypothetical
  + *     JAAS configuration file entry for a database-oriented login module that uses 
  + *     a Tomcat-managed JNDI database resource:
  + *     <blockquote><pre>Catalina {
  +org.foobar.auth.DatabaseLoginModule REQUIRED
  +    JNDI_RESOURCE=jdbc/AuthDB
  +  USER_TABLE=users
  +  USER_ID_COLUMN=id
  +  USER_NAME_COLUMN=name
  +  USER_CREDENTIAL_COLUMN=password
  +  ROLE_TABLE=roles
  +  ROLE_NAME_COLUMN=name
  +  PRINCIPAL_FACTORY=org.foobar.auth.impl.SimplePrincipalFactory;
  +};</pre></blockquote></li>
  + * <li>To set the JAAS configuration file
  + *     location, set the <code>CATALINA_OPTS</code> environment variable
  + *     similar to the following:
  
+<blockquote><code>CATALINA_OPTS="-Djava.security.auth.login.config=$CATALINA_HOME/conf/jaas.config"</code></blockquote>
  + * </li>
  + * <li>As part of the login process, JAASRealm registers its own 
<code>CallbackHandler</code>,
  + *     called (unsurprisingly) <code>JAASCallbackHandler</code>. This handler 
supplies the 
  + *     HTTP requests's username and credentials to the user-supplied 
<code>LoginModule</code></li>
  + * <li>As with other <code>Realm</code> implementations, digested passwords are 
supported if
  + *     the <code>&lt;Realm&gt;</code> element in <code>server.xml</code> contains a 
  + *     <code>digest</code> attribute; <code>JAASCallbackHandler</code> will digest 
the password
  + *     prior to passing it back to the <code>LoginModule</code></li>  
    * </ul>
    *
    * @author Craig R. McClanahan
    * @author Yoav Shapira
  + * @author Andrew R. Jaquith
    * @version $Revision$ $Date$
    */
   
  @@ -104,13 +137,13 @@
   
       /**
        * The application name passed to the JAAS <code>LoginContext</code>,
  -     * which uses it to select the set of relevant <code>LoginModules</code>.
  +     * which uses it to select the set of relevant <code>LoginModule</code>s.
        */
       protected String appName = null;
   
   
       /**
  -     * Descriptive information about this Realm implementation.
  +     * Descriptive information about this <code>Realm</code> implementation.
        */
       protected static final String info =
           "org.apache.catalina.realm.JAASRealm/1.0";
  @@ -125,7 +158,7 @@
       /**
        * The list of role class names, split out for easy processing.
        */
  -    protected ArrayList roleClasses = new ArrayList();
  +    protected List roleClasses = new ArrayList();
   
   
       /**
  @@ -138,7 +171,14 @@
       /**
        * The set of user class names, split out for easy processing.
        */
  -    protected ArrayList userClasses = new ArrayList();
  +     protected List userClasses = new ArrayList();
  +
  +    /**
  +     * Map associating each user <code>Principal</code> object
  +     * with an array of role <code>Principal</code>s. 
  +     * This Map is read when <code>hasRole</code> is called.
  +     */
  +    protected Map roleMap = new HashMap();
   
       /**
        * Whether to use context ClassLoader or default ClassLoader.
  @@ -152,15 +192,15 @@
   
       
       /**
  -     * setter for the appName member variable
  -     * @deprecated JAAS should use the Engine ( domain ) name and webpp/host 
overrides
  +     * setter for the <code>appName</code> member variable
  +     * @deprecated JAAS should use the <code>Engine</code> (domain) name and 
webpp/host overrides
        */
       public void setAppName(String name) {
           appName = name;
       }
       
       /**
  -     * getter for the appName member variable
  +     * getter for the <code>appName</code> member variable
        */
       public String getAppName() {
           return appName;
  @@ -201,7 +241,7 @@
       }
   
       /**
  -     * Comma-delimited list of <code>javax.security.Principal</code> classes
  +     * Comma-delimited list of <code>java.security.Principal</code> classes
        * that represent security roles.
        */
       protected String roleClassNames = null;
  @@ -210,6 +250,14 @@
           return (this.roleClassNames);
       }
   
  +     /**
  +     * Sets the list of comma-delimited classes that represent 
  +     * roles. The classes in the list must implement 
<code>java.security.Principal</code>.
  +     * When this accessor is called (for example, by a <code>Digester</code>
  +     * instance parsing the
  +     * configuration file), it will parse the class names and store the resulting
  +     * string(s) into the <code>ArrayList</code> field </code>roleClasses</code>.
  +     */
       public void setRoleClassNames(String roleClassNames) {
           this.roleClassNames = roleClassNames;
           roleClasses.clear();
  @@ -233,7 +281,7 @@
   
   
       /**
  -     * Comma-delimited list of <code>javax.security.Principal</code> classes
  +     * Comma-delimited list of <code>java.security.Principal</code> classes
        * that represent individual users.
        */
       protected String userClassNames = null;
  @@ -242,6 +290,14 @@
           return (this.userClassNames);
       }
   
  +     /**
  +     * Sets the list of comma-delimited classes that represent individual
  +     * users. The classes in the list must implement 
<code>java.security.Principal</code>.
  +     * When this accessor is called (for example, by a <code>Digester</code>
  +     * instance parsing the
  +     * configuration file), it will parse the class names and store the resulting
  +     * string(s) into the <code>ArrayList</code> field </code>userClasses</code>.
  +     */
       public void setUserClassNames(String userClassNames) {
           this.userClassNames = userClassNames;
           userClasses.clear();
  @@ -268,7 +324,7 @@
   
   
       /**
  -     * Return the Principal associated with the specified username and
  +     * Return the <code>Principal</code> associated with the specified username and
        * credentials, if there is one; otherwise return <code>null</code>.
        *
        * If there are any errors with the JDBC connection, executing
  @@ -276,7 +332,7 @@
        * event is also logged, and the connection will be closed so that
        * a subsequent request will automatically re-open it.
        *
  -     * @param username Username of the Principal to look up
  +     * @param username Username of the <code>Principal</code> to look up
        * @param credentials Password or other credentials to use in
        *  authenticating this username
        */
  @@ -288,7 +344,7 @@
           if( appName==null ) appName="Tomcat";
   
           if( log.isDebugEnabled())
  -            log.debug("Authenticating " + appName + " " +  username);
  +            log.debug(sm.getString("jaasRealm.beginLogin", username, appName));
   
           // What if the LoginModule is in the container class loader ?
           ClassLoader ocl = null;
  @@ -345,7 +401,7 @@
           }
   
           if( log.isDebugEnabled())
  -            log.debug("Getting principal " + subject);
  +            log.debug(sm.getString("jaasRealm.loginContextCreated", username));
   
           // Return the appropriate Principal for this authenticated Subject
           Principal principal = createPrincipal(username, subject);
  @@ -364,6 +420,53 @@
           }
       }
   
  +     /**
  +     * Returns <code>true</code> if the specified user <code>Principal</code> has 
the specified
  +     * security role, within the context of this <code>Realm</code>; otherwise 
return
  +     * <code>false</code>. This will be true when 
  +     * an associated role <code>Principal</code> can be found whose 
<code>getName</code>
  +     * method returns a <code>String</code> equalling the specified role.
  +     * @param principal <code>Principal</code> for whom the role is to be checked
  +     * @param role Security role to be checked
  +     */
  +     public boolean hasRole(Principal principal, String role) {
  +         if (log.isDebugEnabled()) {
  +             log.debug(sm.getString("jaasRealm.isInRole.start", 
principal.getName(), role));
  +         }
  +
  +         if ((principal == null) || (role == null) ||
  +             (roleMap.get(principal) == null)) {
  +             if (log.isDebugEnabled()) {
  +                 log.debug(sm.getString("jaasRealm.isInRole.noPrincipalOrRole"));
  +             }
  +             return false;
  +         }
  +
  +        List roles = (List)roleMap.get(principal);
  +        if (log.isDebugEnabled()) {
  +            log.debug(sm.getString("jaasRealm.isInRole.principalCached", 
String.valueOf(roles.size())));
  +        }
  +
  +        for (Iterator it = roles.iterator(); it.hasNext();) {
  +            Principal possessedRole = (Principal)it.next();
  +            String possessedRoleName = possessedRole.getName();
  +            if (log.isDebugEnabled()) {
  +                log.debug(sm.getString("jaasRealm.isInRole.possessesRole", 
possessedRole.getName()));
  +            }
  +
  +            if (possessedRoleName.equals(role)) {
  +                if (log.isDebugEnabled()) {
  +                    log.debug(sm.getString("jaasRealm.isInRole.match"));
  +                }
  +                return true;
  +            }
  +        }
  +        if (log.isDebugEnabled()) {
  +            log.debug(sm.getString("jaasRealm.isInRole.noMatch"));
  +        }
  +        return false;
  +     }
  +
   
       // -------------------------------------------------------- Package Methods
   
  @@ -372,7 +475,7 @@
   
   
       /**
  -     * Return a short name for this Realm implementation.
  +     * Return a short name for this <code>Realm</code> implementation.
        */
       protected String getName() {
   
  @@ -392,7 +495,7 @@
   
   
       /**
  -     * Return the Principal associated with the given user name.
  +     * Return the <code>Principal</code> associated with the given user name.
        */
       protected Principal getPrincipal(String username) {
   
  @@ -402,59 +505,77 @@
   
   
       /**
  -     * Construct and return a <code>java.security.Principal</code> instance
  -     * representing the authenticated user for the specified Subject.  If no
  -     * such Principal can be constructed, return <code>null</code>.
  -     *
  -     * @param subject The Subject representing the logged in user
  +     * Identify and return a <code>java.security.Principal</code> instance
  +     * representing the authenticated user for the specified <code>Subject</code>.
  +     * The Principal is constructed by scanning the list of Principals returned
  +     * by the JAASLoginModule. The first <code>Principal</code> object that matches
  +     * one of the class names supplied as a "user class" is the user Principal.
  +     * This object is returned to tha caller.
  +     * Any remaining principal objects returned by the LoginModules are mapped to  
  +     * roles, but only if their respective classes match one of the "role class" 
classes. 
  +     * If a user Principal cannot be constructed, return <code>null</code>.
  +     * @param subject The <code>Subject</code> representing the logged-in user
        */
       protected Principal createPrincipal(String username, Subject subject) {
           // Prepare to scan the Principals for this Subject
           String password = null; // Will not be carried forward
  -        ArrayList roles = new ArrayList();
  +
  +        List roles = new ArrayList();
  +        Principal userPrincipal = null; 
   
           // Scan the Principals for this Subject
           Iterator principals = subject.getPrincipals().iterator();
           while (principals.hasNext()) {
               Principal principal = (Principal) principals.next();
  -            // No need to look further - that's our own stuff
  -            if( principal instanceof GenericPrincipal ) {
  -                if( log.isDebugEnabled() )
  -                    log.debug("Found old GenericPrincipal " + principal );
  -                return principal;
  -            }
               String principalClass = principal.getClass().getName();
  -            if( log.isDebugEnabled() )
  -                log.info("Principal: " + principalClass + " " + principal);
   
  -            if (userClasses.contains(principalClass)) {
  -                // Override the default - which is the original user, accepted by
  -                // the friendly LoginManager
  -                username = principal.getName();
  +            if( log.isDebugEnabled() ) {
  +                log.debug(sm.getString("jaasRealm.checkPrincipal", principal, 
principalClass));
               }
  +
  +            if (userPrincipal == null && userClasses.contains(principalClass)) {
  +                userPrincipal = principal;
  +                if( log.isDebugEnabled() ) {
  +                    log.debug(sm.getString("jaasRealm.userPrincipalSuccess", 
principal.getName()));
  +                }
  +            }
  +
               if (roleClasses.contains(principalClass)) {
                   roles.add(principal.getName());
               }
  -            // Same as Jboss - that's a pretty clean solution
  -            if( (principal instanceof Group) &&
  -                 "Roles".equals( principal.getName())) {
  -                Group grp=(Group)principal;
  -                Enumeration en=grp.members();
  -                while( en.hasMoreElements() ) {
  -                    Principal roleP=(Principal)en.nextElement();
  -                    roles.add( roleP.getName());
  -                }
   
  +            if (roleClasses.contains(principalClass)) {
  +                roles.add(principal);
  +                if( log.isDebugEnabled() ) {
  +                    log.debug(sm.getString("jaasRealm.rolePrincipalAdd", 
principal.getName()));
  +                }
               }
           }
   
  -        // Create the resulting Principal for our authenticated user
  -        if (username != null) {
  -            return (new GenericPrincipal(this, username, password, roles));
  +        // Print failure message if needed
  +        if (userPrincipal == null) {
  +            if (log.isDebugEnabled()) {
  +                log.debug(sm.getString("jaasRealm.userPrincipalFailure"));
  +                log.debug(sm.getString("jaasRealm.rolePrincipalFailure"));
  +            }
           } else {
  -            return (null);
  +            if (roles.size() == 0) {
  +                if (log.isDebugEnabled()) {
  +                    log.debug(sm.getString("jaasRealm.rolePrincipalFailure"));
  +                }
  +            } else {
  +                roleMap.put(userPrincipal, roles);
  +                if (log.isDebugEnabled()) {
  +                    log.debug(sm.getString("jaasRealm.rolePrincipalSuccess", 
String.valueOf(roles.size())));
  +                    log.debug(sm.getString("jaasRealm.cachePrincipal", 
userPrincipal.getName(), String.valueOf(roles.size())));
  +                }
  +            }
           }
  +
  +        // Return the resulting Principal for our authenticated user
  +        return userPrincipal;
       }
  +
        /**
         * Ensure the given name is legal for JAAS configuration.
         * Added for Bugzilla 30869, made protected for easy customization
  @@ -487,7 +608,7 @@
   
       /**
        *
  -     * Prepare for active use of the public methods of this Component.
  +     * Prepare for active use of the public methods of this <code>Component</code>.
        *
        * @exception LifecycleException if this component detects a fatal error
        *  that prevents it from being started
  @@ -501,7 +622,7 @@
   
   
       /**
  -     * Gracefully shut down active use of the public methods of this Component.
  +     * Gracefully shut down active use of the public methods of this 
<code>Component</code>.
        *
        * @exception LifecycleException if this component detects a fatal error
        *  that needs to be reported
  
  
  
  1.5.2.1   +27 -6     
jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/realm/LocalStrings.properties
  
  Index: LocalStrings.properties
  ===================================================================
  RCS file: 
/home/cvs/jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/realm/LocalStrings.properties,v
  retrieving revision 1.5
  retrieving revision 1.5.2.1
  diff -u -r1.5 -r1.5.2.1
  --- LocalStrings.properties   12 Dec 2003 22:37:58 -0000      1.5
  +++ LocalStrings.properties   21 Sep 2004 23:01:50 -0000      1.5.2.1
  @@ -4,12 +4,33 @@
   
   # package org.apache.catalina.realm
   
  -jaasRealm.accountExpired=Username {0} NOT authenticated due to expired account
  -jaasRealm.authenticateSuccess=Username {0} successfully authenticated
  -jaasRealm.credentialExpired=Username {0} NOT authenticated due to expired credential
  -jaasRealm.failedLogin=Username {0} NOT authenticated due to failed login
  -jaasRealm.loginException=Login exception authenticating username {0}
  +jaasRealm.beginLogin=JAASRealm login requested for username "{0}" using 
LoginContext for application "{1}"
  +jaasRealm.accountExpired=Username "{0}" NOT authenticated due to expired account
  +jaasRealm.authenticateFailure=Username "{0}" NOT successfully authenticated
  +jaasRealm.authenticateSuccess=Username "{0}" successfully authenticated as 
Principal "{1}" -- Subject was created too
  +jaasRealm.credentialExpired=Username "{0}" NOT authenticated due to expired 
credential
  +jaasRealm.failedLogin=Username "{0}" NOT authenticated due to failed login
  +jaasRealm.loginException=Login exception authenticating username "{0}"
   jaasRealm.unexpectedError=Unexpected error
  +jaasRealm.loginContextCreated=JAAS LoginContext created for username "{0}"
  +jaasRealm.userPrincipalSuccess=Subject for username "{0}" returned user Principal 
"{1}"
  +jaasRealm.userPrincipalFailure=Subject for username "{0}" did not return a valid 
user Principal
  +jaasRealm.cachePrincipal=User Principal "{0}" cached; has {1} role Principal(s)
  +jaasRealm.checkPrincipal=Checking Principal "{0}" [{1}]
  +jaasRealm.userPrincipalSuccess=Principal "{0}" is a valid user class. We will use 
this as the user Principal.
  +jaasRealm.userPrincipalFailure=No valid user Principal found
  +jaasRealm.rolePrincipalAdd=Adding role Principal "{0}" to this user Principal\'s 
roles
  +jaasRealm.rolePrincipalSuccess={0} role Principal(s) found
  +jaasRealm.rolePrincipalFailure=No valid role Principals found.
  +jaasRealm.isInRole.start=Checking if user Principal "{0}" possesses role "{1}"
  +jaasRealm.isInRole.noPrincipalOrRole=No roles Principals found. User Principal or 
Subject is null, or user Principal not in cache
  +jaasRealm.isInRole.principalCached=User Principal has {0} role Principal(s)
  +jaasRealm.isInRole.possessesRole=User Principal has a role Principal called "{0}"
  +jaasRealm.isInRole.match=Matching role Principal found.
  +jaasRealm.isInRole.noMatch=Matching role Principal NOT found.
  +jaasCallback.digestpassword=Digested password "{0}" as "{1}"
  +jaasCallback.username=Returned username "{0}"
  +jaasCallback.password=Returned password "{0}"
   jdbcRealm.authenticateFailure=Username {0} NOT successfully authenticated
   jdbcRealm.authenticateSuccess=Username {0} successfully authenticated
   jdbcRealm.close=Exception closing database connection
  
  
  
  No                   revision
  No                   revision
  1.70.2.40 +3 -0      jakarta-tomcat-catalina/webapps/docs/changelog.xml
  
  Index: changelog.xml
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat-catalina/webapps/docs/changelog.xml,v
  retrieving revision 1.70.2.39
  retrieving revision 1.70.2.40
  diff -u -r1.70.2.39 -r1.70.2.40
  --- changelog.xml     21 Sep 2004 19:45:08 -0000      1.70.2.39
  +++ changelog.xml     21 Sep 2004 23:01:50 -0000      1.70.2.40
  @@ -79,6 +79,9 @@
         <fix> 
           <bug>31277</bug>: Clarified automatic application deployment section of 
Host configuration page. (yoavs) 
         </fix> 
  +      <fix>
  +        <bug>28631</bug>: JAASRealm enhancements to support custom user, role class 
names, use Commons-Logging. (yoavs)
  +      </fix>
       </changelog>
     </subsection>
     <subsection name="Webapps">
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to