You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by "philippe.leothaud" <ph...@wanadoo.fr> on 2003/12/09 03:43:16 UTC

Tomcat authorization handling seems not to function according to Servlet 2.4 Spec

Hi all, 

I am new to Tomcat's mailing lists, and I don't really know if this list is the right place for such a post : excuse me if it is not the case.

I wonder if I didn't notice something which is not a real bug in Tomcat, as it seems to do exactly what developers want it to do,  
but more a difference between the implementation of authorization policy (the handling of a Web Application web.xml 
security-constraint elements) in Tomcat5 and what the Servlet 2.4 Spec says.

Example of the problem (from the Tomcat Jsp-examples WebApp) : 

<?xml version='1.0' encoding='utf-8'?>
<tomcat-users>
  <role rolename="tomcat"/>
  <role rolename="role1"/>
  <role rolename="manager"/>
  <role rolename="admin"/>
  <user username="tomcat" password="tomcat" roles="tomcat"/>
  <user username="both" password="tomcat" roles="tomcat,role1"/>
  <user username="role1" password="tomcat" roles="role1"/>
  <user username="admin" password="tomcat" roles="admin,manager,tomcat"/>
</tomcat-users>

tomcat-users.xml

<security-constraint>
 <display-name>Example Security Constraint</display-name>
 <web-resource-collection>
  <web-resource-name>Protected Area</web-resource-name>
  <!-- Define the context-relative URL(s) to be protected -->
  <url-pattern>/security/protected/*</url-pattern>
  <!-- If you list http methods, only those methods are protected -->
  <http-method>DELETE</http-method>
  <http-method>GET</http-method>
  <http-method>POST</http-method>
  <http-method>PUT</http-method>
 </web-resource-collection>
 <auth-constraint>
  <!-- Anyone with one of the listed roles may access this area -->
  <role-name>tomcat</role-name>
 </auth-constraint>
</security-constraint>

<security-constraint>
 <display-name>Example Security Constraint</display-name>
 <web-resource-collection>
  <web-resource-name>Protected Area</web-resource-name>
  <!-- Define the context-relative URL(s) to be protected -->
  <url-pattern>/security/protected/*</url-pattern>
  <!-- If you list http methods, only those methods are protected -->
  <http-method>DELETE</http-method>
  <http-method>GET</http-method>
  <http-method>POST</http-method>
  <http-method>PUT</http-method>
 </web-resource-collection>
 <auth-constraint>
  <!-- Anyone with one of the listed roles may access this area -->
  <role-name>role1</role-name>
 </auth-constraint>
</security-constraint>

<!-- Default login configuration uses form-based authentication -->
<login-config>
 <auth-method>FORM</auth-method>
 <realm-name>Example Form-Based Authentication Area</realm-name>
 <form-login-config>
  <form-login-page>/security/protected/login.jsp</form-login-page>
  <form-error-page>/security/protected/error.jsp</form-error-page>
 </form-login-config>
</login-config>
    
<!-- Security roles referenced by this web application -->
<security-role>
 <role-name>role1</role-name>
</security-role>
<security-role>
 <role-name>tomcat</role-name>
</security-role>    

webapps/jsp-examples/WEB-INF/web.xml (excerpt)

I've been adding  a new security-constraint element, separing the authorized roles each in its security-constraint

According to what the Servlet 2.4 says (see below for exact reference), two security constraints on the same 
(url-pattern, http-method) should result in the addition of the given authorizations and so in this case,
users "tomcat", "role1" and "both" should be authorized to access the protected resource.

But here, it is the contrary : you can't access http://10.160.4.205:8080/jsp-examples/security/protected/ under 
"tomcat" or "role1" identity any more, but you can still using the "both" identity : Tomcat has realized the intersection
of the authorizations instead of doing the union.


Analyze of the problem

After inverstigating a while in the code, here is what I noticed : 

First, 

In SecurityConstraint[] RealmBase.findSecurityConstraints(HttpRequest request, Context context) 
(the method begins at l. 445 of the org.apache.catalina.realm.RealmBase file),

each and every SecurityConstraint (<=> security-constraint in web.xml) containing a SecurityCollection
(<=> web-ressource-collection in web.xml) containing a url-pattern matching the User's request URI 
and defining a restriction on the http-method used by the user for his request is retrieved, using

boolean SecurityConstraint.included(String uri, String method) 
(method starts at line 343 of org.apache.catalina.deploy.SecurityConstraint)

While only SecurityConstraints containing SecurityCollections containing the url-pattern which is the
best-match to the User's request URI amongst all the url-patterns defined in web.xml should be retained first, and then amongst
these remaining constraints we shall keep only the ones defining a restriction on the same method (or no restriction
on the method, as stated in servlet-2_4-fr-spec.pdf, ch SRV 12-8-3, pp 100-101)


Second

in public boolean hasResourcePermission(HttpRequest request,
                                       HttpResponse response,
                                       SecurityConstraint constraint,
                                       Context context)
(the method begins at line 501 of the org.apache.catalina.realm.RealmBase file)

the restrictions on the authorized groups are analyzed, constraint after constraint, and as soon as one constraint is not verified, 

response.getResponse()).sendError(
    HttpServletResponse.SC_FORBIDDEN,
    sm.getString("realmBase.forbidden"));

is sent to the User : this means that at the contrary of what the spec says, for a same 
(http-method, url-pattern) couple, it's not the union of the authorizations but the intersection that is realized.

Spec : The rules to combine roles are given in servlet-2_4-fr-spec.pdf, ch SRV 12.8.1, pp97-98 : 

 "The combination of authorization constraints that name roles or that imply
 roles via the name shall yield the union of the role names in the individual
 constraints as permitted roles. A security constraint that does not contain an
 authorization constraint shall combine with authorization constraints that name or
 imply roles to allow unauthenticated access. The special case of an authorization
 constraint that names no roles shall combine with any other constraints to override
 their affects and cause access to be precluded."


Third

A similar problem as the second one accurs in the call to 

public boolean hasUserDataPermission(HttpRequest request,
                                     HttpResponse response,
                                     SecurityConstraint constraint)
(the method begins at line 627 of the org.apache.catalina.realm.RealmBase file)

As in the second point, constraints are examined one by one, instead of determining globally the policy for all
the constraints applying for the same (http-method, url-patern)

Spec :The rules to combine user-data-constraints are given in servlet-2_4-fr-spec.pdf, ch SRV 12.8.1, p98 : 

 "The combination of user-data-constraints that apply to a common urlpattern
 and http-method shall yield the union of connection types accepted by
 the individual constraints as acceptable connection types. A security constraint
 that does not contain a user-data-constraint shall combine with other userdata-
 constraints to cause the unprotected connection type to be an accepted
 connection type."


Possible workaround

I've coded a simple workaround (see below), consisting mainly of a class, MergedConstraintBuilder, 
whose job is to build a fake SecurityConstraint for each(requestURI, httpMethod) couple, 
implementing the selection algorithms described in the spec (as I understood it) :
 1 - select the best matching url-pattern and retain the SecurityCollections containing this pattern (if any)
 2 - retaining only the constraint of this first set defining a constraint for the http-method (if any)
 3 - determining the global user-data-contraint (ie transport protocol) for the resulting set of constraints
 4 - determining the authorized use groups for the set

When the

MergedConstraintBuilder.getMergedConstraintForRequest(SecurityConstraint[] allConstraints, 
                                                      String requestURI, 
                                                      String method)

method is called, I use successively these four algorithms to select the applying SecurityConstraints, and then 
I first build a fake org.apache.catalina.deploy.SecurityCollection, with the following properties : 
    - String name       :  
    - String[] patterns :  an array of Strings containing only one String, the request URI 
    - String[] methods  :  an array of Strings containing only one String, the request method 

Once the SecurityCollection built, I construct over it a fake SecurityConstraint with the folowing properties :
    - boolean allRoles                  :  appropriately set by the fourth algorithm
    - boolean authConstraint            :  appropriately set by the fourth algorithm
    - String[] roleNames                :  appropriately set by the fourth algorithm
    - String userConstraint             :  the the global user-data-contraint as returned by the third algorithm
    - SecurityCollection[] collections  :  an array containing a single element, the previously determined 
                                                                              fake SecurityCollection 

and I return it encapsulated in an array of SecurityConstraints, the goal of tis encapsulation being to avoid breaking 
org.apache.catalina.realm.RealmBase existing code.


In order to put this piece of code to work, we have to have org.apache.catalina.realm.RealmBase invoke it :
I have so added MergedConstraintBuilder in the org.apache.catalina.realm package, I've modified the 
public SecurityConstraint[] findSecurityConstraints(HttpRequest request, Context context) method of 
RealmBase (see just below), and I also added three short utilities methods in org.apache.catalina.deploy.SecurityCollection
(these utilities are in charge to retrieve the best matching url-pattern)

I didn't do extensive testing, but the spec examples work (and some more, too ;))

I don't know if the difference with the spec is a will or not, so I don't know if this will help

Anyway, it was fun

Philippe (philippe.leothaud@wanadoo.fr)

Note : the fake SecurityConstraints could actually be cached, so that the computation 
is done only once for a (URI, http-method) couple : I've got another version of the MergedConstraintBuilder providing
an implementation of this strategy.

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Changed SecurityConstraint[] findSecurityConstraints(HttpRequest request, Context context) 
method in org.apache.catalina.realm.RealmBase (starting at line 438)
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    /** l.438 org.apache.catalina.realm.RealmBase
     *
     * Return the SecurityConstraints configured to guard the request URI for
     * this request, or <code>null</code> if there is no such constraint.
     *
     * @param request Request we are processing
     * @param context Context the Request is mapped to
     */
    public SecurityConstraint[] findSecurityConstraints(HttpRequest request, Context context) {
     
        // Are there any defined security constraints?
        SecurityConstraint constraints[] = context.findConstraints();
        if ((constraints == null) || (constraints.length == 0)) {
            if (log.isDebugEnabled()) 
             log.debug("  No applicable constraints defined");
            return (null);
        }

        HttpServletRequest hreq = (HttpServletRequest) request.getRequest();
        String uri = request.getDecodedRequestURI();
        String contextPath = hreq.getContextPath();
        if (contextPath.length() > 0) uri = uri.substring(contextPath.length());
        String method = hreq.getMethod();

     MergedConstraintBuilder builder = new MergedConstraintBuilder ();
     return getMergedConstraintForRequest(allConstraints, uri, method);
        // Check each defined security constraint
    }
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


 ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 Methods to be added to the org.apache.catalina.deploy.SecurityCollection class
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  /** 
  * Builds and returns the <code>List</code> of all the <code>SecurityCollection</code>s 
  * part of this <code>SecurityConstraint</code> and containing amongst their 
  * <code>url-pattern</code>s at least one pattern matching exactly the given requestURI. 
  * 
  * @param requestURI : the URI to match exactly
  * @return           : the <code>List</code> of the <code>SecurityCollection</code>s 
  *                     containing a pattern matching this URI
  */
 public List getExactMatchingWebCollections(String requestURI) {
  List exactMatchingCollections = null;
  for (int i = 0; i < collections.length; i++) {
   String patterns[] = collections[i].getPatterns();
   for (int j = 0; j < patterns.length; j++) {
    if (requestURI.equals(patterns[j])) {
     if(exactMatchingCollections == null) {
      exactMatchingCollections = new ArrayList();
     }
     exactMatchingCollections.add(collections[i]);
     break;
    }
   }
  }
  return exactMatchingWebCollections;
 }

 /**
  * Builds and returns the <code>List</code> of all the <code>SecurityCollection</code>s 
  * part of this <code>SecurityConstraint</code> and containing amongst their 
  * <code>url-pattern</code>s at least one pattern matching exactly the given URI's bestMatch. 
  * 
  * @param bestMatch : the URI's bestMatch to match exactly
  * @return          : the <code>List</code> of the <code>SecurityCollection</code>s 
  *                    containing a pattern matching this URI's bestMatch
  */
 public List getMatchingWebCollections(String bestMatch) {
  List matchingCollections = null;
  for (int i = 0; i < collections.length; i++) {
   String patterns[] = collections[i].getPatterns();
   for (int j = 0; j < patterns.length; j++) {
    if (bestMatch.equals(patterns[j])) {
     if(matchingCollections == null) {
      matchingCollections = new ArrayList();
     }
     matchingCollections.add(collections[i]);
     break;
    }
   }
  }
  return matchingCollections;
 }

 /**
  * Gets <code>url-pattern</code> which is the best match to the given URI, amongst  
  * all the <code>SecurityCollection</code>s part of this <code>SecurityConstraint</code> 
  * 
  * @param requestURI : the URI's to match best
  * @return           : the <code>String</code> representation of the <code>url-pattern</code> 
  *                     which is best matching the given URI, amongst all the patterns of all the 
  *                     <code>SecurityCollection</code>s of this <code>SecurityConstraint</code>
  */
 public String getBestMatch(String requestURI) {
  String bestMatch = "";
  for (int i = 0; i < collections.length; i++) {
   String patterns[] = collections[i].getPatterns();
   for (int j = 0; j < patterns.length; j++) {
    if (matchPattern(requestURI, patterns[j])) {
     if(patterns[j].length() > bestMatch.length())
      bestMatch = patterns[j];
    }
   }
  }
  return bestMatch;  
 }
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------



----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Class MergedConstraintBuilder, to be added in the org.apache.catalina.realm package
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
package org.apache.catalina.realm;

import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;

import org.apache.log4j.Logger;

 /**
  * Builds a custom <code>SecurityConstraint</code> merging all valid <code>SecurityConstraint</code>s 
  * for the method and URI that will be extracted from the given <code>HttpServletRequest</code>, or
  * <code>null</code> if there is no such <code>SecurityConstraint</code>.
  * 
  * The process to determine which <code>SecurityConstraint</code>s are valid for a URI and a method
  * is defined in servlet-2_4-fr-spec.pdf, ch SRV 12-8-3, pp 100-101 : 
  *  1 - "Select the constraints (if any) defined on the url-pattern that is the
  *         best match to the request URI. If no constraints are selected, the container shall
  *         accept the request." (i.e. the custom Constraint is null)
  *  2 - "Determine if the HTTP method of the request is constrained at the selected pattern. 
  *         If it is not, the request shall be accepted." (i.e. the custom Constraint is null)
  *  3 -  Determine the user-data-constraint, given that : "The characteristics of the connection 
  *         on which the request was received must satisfy at least one of the supported 
  *         connection types defined by the constraints."
  *  4 -  Determine the array of authorized roles, given that : "The authentication characteristics 
  *         of the request must satisfy any authentication and role requirements 
  *         defined by the constraints."
  * 
  * After applying this process, the custom <code>SecurityConstraint</code> is created with the 
  * following properties : 
  *  - display-name = requestURI + "::" + requestMethod
  *  - SecurityCollection[] collection = 
  *          new SecurityCollection[] { 
  *              new SecurityCollection(requestURI + "::" + requestMethod, 
  *          new String[] { requestURI }, 
  *          new String[] { requestMethod } ) }
  *  - boolean authConstraint
  *  - boolean allRoles
  *  - String[] authorizedRoles
  */
public class MergedConstraintBuilder {

 static Logger logger = Logger.getLogger(MergedConstraintBuilder.class.getName());


 /**
  * Special method for integration with Catalina's RealmBase.
  * 
  * @param allConstraints : all the <code>SecurityConstraint</code>s defined in <code>web.xml</code> 
  * @param req            : the request of the User
  * 
  * @return               : the custom <code>SecurityConstraint</code>, wrapped in an array 
   *                                                   of <code>SecurityConstraint</code>s 
  */
 public SecurityConstraint[] getMergedConstraintForRequest(
     SecurityConstraint[] allConstraints, 
     String requestURI, 
     String method) {

  return new SecurityConstraint[] { mergeConstraintsForRequest(allConstraints, requestURI, method) };

 }


 /**
  * Builds and returns a custom <code>SecurityConstraint</code> merging all valid <code>SecurityConstraint</code>s 
  * for the method and URI that will be extracted from the given <code>HttpServletRequest</code>, or
  * <code>null</code> if there is no such <code>SecurityConstraint</code>.
  * 
  * @param allConstraints : all the <code>SecurityConstraint</code>s defined in <code>web.xml</code> 
  * @param req            : the request of the User
  * 
  * @return               : the custom <code>SecurityConstraint</code>
  */

 public SecurityConstraint mergeConstraintsForRequest(
     SecurityConstraint[] allConstraints, 
     HttpServletRequest req) {

  // On determine l'URI contextuelle et la methode de la requete
  String requestURI = req.getRequestURI();
  String contextPath = req.getContextPath();
  if (contextPath.length() > 0) {
   requestURI = requestURI.substring(contextPath.length());
  } 
  String method = req.getMethod();
  return mergeConstraintsForRequest(allConstraints, requestURI, method);

 }


 /**
  * Builds and returns a custom <code>SecurityConstraint</code> merging all valid 
  * <code>SecurityConstraint</code>s for the given method and URI, or <code>null</code> 
  * if there is no such <code>SecurityConstraint</code>
  *
  * @param allConstraints : all the <code>SecurityConstraint</code>s defined in <code>web.xml</code> 
  * @param requestURI     : URI of the User's request
  * @param method         : method of the User's request
  * 
  * @return               : the custom <code>SecurityConstraint</code>
  */
 public SecurityConstraint mergeConstraintsForRequest(
     SecurityConstraint[] allConstraints, 
     String requestURI, 
     String method) {

  Map matchingConstraintsAndWebResources = 
      filterConstraintsByURI(allConstraints, requestURI);
  if(matchingConstraintsAndWebResources == null) {
   return null;
  } 
  Collection matchingConstraints = 
      filterConstraintsByMethod(matchingConstraintsAndWebResources, method);
  if(matchingConstraints == null) {
   return null;
  } 
  // getAuthorizedRoles() takes care of setting the boolean allRoles 
  // and boolean authConstraint appropriately
  SecurityConstraint mergedSecurityConstraint = 
      getAuthorizedRoles(matchingConstraints);
  String userConstraint = getUserConstraint(matchingConstraints);
  SecurityCollection mergedCollection = new SecurityCollection( 
      requestURI + "::" + method, 
      new String[] { requestURI }, 
      new String[] { method } );
  mergedSecurityConstraint.setDisplayName(requestURI + "::" + method);
  mergedSecurityConstraint.setSecurityCollection(
      new SecurityCollection[] { mergedCollection });
  mergedSecurityConstraint.setUserConstraint(userConstraint);
  return mergedSecurityConstraint;
 }

 /**
  * Returns a <code>Map</code> containing the <code>SecurityConstraint</code>s 
  * and associated <code>SecurityCollection</code>s valid for the request URI (that is, 
  * all the <code>SecurityConstraint</code>s containing at least a <code>SecurityCollection</code>  
  * containing the <code>url-pattern</code> which is the best-matching pattern for the given URI, 
  * amongst all the <code>url-pattern</code>s defined in the webApp's <code>web.xml</code>), 
  * or null if there is no such <code>SecurityCollection</code> (and therfore, no such 
  * <code>SecurityConstraint</code>.
  * 
  * The rules to determine the best-matching pattern for the given URI are defined in 
  * servlet-2_4-fr-spec.pdf, ch SRV 11-1 pp 85-86 :
  *  1 - "The container will try to find an exact match of the path of the request to the
  *        path of the servlet."
  *  2 - "The container will recursively try to match the longest path-prefix."
  *  3 - "If the last segment in the URL path contains an extension (e.g. .jsp), the servlet
  *        container will try to match (...) the extension". 
  * 
  * 
  * @param allConstraints : all the <code>SecurityConstraint</code>s defined in web.xml 
  * @param requestURI     : URI of the User's request
  * 
  * @return               : a <code>Map</code> containing the <code>SecurityConstraint</code>s and 
  *                         associated <code>SecurityCollection</code>s valid for the request URI 
  */
 public Map filterConstraintsByURI(SecurityConstraint[] allConstraints, String requestURI) {
  if(logger.isDebugEnabled())
   logger.debug("MergedConstraintBuilder.filterConstraintsByURI() - "
    + "Checking SecurityConstraints " + allConstraints 
    + " against URI " + requestURI); 
  // Determining valid constraints, checking the constraints' url-patterns against the given requestURI 
  Map constraintsAndCollections = null;
  boolean exactMatch = false;
  for (int i = 0; i < allConstraints.length; i++) {
   // First choice : "case-exact-match" url-patterns
   List exactMatchingWebCollections = 
       allConstraints[i].getExactMatchingWebCollections(requestURI);
   if(logger.isDebugEnabled())
    logger.debug("MergedConstraintBuilder.filterConstraintsByURI() - " 
     + "List exactMatchingWebCollections obtained : " + exactMatchingWebCollections);
   if(exactMatchingWebCollections != null) {
    if(constraintsAndCollections == null) {
     constraintsAndCollections = new HashMap();
    }
    constraintsAndCollections.put(allConstraints[i], exactMatchingWebCollections);
   }
  }
  if(constraintsAndCollections == null) {
   // Second choice : "pattern-match" url-patterns 
   // Determining the best-matching pattern (=the longest amongst matching patterns)
   // We keep constraints containing at least one WebCollection containing the pattern 
   // and remove the others
   String bestMatch = "";
   for (int i = 0; i < allConstraints.length; i++) {
    String constraintBestMatch = allConstraints[i].getBestMatch(requestURI);
    if(constraintBestMatch.length() > bestMatch.length()) {
     bestMatch = constraintBestMatch;
    }
   }
   if(logger.isDebugEnabled())
    logger.debug("MergedConstraintBuilder.filterConstraintsByURI() - " 
     + "bestMatch obtained : " + bestMatch);
   for (int i = 0; i < allConstraints.length; i++) {
    List bestMatchingWebCollections = allConstraints[i].getMatchingWebCollections(bestMatch);
    if(logger.isDebugEnabled())
     logger.debug("MergedConstraintBuilder.filterConstraintsByURI() - " 
     + "partial bestMatchingWebCollections List obtained : " + bestMatchingWebCollections);
    if(bestMatchingWebCollections != null) {
     if(constraintsAndCollections == null) {
      constraintsAndCollections = new HashMap();
     }
     if(logger.isDebugEnabled())
      logger.debug("MergedConstraintBuilder.filterConstraintsByURI() - " 
      + "bestMatchingWebCollections List : " + bestMatchingWebCollections 
      + " stored for SecurityConstraint : " + allConstraints[i]);
     constraintsAndCollections.put(allConstraints[i], bestMatchingWebCollections);
    }
   }
  } else {
   exactMatch = true;
  }
  if(constraintsAndCollections == null) {
   // No matching constraint
   if(logger.isDebugEnabled())
    logger.debug("MergedConstraintBuilder.filterConstraintsByURI() - " 
    + "No SecurityConstraint restraining URI " + requestURI 
    + " amongst given constraints " + allConstraints);
   return null;
  } 
  return constraintsAndCollections;
 }

 /**
  * Returns a <code>Collection</code> containing the <code>SecurityConstraint</code>s restraining tne
  * use of the given HTTP method, or null if there is no such <code>SecurityConstraint</code>.
  * 
  * A <code>SecurityConstraint</code> is restraining the use of a given HTTP method if it contains 
  * at least a <code>SecurityColection</code> containing this HTTP method amongst the <code>String</code>s 
  * constituting its methods field (corresponding to the <code>http-method</code> element of the 
  * <code>web-resource-collection</code> element in the <code>web.xml</code>), or if its methods field is 
  * <code>null</code> or empty 
  * 
  * @param constraintsAndCollections : a <code>Map</code> containing the <code>SecurityConstraint</code>s
  *                                    to analyze, and their associated <code>SecurityCollection</code>s
  * @param method                    : the HTTP method against which the <code>SecurityConstraint</code>s
  *                                    must be checked
  * 
  * @return                          : a <code>Collection</code> containing the 
  *                                    <code>SecurityConstraint</code>s valid for the given HTTP method
  */
 public Collection filterConstraintsByMethod(Map constraintsAndCollections, String method) {
  if(logger.isDebugEnabled())
   logger.debug("MergedConstraintBuilder.filterConstraintsByMethod() - "
   + "Checking Constraints-WebCollection Map " + constraintsAndCollections 
   + " against method " + method); 
  Set matchingConstraints = constraintsAndCollections.keySet();
  Iterator matchingConstraintsIterator = matchingConstraints.iterator();
  while (matchingConstraintsIterator.hasNext()) {
   SecurityConstraint constraint = (SecurityConstraint) matchingConstraintsIterator.next();
   List matchingWebCollections = (List) constraintsAndCollections.get(constraint);
   Iterator matchingWebCollectionsIterator = matchingWebCollections.iterator();
   boolean methodIsProtected = false;
   // Pour chaque contrainte, il suffit de trouver une seule WebCollection
   while (matchingWebCollectionsIterator.hasNext()) {
    SecurityCollection collection = (SecurityCollection) matchingWebCollectionsIterator.next();
    String[] constrainedMethods = collection.getMethods();
    if(constrainedMethods == null || constrainedMethods.length == 0) {
     methodIsProtected = true;
     break;
    }
    for (int i = 0; i < constrainedMethods.length; i++) {
     if(method.equals(constrainedMethods[i])) {
      methodIsProtected = true;
      break;
     }
    }
    if(methodIsProtected) {
     break;
    }
   }
   if(!methodIsProtected) {
    matchingConstraintsIterator.remove();
   }   
  }
  if(matchingConstraints.size() == 0) { 
   System.out.println("MergedConstraintBuilder.filterConstraintsByMethod() - " 
    + "No SecurityConstraint restraining method " + method 
    + " in the Constraints-WebCollection Map");
   return null; 
  }
  return matchingConstraints;
 }


 /**
  * Combines all the given <code>SecurityConstraint</code>s to determine and return as a
  * <code>String</code> the applying user-data-constraint 
  * 
  * The rules to combine user-data-constraints are given in servlet-2_4-fr-spec.pdf, ch SRV 12.8.1, p98 : 
  * "The combination of user-data-constraints that apply to a common urlpattern
  * and http-method shall yield the union of connection types accepted by
  * the individual constraints as acceptable connection types. A security constraint
  * that does not contain a user-data-constraint shall combine with other userdata-
  * constraints to cause the unprotected connection type to be an accepted
  * connection type."
  * 
  * @param matchingConstraints : The <code>Collection</code> of <code>SecurityConstraint</code>s
  *                              to analyze and combine
  * 
  * @return                    : A <code>String</code> containing the applying user-data-constraint
  */
 public String getUserConstraint(Collection matchingConstraints) {
  SecurityConstraint mergedSecurityConstraint = new SecurityConstraint();
  mergedSecurityConstraint.setUserConstraint("INTEGRAL");
  Iterator matchingConstraintsIterator = matchingConstraints.iterator(); 
  while (matchingConstraintsIterator.hasNext()) {
   SecurityConstraint constraint = (SecurityConstraint) matchingConstraintsIterator.next();
   String userConstraint = constraint.getUserConstraint();
   if (userConstraint == null || userConstraint.equals("NONE")) {
    mergedSecurityConstraint.setUserConstraint("NONE");
    break;
   } else if (userConstraint.equals("CONFIDENTIAL")) {
    mergedSecurityConstraint.setUserConstraint("CONFIDENTIAL");
   }
  }  
  if (logger.isDebugEnabled())
   logger.debug(
       "MergedConstraintBuilder.getUserConstraint() - "
           + "String userConstraint obtained : "
           + mergedSecurityConstraint.getUserConstraint());
  return mergedSecurityConstraint.getUserConstraint();
 }


 /**
  * Combines all the given <code>SecurityConstraint</code>s to determine and return as an
  * array of <code>String</code>s the set of authorized roles 
  * 
  * The rules to combine roles are given in servlet-2_4-fr-spec.pdf, ch SRV 12.8.1, pp97-98 : 
  * "The combination of authorization constraints that name roles or that imply
  * roles via the name "*" shall yield the union of the role names in the individual
  * constraints as permitted roles. A security constraint that does not contain an
  * authorization constraint shall combine with authorization constraints that name or
  * imply roles to allow unauthenticated access. The special case of an authorization
  * constraint that names no roles shall combine with any other constraints to override
  * their affects and cause access to be precluded."
  * 
  * @param matchingConstraints : The <code>Collection</code> of <code>SecurityConstraint</code>s
  *                              to analyze and combine
  * 
  * @return                    : A <code>SecurityConstraint</code> containing the authorized roles.
  */
 public SecurityConstraint getAuthorizedRoles(Collection matchingConstraints) {
  SecurityConstraint mergedSecurityConstraint = new SecurityConstraint();
  mergedSecurityConstraint.setAuthConstraint(true);
  String[] groups = new String[0];
  Iterator matchingConstraintsIterator = matchingConstraints.iterator(); 
  while (matchingConstraintsIterator.hasNext()) {
   SecurityConstraint constraint = (SecurityConstraint) matchingConstraintsIterator.next();
   if(constraint.getAuthConstraint()) {
    String[] roleNames = constraint.getRoleNames();
    if(roleNames == null || roleNames.length == 0) {
     mergedSecurityConstraint.setAuthConstraint(true);
     mergedSecurityConstraint.setRoleNames(new String[0]);
     break;
    } else {
     if (!mergedSecurityConstraint.getAuthConstraint() || mergedSecurityConstraint.getAllRoles()) {
      continue;
     }
     mergedSecurityConstraint.setAuthConstraint(true);
     if(constraint.getAllRoles()) {
      mergedSecurityConstraint.setRoleNames(new String[] { "*" } );
      continue;
     }
     for(int j=0; j<roleNames.length; j++) {
      String roleName = roleNames[j];
      // Check if this role is already registered
      for(int k=0; k<groups.length; k++) {
       if(roleName.equals(groups[k]))
        break;
      }
      // If not, add it to the role array
      String[] newGroups = new String[groups.length + 1];
      System.arraycopy(groups, 0, newGroups, 0, groups.length);
      newGroups[groups.length] = roleName;
      groups = newGroups;
     }
    }
    mergedSecurityConstraint.setRoleNames(groups);
   } else {
    mergedSecurityConstraint.setAuthConstraint(false);
   }
  }
  if (logger.isDebugEnabled())
   logger.debug(
       "MergedConstraintBuilder.getUserConstraint() - "
           + "String[] groups obtained and added to the returned SecurityConstraint : "
           + mergedSecurityConstraint);
  return mergedSecurityConstraint;

 }

}
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


Re: Tomcat authorization handling seems not to function according to Servlet 2.4 Spec

Posted by Remy Maucherat <re...@apache.org>.
Brian Stansberry wrote:
> At 10:03 PM 12/8/2003 -0800, you wrote:
> 
>> The decision on whether to change the Realm interface, or move the
>> header processing to AuthenticatorBase is still open.
> 
> So soon after such a major release it seems foolhardy to bring this
> up, but Phillipe's post seems to have opened a can of worms....
> 
> Are there any plans to do anything about JSR-115?  As it's part of
> the J2EE 1.4 spec, I would think that for a compliant appserver to
> embed Tomcat (any others besides JBoss??), Tomcat would need to
> comply.  I bring this up because if there is consideration of API
> changes to deal w/ the Servlet 2.4 authorization handling, it might
> be a good time to look into it.  I'd be happy to help in such an
> effort if there is any interest.

I'll be looking into this. I don't know yet if the web related parts 
belong more to the appserver (JBoss), or more to the servlet container.

Rémy



---------------------------------------------------------------------
To unsubscribe, e-mail: tomcat-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: tomcat-dev-help@jakarta.apache.org


Re: Tomcat authorization handling seems not to function according to Servlet 2.4 Spec

Posted by Remy Maucherat <re...@apache.org>.
Jeanfrancois Arcand wrote:
> Remy Maucherat wrote:
> 
>> Jeanfrancois Arcand wrote:
>>>
>>> Brian Stansberry wrote:
>>>
>>>> At 10:03 PM 12/8/2003 -0800, you wrote:
>>>>
>>>>> The decision on whether to change the Realm interface, or
>>>>> move the header processing to AuthenticatorBase is still open.
>>>>
>>>> So soon after such a major release it seems foolhardy to bring this 
>>>> up, but Phillipe's post seems to have opened a can of worms....
>>>>
>>>> Are there any plans to do anything about JSR-115?  As it's part of 
>>>> the J2EE 1.4 spec, I would think that for a compliant appserver to 
>>>> embed Tomcat (any others besides JBoss??), Tomcat would need to 
>>>> comply.  I bring this up because if there is consideration of API 
>>>> changes to deal w/ the Servlet 2.4 authorization handling, it might 
>>>> be a good time to look into it.  I'd be happy to help in such an 
>>>> effort if there is any interest.
>>>
>>> All you have to do to comply with jsr115 is to extends RealmBase and 
>>> override:
>>>
>>> - hasUserDataPermission
>>> - hasRole
>>> - hasResourcePermission
>>> - findSecurityConstraint
>>>
>>> A couple of months ago (search the tomcat-dev list) we have discussed 
>>> the possibility of implementing jsr115 directly into Tomcat. Still on 
>>> my plate (don't know when)....
>>>
>>> J2EE 1.4 RI contains Tomcat 5 "powered by" jsr 115. The problem with 
>>> jsr115 is you have to run under a Security Manager, and this is for 
>>> sure slower than the current "native" implementation.
>>
>> I think you'll have an opportunity to make your changes if you want 
>> to, since we'll have some refactoring to do on the realm before the 
>> next stable 5.0.x release occurs.
> 
> That's a pretty good opportunity....For reference, here is the link to 
> my original proposal
> 
> http://www.mail-archive.com/tomcat-dev@jakarta.apache.org/msg39894.html
> 
> My original idea was to remove the current Authenticator <-> Realm 
> dependency. Currently, the Realm is doing Authorization and 
> Authentication, which I think should be splitted.
> 
>> Did you do benches ?
>> Comparing (4.1.x) vs (5.0.x) vs (5.0.x + sec manager), for example.
>>
>> I wouldn't be surprised if the last one benches as well as the first 
>> one (which would be really cool).
> 
> Not yet, but it is one of the thing I want to do when I've found spare 
> time. For sure (5.0.x + sec manager) is faster than (5.0.x + sec manager 
> + jsr115) since with 115, the policy provider is called everytime 
> hasUser/ResourcePermission are called, and this approach cannot beat the 
> current way of doing.
> 
> On the original topic, I think I've missed the spec change from pfd3 and 
> fcs. I was under the wrong impression that Bill's last changes on the 
> RealmAdapter was related to that changes (I was completely wrong). I 
> will try to find the reason whythe change was make...
> 
> Now the only problem I'm seeing with Phillipe's work is Strings are used 
> everywhere and that may have a performance impact. It may be time to 
> start using MessageByte.....

That's the idea. If you're inside the container and you consider it a 
critical section for whatever reason, then strings are really evil, and 
you should use MessageBytes instead (or even ByteChunks if manipulating 
header values) ;-)

I see supporing JSR 115 would be good, but making it disabled by default 
when there's a security manager would be the most reasonable.

Rémy



---------------------------------------------------------------------
To unsubscribe, e-mail: tomcat-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: tomcat-dev-help@jakarta.apache.org


Re: Tomcat authorization handling seems not to function according to Servlet 2.4 Spec

Posted by Remy Maucherat <re...@apache.org>.
Jeanfrancois Arcand wrote:
>> Now the big question: do we plan to have that JSR 115 support for the 
>> next stable release, or is it too early to have it done ?
> 
> Since there is still some gray area I need to think of, I would say no. 
> But let me think of it :-)

Well, since we don't have any really urgent issues to fix in 5.0.16 
(ahhhh, a quality initial release at last), we don't have to release 
5.0.17 within two weeks ;-)

Rémy


---------------------------------------------------------------------
To unsubscribe, e-mail: tomcat-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: tomcat-dev-help@jakarta.apache.org


Re: Tomcat authorization handling seems not to function according to Servlet 2.4 Spec

Posted by Jeanfrancois Arcand <jf...@apache.org>.

Remy Maucherat wrote:

> Jeanfrancois Arcand wrote:
>
>>> Mmm, ok. I'm not quite following very well, since I didn't read the 
>>> spec yet ;-) If I understand, you would implement something which 
>>> would work as the JSR 115 security policy provider, but (likely :) ) 
>>> simpler ?
>>
>>
>> Yes, as a first steps. It will also works without a security manager 
>> (Costin was asking for that if I remember correctly). It is a 
>> required steps to supports jsr115 anyway.
>
>
> Good :)
>
>>> Would it be equivalent performance wise to the current solution if 
>>> JSR 115 support is disabled ?
>>
>>
>> Yes, it could be if ByteChunk/MessageByte etc. are used ( can be 
>> slower also, probably the first implementation will be slower ). One 
>> way to do that will be to create our own Permission collections that 
>> re-use what we already have. I still have to think of it...I would 
>> like to do something similar to what you have done with the Mapper ( 
>> avoiding String etc.)
>
>
> Depending on what you're trying to match and if you can have multiple 
> matches, String.regionMatcher (currently used) works good, since it 
> uses the internal char array of the two String objects. It's the same 
> as using a CharChunk, obviously.
>
> Very good plan overall.
>
> Now the big question: do we plan to have that JSR 115 support for the 
> next stable release, or is it too early to have it done ?

Since there is still some gray area I need to think of, I would say no. 
But let me think of it :-)

-- Jeanfrancois



>
> Rémy
>
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: tomcat-dev-unsubscribe@jakarta.apache.org
> For additional commands, e-mail: tomcat-dev-help@jakarta.apache.org
>
>


---------------------------------------------------------------------
To unsubscribe, e-mail: tomcat-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: tomcat-dev-help@jakarta.apache.org


Re: Tomcat authorization handling seems not to function according to Servlet 2.4 Spec

Posted by Remy Maucherat <re...@apache.org>.
Jeanfrancois Arcand wrote:
>> Mmm, ok. I'm not quite following very well, since I didn't read the 
>> spec yet ;-) If I understand, you would implement something which 
>> would work as the JSR 115 security policy provider, but (likely :) ) 
>> simpler ?
> 
> Yes, as a first steps. It will also works without a security manager 
> (Costin was asking for that if I remember correctly). It is a required 
> steps to supports jsr115 anyway.

Good :)

>> Would it be equivalent performance wise to the current solution if JSR 
>> 115 support is disabled ?
> 
> Yes, it could be if ByteChunk/MessageByte etc. are used ( can be slower 
> also, probably the first implementation will be slower ). One way to do 
> that will be to create our own Permission collections that re-use what 
> we already have. I still have to think of it...I would like to do 
> something similar to what you have done with the Mapper ( avoiding 
> String etc.)

Depending on what you're trying to match and if you can have multiple 
matches, String.regionMatcher (currently used) works good, since it uses 
the internal char array of the two String objects. It's the same as 
using a CharChunk, obviously.

Very good plan overall.

Now the big question: do we plan to have that JSR 115 support for the 
next stable release, or is it too early to have it done ?

Rémy



---------------------------------------------------------------------
To unsubscribe, e-mail: tomcat-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: tomcat-dev-help@jakarta.apache.org


Re: Tomcat authorization handling seems not to function according to Servlet 2.4 Spec

Posted by Jeanfrancois Arcand <jf...@apache.org>.

Remy Maucherat wrote:

> Jeanfrancois Arcand wrote:
>
>> Zzzzoui :-)
>>
>> I will do that (on my own time unfortunalty, so may take a couple of 
>> weeks....)
>>
>> First, it would be easier just to convert the web.xml into security 
>> permissions, and to keep internal collections, for unchecked, 
>> excluded, and role permissions than to do all this constraint 
>> processing work. A simple call to collection.implies, could then be 
>> used for the decision logic (which would be just one step short of 
>> integrating with an external jsr 115 policy provider).
>>
>> Then a full jsr115 implementation can happen once I'm sure step one 
>> works :-)
>
>
> Mmm, ok. I'm not quite following very well, since I didn't read the 
> spec yet ;-) If I understand, you would implement something which 
> would work as the JSR 115 security policy provider, but (likely :) ) 
> simpler ?

Yes, as a first steps. It will also works without a security manager 
(Costin was asking for that if I remember correctly). It is a required 
steps to supports jsr115 anyway.

> Would it be equivalent performance wise to the current solution if JSR 
> 115 support is disabled ?

Yes, it could be if ByteChunk/MessageByte etc. are used ( can be slower 
also, probably the first implementation will be slower ). One way to do 
that will be to create our own Permission collections that re-use what 
we already have. I still have to think of it...I would like to do 
something similar to what you have done with the Mapper ( avoiding 
String etc.)

-- Jeanfrancois


>
> Rémy
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: tomcat-dev-unsubscribe@jakarta.apache.org
> For additional commands, e-mail: tomcat-dev-help@jakarta.apache.org
>
>


---------------------------------------------------------------------
To unsubscribe, e-mail: tomcat-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: tomcat-dev-help@jakarta.apache.org


Re: Tomcat authorization handling seems not to function according to Servlet 2.4 Spec

Posted by Remy Maucherat <re...@apache.org>.
Jeanfrancois Arcand wrote:
> Zzzzoui :-)
> 
> I will do that (on my own time unfortunalty, so may take a couple of 
> weeks....)
> 
> First, it would be easier just to convert the web.xml into security 
> permissions, and to keep internal collections, for unchecked, excluded, 
> and role permissions than to do all this constraint processing work. A 
> simple call to collection.implies, could then be used for the decision 
> logic (which would be just one step short of integrating with an 
> external jsr 115 policy provider).
> 
> Then a full jsr115 implementation can happen once I'm sure step one 
> works :-)

Mmm, ok. I'm not quite following very well, since I didn't read the spec 
yet ;-) If I understand, you would implement something which would work 
as the JSR 115 security policy provider, but (likely :) ) simpler ?
Would it be equivalent performance wise to the current solution if JSR 
115 support is disabled ?

Rémy


---------------------------------------------------------------------
To unsubscribe, e-mail: tomcat-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: tomcat-dev-help@jakarta.apache.org


Re: Tomcat authorization handling seems not to function according to Servlet 2.4 Spec

Posted by Jeanfrancois Arcand <jf...@apache.org>.

Remy Maucherat wrote:

> Jeanfrancois Arcand wrote:
>
>> Not yet, but it is one of the thing I want to do when I've found 
>> spare time. For sure (5.0.x + sec manager) is faster than (5.0.x + 
>> sec manager + jsr115) since with 115, the policy provider is called 
>> everytime hasUser/ResourcePermission are called, and this approach 
>> cannot beat the current way of doing.
>>
>> On the original topic, I think I've missed the spec change from pfd3 
>> and fcs. I was under the wrong impression that Bill's last changes on 
>> the RealmAdapter was related to that changes (I was completely 
>> wrong). I will try to find the reason whythe change was make...
>>
>> Now the only problem I'm seeing with Phillipe's work is Strings are 
>> used everywhere and that may have a performance impact. It may be 
>> time to start using MessageByte.....
>
>
> So, now that the problems have been resolved (even the performance 
> ones :) ), can you plan to add your JSR 115 (with a flag somewhere to 
> enable it, likely on the realm :) ) stuff on top of Bill's stuff ?

Zzzzoui :-)

I will do that (on my own time unfortunalty, so may take a couple of 
weeks....)

First, it would be easier just to convert the web.xml into security 
permissions, and to keep internal collections, for unchecked, excluded, 
and role permissions than to do all this constraint processing work. A 
simple call to collection.implies, could then be used for the decision 
logic (which would be just one step short of integrating with an 
external jsr 115 policy provider).

Then a full jsr115 implementation can happen once I'm sure step one 
works :-)

-- Jeanfrancois


>
> Rémy
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: tomcat-dev-unsubscribe@jakarta.apache.org
> For additional commands, e-mail: tomcat-dev-help@jakarta.apache.org
>
>


---------------------------------------------------------------------
To unsubscribe, e-mail: tomcat-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: tomcat-dev-help@jakarta.apache.org


Re: Tomcat authorization handling seems not to function according to Servlet 2.4 Spec

Posted by Remy Maucherat <re...@apache.org>.
Jeanfrancois Arcand wrote:
> Not yet, but it is one of the thing I want to do when I've found spare 
> time. For sure (5.0.x + sec manager) is faster than (5.0.x + sec manager 
> + jsr115) since with 115, the policy provider is called everytime 
> hasUser/ResourcePermission are called, and this approach cannot beat the 
> current way of doing.
> 
> On the original topic, I think I've missed the spec change from pfd3 and 
> fcs. I was under the wrong impression that Bill's last changes on the 
> RealmAdapter was related to that changes (I was completely wrong). I 
> will try to find the reason whythe change was make...
> 
> Now the only problem I'm seeing with Phillipe's work is Strings are used 
> everywhere and that may have a performance impact. It may be time to 
> start using MessageByte.....

So, now that the problems have been resolved (even the performance ones 
:) ), can you plan to add your JSR 115 (with a flag somewhere to enable 
it, likely on the realm :) ) stuff on top of Bill's stuff ?

Rémy


---------------------------------------------------------------------
To unsubscribe, e-mail: tomcat-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: tomcat-dev-help@jakarta.apache.org


Re: Tomcat authorization handling seems not to function according to Servlet 2.4 Spec

Posted by Jeanfrancois Arcand <jf...@apache.org>.

Remy Maucherat wrote:

> Jeanfrancois Arcand wrote:
>
>>
>>
>> Brian Stansberry wrote:
>>
>>> At 10:03 PM 12/8/2003 -0800, you wrote:
>>>  
>>>
>>>> The decision on whether to change the Realm interface, or
>>>> move the header processing to AuthenticatorBase is still open.
>>>>   
>>>
>>>
>>>
>>> So soon after such a major release it seems foolhardy to bring this 
>>> up, but Phillipe's post seems to have opened a can of worms....
>>>
>>> Are there any plans to do anything about JSR-115?  As it's part of 
>>> the J2EE 1.4 spec, I would think that for a compliant appserver to 
>>> embed Tomcat (any others besides JBoss??), Tomcat would need to 
>>> comply.  I bring this up because if there is consideration of API 
>>> changes to deal w/ the Servlet 2.4 authorization handling, it might 
>>> be a good time to look into it.  I'd be happy to help in such an 
>>> effort if there is any interest.
>>>  
>>
>> All you have to do to comply with jsr115 is to extends RealmBase and 
>> override:
>>
>> - hasUserDataPermission
>> - hasRole
>> - hasResourcePermission
>> - findSecurityConstraint
>>
>> A couple of months ago (search the tomcat-dev list) we have discussed 
>> the possibility of implementing jsr115 directly into Tomcat. Still on 
>> my plate (don't know when)....
>>
>> J2EE 1.4 RI contains Tomcat 5 "powered by" jsr 115. The problem with 
>> jsr115 is you have to run under a Security Manager, and this is for 
>> sure slower than the current "native" implementation.
>
>
> I think you'll have an opportunity to make your changes if you want 
> to, since we'll have some refactoring to do on the realm before the 
> next stable 5.0.x release occurs.

That's a pretty good opportunity....For reference, here is the link to 
my original proposal

http://www.mail-archive.com/tomcat-dev@jakarta.apache.org/msg39894.html

My original idea was to remove the current Authenticator <-> Realm 
dependency. Currently, the Realm is doing Authorization and 
Authentication, which I think should be splitted.

>
> Did you do benches ?
> Comparing (4.1.x) vs (5.0.x) vs (5.0.x + sec manager), for example.
>
> I wouldn't be surprised if the last one benches as well as the first 
> one (which would be really cool).

Not yet, but it is one of the thing I want to do when I've found spare 
time. For sure (5.0.x + sec manager) is faster than (5.0.x + sec manager 
+ jsr115) since with 115, the policy provider is called everytime 
hasUser/ResourcePermission are called, and this approach cannot beat the 
current way of doing.

On the original topic, I think I've missed the spec change from pfd3 and 
fcs. I was under the wrong impression that Bill's last changes on the 
RealmAdapter was related to that changes (I was completely wrong). I 
will try to find the reason whythe change was make...

Now the only problem I'm seeing with Phillipe's work is Strings are used 
everywhere and that may have a performance impact. It may be time to 
start using MessageByte.....


-- Jeanfrancois



>
> Rémy
>
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: tomcat-dev-unsubscribe@jakarta.apache.org
> For additional commands, e-mail: tomcat-dev-help@jakarta.apache.org
>
>


---------------------------------------------------------------------
To unsubscribe, e-mail: tomcat-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: tomcat-dev-help@jakarta.apache.org


Re: Tomcat authorization handling seems not to function according to Servlet 2.4 Spec

Posted by Remy Maucherat <re...@apache.org>.
Jeanfrancois Arcand wrote:

> 
> 
> Brian Stansberry wrote:
> 
>> At 10:03 PM 12/8/2003 -0800, you wrote:
>>  
>>
>>> The decision on whether to change the Realm interface, or
>>> move the header processing to AuthenticatorBase is still open.
>>>   
>>
>>
>> So soon after such a major release it seems foolhardy to bring this 
>> up, but Phillipe's post seems to have opened a can of worms....
>>
>> Are there any plans to do anything about JSR-115?  As it's part of the 
>> J2EE 1.4 spec, I would think that for a compliant appserver to embed 
>> Tomcat (any others besides JBoss??), Tomcat would need to comply.  I 
>> bring this up because if there is consideration of API changes to deal 
>> w/ the Servlet 2.4 authorization handling, it might be a good time to 
>> look into it.  I'd be happy to help in such an effort if there is any 
>> interest.
>>  
> All you have to do to comply with jsr115 is to extends RealmBase and 
> override:
> 
> - hasUserDataPermission
> - hasRole
> - hasResourcePermission
> - findSecurityConstraint
> 
> A couple of months ago (search the tomcat-dev list) we have discussed 
> the possibility of implementing jsr115 directly into Tomcat. Still on my 
> plate (don't know when)....
> 
> J2EE 1.4 RI contains Tomcat 5 "powered by" jsr 115. The problem with 
> jsr115 is you have to run under a Security Manager, and this is for sure 
> slower than the current "native" implementation.

I think you'll have an opportunity to make your changes if you want to, 
since we'll have some refactoring to do on the realm before the next 
stable 5.0.x release occurs.

Did you do benches ?
Comparing (4.1.x) vs (5.0.x) vs (5.0.x + sec manager), for example.

I wouldn't be surprised if the last one benches as well as the first one 
(which would be really cool).

Rémy



---------------------------------------------------------------------
To unsubscribe, e-mail: tomcat-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: tomcat-dev-help@jakarta.apache.org


Re: Tomcat authorization handling seems not to function according to Servlet 2.4 Spec

Posted by Jeanfrancois Arcand <jf...@apache.org>.

Brian Stansberry wrote:

>At 10:03 PM 12/8/2003 -0800, you wrote:
>  
>
>>The decision on whether to change the Realm interface, or
>>move the header processing to AuthenticatorBase is still open.
>>    
>>
>
>So soon after such a major release it seems foolhardy to bring this up, but Phillipe's post seems to have opened a can of worms....
>
>Are there any plans to do anything about JSR-115?  As it's part of the J2EE 1.4 spec, I would think that for a compliant appserver to embed Tomcat (any others besides JBoss??), Tomcat would need to comply.  I bring this up because if there is consideration of API changes to deal w/ the Servlet 2.4 authorization handling, it might be a good time to look into it.  I'd be happy to help in such an effort if there is any interest.
>  
>
All you have to do to comply with jsr115 is to extends RealmBase and 
override:

- hasUserDataPermission
- hasRole
- hasResourcePermission
- findSecurityConstraint

A couple of months ago (search the tomcat-dev list) we have discussed 
the possibility of implementing jsr115 directly into Tomcat. Still on my 
plate (don't know when)....

J2EE 1.4 RI contains Tomcat 5 "powered by" jsr 115. The problem with 
jsr115 is you have to run under a Security Manager, and this is for sure 
slower than the current "native" implementation.

-- Jeanfrancois



>
>Brian Stansberry
>WAN Concepts, Inc.
>www.wanconcepts.com
>Tel:    (510) 894-0114 x 116
>Fax:    (510) 797-3005 
>
>
>---------------------------------------------------------------------
>To unsubscribe, e-mail: tomcat-dev-unsubscribe@jakarta.apache.org
>For additional commands, e-mail: tomcat-dev-help@jakarta.apache.org
>
>
>  
>


---------------------------------------------------------------------
To unsubscribe, e-mail: tomcat-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: tomcat-dev-help@jakarta.apache.org


Re: Tomcat authorization handling seems not to function according to Servlet 2.4 Spec

Posted by Brian Stansberry <br...@wanconcepts.com>.
At 10:03 PM 12/8/2003 -0800, you wrote:
>The decision on whether to change the Realm interface, or
>move the header processing to AuthenticatorBase is still open.

So soon after such a major release it seems foolhardy to bring this up, but Phillipe's post seems to have opened a can of worms....

Are there any plans to do anything about JSR-115?  As it's part of the J2EE 1.4 spec, I would think that for a compliant appserver to embed Tomcat (any others besides JBoss??), Tomcat would need to comply.  I bring this up because if there is consideration of API changes to deal w/ the Servlet 2.4 authorization handling, it might be a good time to look into it.  I'd be happy to help in such an effort if there is any interest.


Brian Stansberry
WAN Concepts, Inc.
www.wanconcepts.com
Tel:    (510) 894-0114 x 116
Fax:    (510) 797-3005 


---------------------------------------------------------------------
To unsubscribe, e-mail: tomcat-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: tomcat-dev-help@jakarta.apache.org


Re: Tomcat authorization handling seems not to function according to Servlet 2.4 Spec

Posted by Philippe Leothaud <ph...@wanadoo.fr>.
Hi Bill,

I totally agree with you, this security model is going to be a pain
somewhere to correctly configure access rights...

As of my "solution", the real interest is that (as far as I can see) it
doesn't change at all any API : the only thing that changes is the body of
the method
SecurityConstraint[] findSecurityConstraints(HttpRequest request, Context
context) in org.apache.catalina.realm.RealmBase (starting at line 438), I
only changed the body so that it calls the new MergedConstraintBuilder
class.

In any case, the signature remains the same.

Today :
1-  AuthenticatorBase gets all possible Constraints mathing the request, by
calling
SecurityConstraint [] constraints = realm.findSecurityConstraints(hrequest,
this.context); (AuthenticatorBase , l. 502)
2- RealmBase handles that request, returning an array of all the Constraints
matching the URI and the HTTP method of the request
3- AuthenticatorBase retrieves all these Constraints, cast the Request and
Response into HttpServletRequest and  HttpServletResponse (l 515-525)
4- For each Constraint of the array, AuthenticatorBase asks RealmBase if the
UserDataPermission is OK
5- For each Constraint of the array, AuthenticatorBase asks RealmBase if the
AuthConstraint is OK

All that I change is what is happening in step 2 :
    - RealmBase asks the new Class to retrieve an Array of matching
Constraints
    - The new Class builds a fake Constraint representing all possible
AuthConstraints and UserDataConstraints for this (URI, HTTPMethod)
    - It returns an array of SecurityConstraints, just like before.

The only difference is that now this array has only one (or zero) element,
but neither RealmBase neither AuthenticatorBase are aware of that.

All the remaining methods calls are just the same, they will just be faster
because there is only one Constraint in the array (and I think that if the
10 lines of code needed to cache the fake Constraints are added in this new
Class, it may really be faster and compute-time saving) .

I used that for a custom authorization framework I'm working on and it's OK
for me.


Thanks for having replied quickly (It's difficult to work seriously while
you're looking at your mail-box every minute to see if a response wasn't
posted ;-)


----- Original Message -----
From: "Bill Barker" <wb...@wilshire.com>
To: "Tomcat Developers List" <to...@jakarta.apache.org>
Sent: Tuesday, December 09, 2003 6:00 AM
Subject: Re: Tomcat authorization handling seems not to function according
to Servlet 2.4 Spec


> Yes, this looks like it changed between pfd3 to fr :(.
Security-constraints
> now work like 'grants' instead of 'constraints'.  IMHO, this make the 2.4
> security model all but useless.
> <whine>
> It would be natural to configure something like:
>   <security-constraint>
>      <web-resource-collection>
>         <web-resource-name>Client Area</web-resource-name>
>         <url-pattern>/clients/*</url-pattern>
>       </web-resource-collection>
>       <auth-constraint>
>          <!-- Any authenticated user -->
>          <role-name>*</role-name>
>       </auth-constraint>
>   </security-constraint>
>   <security-constraint>
>      <web-resource-collection>
>         <web-resource-name>Product1 Client Area</web-resource-name>
>         <url-pattern>/clients/product1/*</url-pattern>
>       </web-resource-collection>
>       <auth-constraint>
>          <!-- Any product1 user -->
>          <role-name>product1</role-name>
>       </auth-constraint>
>   </security-constraint>
>   <security-constraint>
>      <web-resource-collection>
>         <web-resource-name>Product2 Client Area</web-resource-name>
>         <url-pattern>/clients/product2/*</url-pattern>
>       </web-resource-collection>
>       <auth-constraint>
>          <!-- Any product2 user -->
>          <role-name>product2</role-name>
>       </auth-constraint>
>   </security-constraint>
>
> The way the 2.4 spec is written, all authenticated users have access to
> everything under /myapp/clients/.  To get what I want is now a
configuration
> nightmare :(.
> </whine>
>
> Now back to fixing things.  I sort of like the idea of changing the Realm
> interface so that 'hasUserDataPermissions' and 'hasResourcePermissions'
take
> a SecurityConstraint [].  However, after a GA release, this may be a bit
> much.  Philippe's solution looks a bit over-kill to me, but I'm not going
to
> object if someone wants to commit it.  I'm thinking of moving the
> header-setting stuff out of RealmBase and into AuthenticatorBase.  It may
> cause some custom realms to stop working, but there shouldn't be that many
> for TC 5 yet :).
>
> Of course, I'm volunteering for the code-monkey (&copy; Pier) part of
this.
> Since anyway we go is a pretty big change, I just wanted to get a
consensus
> first.
>
> Opinions, Comments, Flames?
>
> ----- Original Message -----
> From: "philippe.leothaud" <ph...@wanadoo.fr>
> To: <to...@jakarta.apache.org>
> Sent: Monday, December 08, 2003 6:43 PM
> Subject: Tomcat authorization handling seems not to function according to
> Servlet 2.4 Spec
>
>



---------------------------------------------------------------------
To unsubscribe, e-mail: tomcat-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: tomcat-dev-help@jakarta.apache.org


Re: Tomcat authorization handling seems not to function according to Servlet 2.4 Spec

Posted by Remy Maucherat <re...@apache.org>.
Bill Barker wrote:
> Ok, I take back my <whine/>.  It seems that they have really made a hash out
> of the security-constraints.  Something like Philippe's implementation is
> required.  Section 12.8.3 requires that only the 'best match' constraints
> are processed, and those in a 'grant' fashion (i.e. you get the least
> restrictive privilege of the most restrictive constraints).  Now you just
> need to be a rocket-scientist to figure out how your security-constraints
> work ;-). So in my example below, a request for /myapp/clients/product1/
> will only consider the 'product1' constraint, and ignore the constraint for
> '/clients/*'.  If I had added a security-constraint for '*.jsp', then a
> request for /myapp/clients/product1/index.jsp would use the 'product1'
> constraint, and ignore the '*.jsp' constraint.  Isn't life going to be fun
> on the user-list ;-).
> 
> This means that RealmBase.findSecurityConstraints is going to have to change
> to only pass back the 'best-match' constraints.  At least this isn't an
> interface change.  The decision on whether to change the Realm interface, or
> move the header processing to AuthenticatorBase is still open.

I don't care much about this problem. Constraint composition is too 
complex for the real world, and generally container provided auth is 
just bad to use in real websites. This plus the fact that those changes 
are brand new (and apparently not tested by Sun's compatibility kit, or 
Jean François would have done something about it). So this should not 
impact many people.

I'm ok to change the Realm interface, as there's a RealmBase abstract 
class. I would believe most realms extend it, so there shouldn't be too 
much trouble.
We should take ample time to fix this, to make sure this is correct when 
done :)

Rémy



---------------------------------------------------------------------
To unsubscribe, e-mail: tomcat-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: tomcat-dev-help@jakarta.apache.org


Re: Tomcat authorization handling seems not to function according to Servlet 2.4 Spec

Posted by Bill Barker <wb...@wilshire.com>.
Ok, I take back my <whine/>.  It seems that they have really made a hash out
of the security-constraints.  Something like Philippe's implementation is
required.  Section 12.8.3 requires that only the 'best match' constraints
are processed, and those in a 'grant' fashion (i.e. you get the least
restrictive privilege of the most restrictive constraints).  Now you just
need to be a rocket-scientist to figure out how your security-constraints
work ;-). So in my example below, a request for /myapp/clients/product1/
will only consider the 'product1' constraint, and ignore the constraint for
'/clients/*'.  If I had added a security-constraint for '*.jsp', then a
request for /myapp/clients/product1/index.jsp would use the 'product1'
constraint, and ignore the '*.jsp' constraint.  Isn't life going to be fun
on the user-list ;-).

This means that RealmBase.findSecurityConstraints is going to have to change
to only pass back the 'best-match' constraints.  At least this isn't an
interface change.  The decision on whether to change the Realm interface, or
move the header processing to AuthenticatorBase is still open.

----- Original Message ----- 
From: "Bill Barker" <wb...@wilshire.com>
To: "Tomcat Developers List" <to...@jakarta.apache.org>
Sent: Monday, December 08, 2003 9:00 PM
Subject: Re: Tomcat authorization handling seems not to function according
to Servlet 2.4 Spec


> Yes, this looks like it changed between pfd3 to fr :(.
Security-constraints
> now work like 'grants' instead of 'constraints'.  IMHO, this make the 2.4
> security model all but useless.
> <whine>
> It would be natural to configure something like:
>   <security-constraint>
>      <web-resource-collection>
>         <web-resource-name>Client Area</web-resource-name>
>         <url-pattern>/clients/*</url-pattern>
>       </web-resource-collection>
>       <auth-constraint>
>          <!-- Any authenticated user -->
>          <role-name>*</role-name>
>       </auth-constraint>
>   </security-constraint>
>   <security-constraint>
>      <web-resource-collection>
>         <web-resource-name>Product1 Client Area</web-resource-name>
>         <url-pattern>/clients/product1/*</url-pattern>
>       </web-resource-collection>
>       <auth-constraint>
>          <!-- Any product1 user -->
>          <role-name>product1</role-name>
>       </auth-constraint>
>   </security-constraint>
>   <security-constraint>
>      <web-resource-collection>
>         <web-resource-name>Product2 Client Area</web-resource-name>
>         <url-pattern>/clients/product2/*</url-pattern>
>       </web-resource-collection>
>       <auth-constraint>
>          <!-- Any product2 user -->
>          <role-name>product2</role-name>
>       </auth-constraint>
>   </security-constraint>
>
> The way the 2.4 spec is written, all authenticated users have access to
> everything under /myapp/clients/.  To get what I want is now a
configuration
> nightmare :(.
> </whine>
>
> Now back to fixing things.  I sort of like the idea of changing the Realm
> interface so that 'hasUserDataPermissions' and 'hasResourcePermissions'
take
> a SecurityConstraint [].  However, after a GA release, this may be a bit
> much.  Philippe's solution looks a bit over-kill to me, but I'm not going
to
> object if someone wants to commit it.  I'm thinking of moving the
> header-setting stuff out of RealmBase and into AuthenticatorBase.  It may
> cause some custom realms to stop working, but there shouldn't be that many
> for TC 5 yet :).
>
> Of course, I'm volunteering for the code-monkey (&copy; Pier) part of
this.
> Since anyway we go is a pretty big change, I just wanted to get a
consensus
> first.
>
> Opinions, Comments, Flames?
>
> ----- Original Message ----- 
> From: "philippe.leothaud" <ph...@wanadoo.fr>
> To: <to...@jakarta.apache.org>
> Sent: Monday, December 08, 2003 6:43 PM
> Subject: Tomcat authorization handling seems not to function according to
> Servlet 2.4 Spec
>
>
> Hi all,
>
> I am new to Tomcat's mailing lists, and I don't really know if this list
is
> the right place for such a post : excuse me if it is not the case.
>
> I wonder if I didn't notice something which is not a real bug in Tomcat,
as
> it seems to do exactly what developers want it to do,
> but more a difference between the implementation of authorization policy
> (the handling of a Web Application web.xml
> security-constraint elements) in Tomcat5 and what the Servlet 2.4 Spec
says.
>
> Example of the problem (from the Tomcat Jsp-examples WebApp) :
>
> <?xml version='1.0' encoding='utf-8'?>
> <tomcat-users>
>   <role rolename="tomcat"/>
>   <role rolename="role1"/>
>   <role rolename="manager"/>
>   <role rolename="admin"/>
>   <user username="tomcat" password="tomcat" roles="tomcat"/>
>   <user username="both" password="tomcat" roles="tomcat,role1"/>
>   <user username="role1" password="tomcat" roles="role1"/>
>   <user username="admin" password="tomcat" roles="admin,manager,tomcat"/>
> </tomcat-users>
>
> tomcat-users.xml
>
> <security-constraint>
>  <display-name>Example Security Constraint</display-name>
>  <web-resource-collection>
>   <web-resource-name>Protected Area</web-resource-name>
>   <!-- Define the context-relative URL(s) to be protected -->
>   <url-pattern>/security/protected/*</url-pattern>
>   <!-- If you list http methods, only those methods are protected -->
>   <http-method>DELETE</http-method>
>   <http-method>GET</http-method>
>   <http-method>POST</http-method>
>   <http-method>PUT</http-method>
>  </web-resource-collection>
>  <auth-constraint>
>   <!-- Anyone with one of the listed roles may access this area -->
>   <role-name>tomcat</role-name>
>  </auth-constraint>
> </security-constraint>
>
> <security-constraint>
>  <display-name>Example Security Constraint</display-name>
>  <web-resource-collection>
>   <web-resource-name>Protected Area</web-resource-name>
>   <!-- Define the context-relative URL(s) to be protected -->
>   <url-pattern>/security/protected/*</url-pattern>
>   <!-- If you list http methods, only those methods are protected -->
>   <http-method>DELETE</http-method>
>   <http-method>GET</http-method>
>   <http-method>POST</http-method>
>   <http-method>PUT</http-method>
>  </web-resource-collection>
>  <auth-constraint>
>   <!-- Anyone with one of the listed roles may access this area -->
>   <role-name>role1</role-name>
>  </auth-constraint>
> </security-constraint>
>
> <!-- Default login configuration uses form-based authentication -->
> <login-config>
>  <auth-method>FORM</auth-method>
>  <realm-name>Example Form-Based Authentication Area</realm-name>
>  <form-login-config>
>   <form-login-page>/security/protected/login.jsp</form-login-page>
>   <form-error-page>/security/protected/error.jsp</form-error-page>
>  </form-login-config>
> </login-config>
>
> <!-- Security roles referenced by this web application -->
> <security-role>
>  <role-name>role1</role-name>
> </security-role>
> <security-role>
>  <role-name>tomcat</role-name>
> </security-role>
>
> webapps/jsp-examples/WEB-INF/web.xml (excerpt)
>
> I've been adding  a new security-constraint element, separing the
authorized
> roles each in its security-constraint
>
> According to what the Servlet 2.4 says (see below for exact reference),
two
> security constraints on the same
> (url-pattern, http-method) should result in the addition of the given
> authorizations and so in this case,
> users "tomcat", "role1" and "both" should be authorized to access the
> protected resource.
>
> But here, it is the contrary : you can't access
> http://10.160.4.205:8080/jsp-examples/security/protected/ under
> "tomcat" or "role1" identity any more, but you can still using the "both"
> identity : Tomcat has realized the intersection
> of the authorizations instead of doing the union.
>
>
> Analyze of the problem
>
> After inverstigating a while in the code, here is what I noticed :
>
> First,
>
> In SecurityConstraint[] RealmBase.findSecurityConstraints(HttpRequest
> request, Context context)
> (the method begins at l. 445 of the org.apache.catalina.realm.RealmBase
> file),
>
> each and every SecurityConstraint (<=> security-constraint in web.xml)
> containing a SecurityCollection
> (<=> web-ressource-collection in web.xml) containing a url-pattern
matching
> the User's request URI
> and defining a restriction on the http-method used by the user for his
> request is retrieved, using
>
> boolean SecurityConstraint.included(String uri, String method)
> (method starts at line 343 of
org.apache.catalina.deploy.SecurityConstraint)
>
> While only SecurityConstraints containing SecurityCollections containing
the
> url-pattern which is the
> best-match to the User's request URI amongst all the url-patterns defined
in
> web.xml should be retained first, and then amongst
> these remaining constraints we shall keep only the ones defining a
> restriction on the same method (or no restriction
> on the method, as stated in servlet-2_4-fr-spec.pdf, ch SRV 12-8-3, pp
> 100-101)
>
>
> Second
>
> in public boolean hasResourcePermission(HttpRequest request,
>                                        HttpResponse response,
>                                        SecurityConstraint constraint,
>                                        Context context)
> (the method begins at line 501 of the org.apache.catalina.realm.RealmBase
> file)
>
> the restrictions on the authorized groups are analyzed, constraint after
> constraint, and as soon as one constraint is not verified,
>
> response.getResponse()).sendError(
>     HttpServletResponse.SC_FORBIDDEN,
>     sm.getString("realmBase.forbidden"));
>
> is sent to the User : this means that at the contrary of what the spec
says,
> for a same
> (http-method, url-pattern) couple, it's not the union of the
authorizations
> but the intersection that is realized.
>
> Spec : The rules to combine roles are given in servlet-2_4-fr-spec.pdf, ch
> SRV 12.8.1, pp97-98 :
>
>  "The combination of authorization constraints that name roles or that
imply
>  roles via the name shall yield the union of the role names in the
> individual
>  constraints as permitted roles. A security constraint that does not
contain
> an
>  authorization constraint shall combine with authorization constraints
that
> name or
>  imply roles to allow unauthenticated access. The special case of an
> authorization
>  constraint that names no roles shall combine with any other constraints
to
> override
>  their affects and cause access to be precluded."
>
>
> Third
>
> A similar problem as the second one accurs in the call to
>
> public boolean hasUserDataPermission(HttpRequest request,
>                                      HttpResponse response,
>                                      SecurityConstraint constraint)
> (the method begins at line 627 of the org.apache.catalina.realm.RealmBase
> file)
>
> As in the second point, constraints are examined one by one, instead of
> determining globally the policy for all
> the constraints applying for the same (http-method, url-patern)
>
> Spec :The rules to combine user-data-constraints are given in
> servlet-2_4-fr-spec.pdf, ch SRV 12.8.1, p98 :
>
>  "The combination of user-data-constraints that apply to a common
urlpattern
>  and http-method shall yield the union of connection types accepted by
>  the individual constraints as acceptable connection types. A security
> constraint
>  that does not contain a user-data-constraint shall combine with other
> userdata-
>  constraints to cause the unprotected connection type to be an accepted
>  connection type."
>
>
> Possible workaround
>
> I've coded a simple workaround (see below), consisting mainly of a class,
> MergedConstraintBuilder,
> whose job is to build a fake SecurityConstraint for each(requestURI,
> httpMethod) couple,
> implementing the selection algorithms described in the spec (as I
understood
> it) :
>  1 - select the best matching url-pattern and retain the
SecurityCollections
> containing this pattern (if any)
>  2 - retaining only the constraint of this first set defining a constraint
> for the http-method (if any)
>  3 - determining the global user-data-contraint (ie transport protocol)
for
> the resulting set of constraints
>  4 - determining the authorized use groups for the set
>
> When the
>
> MergedConstraintBuilder.getMergedConstraintForRequest(SecurityConstraint[]
> allConstraints,
>                                                       String requestURI,
>                                                       String method)
>
> method is called, I use successively these four algorithms to select the
> applying SecurityConstraints, and then
> I first build a fake org.apache.catalina.deploy.SecurityCollection, with
the
> following properties :
>     - String name       :
>     - String[] patterns :  an array of Strings containing only one String,
> the request URI
>     - String[] methods  :  an array of Strings containing only one String,
> the request method
>
> Once the SecurityCollection built, I construct over it a fake
> SecurityConstraint with the folowing properties :
>     - boolean allRoles                  :  appropriately set by the fourth
> algorithm
>     - boolean authConstraint            :  appropriately set by the fourth
> algorithm
>     - String[] roleNames                :  appropriately set by the fourth
> algorithm
>     - String userConstraint             :  the the global
> user-data-contraint as returned by the third algorithm
>     - SecurityCollection[] collections  :  an array containing a single
> element, the previously determined
>
> fake SecurityCollection
>
> and I return it encapsulated in an array of SecurityConstraints, the goal
of
> tis encapsulation being to avoid breaking
> org.apache.catalina.realm.RealmBase existing code.
>
>
> In order to put this piece of code to work, we have to have
> org.apache.catalina.realm.RealmBase invoke it :
> I have so added MergedConstraintBuilder in the org.apache.catalina.realm
> package, I've modified the
> public SecurityConstraint[] findSecurityConstraints(HttpRequest request,
> Context context) method of
> RealmBase (see just below), and I also added three short utilities methods
> in org.apache.catalina.deploy.SecurityCollection
> (these utilities are in charge to retrieve the best matching url-pattern)
>
> I didn't do extensive testing, but the spec examples work (and some more,
> too ;))
>
> I don't know if the difference with the spec is a will or not, so I don't
> know if this will help
>
> Anyway, it was fun
>
> Philippe (philippe.leothaud@wanadoo.fr)
>
> Note : the fake SecurityConstraints could actually be cached, so that the
> computation
> is done only once for a (URI, http-method) couple : I've got another
version
> of the MergedConstraintBuilder providing
> an implementation of this strategy.
>
> --------------------------------------------------------------------------
--
> --------------------------------------------------------------------------
--
> --------------------------
> Changed SecurityConstraint[] findSecurityConstraints(HttpRequest request,
> Context context)
> method in org.apache.catalina.realm.RealmBase (starting at line 438)
> --------------------------------------------------------------------------
--
> --------------------------------------------------------------------------
--
> --------------------------
>     /** l.438 org.apache.catalina.realm.RealmBase
>      *
>      * Return the SecurityConstraints configured to guard the request URI
> for
>      * this request, or <code>null</code> if there is no such constraint.
>      *
>      * @param request Request we are processing
>      * @param context Context the Request is mapped to
>      */
>     public SecurityConstraint[] findSecurityConstraints(HttpRequest
request,
> Context context) {
>
>         // Are there any defined security constraints?
>         SecurityConstraint constraints[] = context.findConstraints();
>         if ((constraints == null) || (constraints.length == 0)) {
>             if (log.isDebugEnabled())
>              log.debug("  No applicable constraints defined");
>             return (null);
>         }
>
>         HttpServletRequest hreq = (HttpServletRequest)
request.getRequest();
>         String uri = request.getDecodedRequestURI();
>         String contextPath = hreq.getContextPath();
>         if (contextPath.length() > 0) uri =
> uri.substring(contextPath.length());
>         String method = hreq.getMethod();
>
>      MergedConstraintBuilder builder = new MergedConstraintBuilder ();
>      return getMergedConstraintForRequest(allConstraints, uri, method);
>         // Check each defined security constraint
>     }
> --------------------------------------------------------------------------
--
> --------------------------------------------------------------------------
--
> --------------------------
>
>
>  -------------------------------------------------------------------------
--
> --------------------------------------------------------------------------
--
> ---------------------------
>  Methods to be added to the org.apache.catalina.deploy.SecurityCollection
> class
> --------------------------------------------------------------------------
--
> --------------------------------------------------------------------------
--
> --------------------------
>   /**
>   * Builds and returns the <code>List</code> of all the
> <code>SecurityCollection</code>s
>   * part of this <code>SecurityConstraint</code> and containing amongst
> their
>   * <code>url-pattern</code>s at least one pattern matching exactly the
> given requestURI.
>   *
>   * @param requestURI : the URI to match exactly
>   * @return           : the <code>List</code> of the
> <code>SecurityCollection</code>s
>   *                     containing a pattern matching this URI
>   */
>  public List getExactMatchingWebCollections(String requestURI) {
>   List exactMatchingCollections = null;
>   for (int i = 0; i < collections.length; i++) {
>    String patterns[] = collections[i].getPatterns();
>    for (int j = 0; j < patterns.length; j++) {
>     if (requestURI.equals(patterns[j])) {
>      if(exactMatchingCollections == null) {
>       exactMatchingCollections = new ArrayList();
>      }
>      exactMatchingCollections.add(collections[i]);
>      break;
>     }
>    }
>   }
>   return exactMatchingWebCollections;
>  }
>
>  /**
>   * Builds and returns the <code>List</code> of all the
> <code>SecurityCollection</code>s
>   * part of this <code>SecurityConstraint</code> and containing amongst
> their
>   * <code>url-pattern</code>s at least one pattern matching exactly the
> given URI's bestMatch.
>   *
>   * @param bestMatch : the URI's bestMatch to match exactly
>   * @return          : the <code>List</code> of the
> <code>SecurityCollection</code>s
>   *                    containing a pattern matching this URI's bestMatch
>   */
>  public List getMatchingWebCollections(String bestMatch) {
>   List matchingCollections = null;
>   for (int i = 0; i < collections.length; i++) {
>    String patterns[] = collections[i].getPatterns();
>    for (int j = 0; j < patterns.length; j++) {
>     if (bestMatch.equals(patterns[j])) {
>      if(matchingCollections == null) {
>       matchingCollections = new ArrayList();
>      }
>      matchingCollections.add(collections[i]);
>      break;
>     }
>    }
>   }
>   return matchingCollections;
>  }
>
>  /**
>   * Gets <code>url-pattern</code> which is the best match to the given
URI,
> amongst
>   * all the <code>SecurityCollection</code>s part of this
> <code>SecurityConstraint</code>
>   *
>   * @param requestURI : the URI's to match best
>   * @return           : the <code>String</code> representation of the
> <code>url-pattern</code>
>   *                     which is best matching the given URI, amongst all
> the patterns of all the
>   *                     <code>SecurityCollection</code>s of this
> <code>SecurityConstraint</code>
>   */
>  public String getBestMatch(String requestURI) {
>   String bestMatch = "";
>   for (int i = 0; i < collections.length; i++) {
>    String patterns[] = collections[i].getPatterns();
>    for (int j = 0; j < patterns.length; j++) {
>     if (matchPattern(requestURI, patterns[j])) {
>      if(patterns[j].length() > bestMatch.length())
>       bestMatch = patterns[j];
>     }
>    }
>   }
>   return bestMatch;
>  }
> --------------------------------------------------------------------------
--
> --------------------------------------------------------------------------
--
> --------------------------
>
>
>
> --------------------------------------------------------------------------
--
> --------------------------------------------------------------------------
--
> --------------------------
> Class MergedConstraintBuilder, to be added in the
org.apache.catalina.realm
> package
> --------------------------------------------------------------------------
--
> --------------------------------------------------------------------------
--
> --------------------------
> package org.apache.catalina.realm;
>
> import java.util.Collection;
> import java.util.HashMap;
> import java.util.Iterator;
> import java.util.List;
> import java.util.Map;
> import java.util.Set;
>
> import javax.servlet.http.HttpServletRequest;
>
> import org.apache.log4j.Logger;
>
>  /**
>   * Builds a custom <code>SecurityConstraint</code> merging all valid
> <code>SecurityConstraint</code>s
>   * for the method and URI that will be extracted from the given
> <code>HttpServletRequest</code>, or
>   * <code>null</code> if there is no such <code>SecurityConstraint</code>.
>   *
>   * The process to determine which <code>SecurityConstraint</code>s are
> valid for a URI and a method
>   * is defined in servlet-2_4-fr-spec.pdf, ch SRV 12-8-3, pp 100-101 :
>   *  1 - "Select the constraints (if any) defined on the url-pattern that
is
> the
>   *         best match to the request URI. If no constraints are selected,
> the container shall
>   *         accept the request." (i.e. the custom Constraint is null)
>   *  2 - "Determine if the HTTP method of the request is constrained at
the
> selected pattern.
>   *         If it is not, the request shall be accepted." (i.e. the custom
> Constraint is null)
>   *  3 -  Determine the user-data-constraint, given that : "The
> characteristics of the connection
>   *         on which the request was received must satisfy at least one of
> the supported
>   *         connection types defined by the constraints."
>   *  4 -  Determine the array of authorized roles, given that : "The
> authentication characteristics
>   *         of the request must satisfy any authentication and role
> requirements
>   *         defined by the constraints."
>   *
>   * After applying this process, the custom
<code>SecurityConstraint</code>
> is created with the
>   * following properties :
>   *  - display-name = requestURI + "::" + requestMethod
>   *  - SecurityCollection[] collection =
>   *          new SecurityCollection[] {
>   *              new SecurityCollection(requestURI + "::" + requestMethod,
>   *          new String[] { requestURI },
>   *          new String[] { requestMethod } ) }
>   *  - boolean authConstraint
>   *  - boolean allRoles
>   *  - String[] authorizedRoles
>   */
> public class MergedConstraintBuilder {
>
>  static Logger logger =
> Logger.getLogger(MergedConstraintBuilder.class.getName());
>
>
>  /**
>   * Special method for integration with Catalina's RealmBase.
>   *
>   * @param allConstraints : all the <code>SecurityConstraint</code>s
defined
> in <code>web.xml</code>
>   * @param req            : the request of the User
>   *
>   * @return               : the custom <code>SecurityConstraint</code>,
> wrapped in an array
>    *                                                   of
> <code>SecurityConstraint</code>s
>   */
>  public SecurityConstraint[] getMergedConstraintForRequest(
>      SecurityConstraint[] allConstraints,
>      String requestURI,
>      String method) {
>
>   return new SecurityConstraint[] {
> mergeConstraintsForRequest(allConstraints, requestURI, method) };
>
>  }
>
>
>  /**
>   * Builds and returns a custom <code>SecurityConstraint</code> merging
all
> valid <code>SecurityConstraint</code>s
>   * for the method and URI that will be extracted from the given
> <code>HttpServletRequest</code>, or
>   * <code>null</code> if there is no such <code>SecurityConstraint</code>.
>   *
>   * @param allConstraints : all the <code>SecurityConstraint</code>s
defined
> in <code>web.xml</code>
>   * @param req            : the request of the User
>   *
>   * @return               : the custom <code>SecurityConstraint</code>
>   */
>
>  public SecurityConstraint mergeConstraintsForRequest(
>      SecurityConstraint[] allConstraints,
>      HttpServletRequest req) {
>
>   // On determine l'URI contextuelle et la methode de la requete
>   String requestURI = req.getRequestURI();
>   String contextPath = req.getContextPath();
>   if (contextPath.length() > 0) {
>    requestURI = requestURI.substring(contextPath.length());
>   }
>   String method = req.getMethod();
>   return mergeConstraintsForRequest(allConstraints, requestURI, method);
>
>  }
>
>
>  /**
>   * Builds and returns a custom <code>SecurityConstraint</code> merging
all
> valid
>   * <code>SecurityConstraint</code>s for the given method and URI, or
> <code>null</code>
>   * if there is no such <code>SecurityConstraint</code>
>   *
>   * @param allConstraints : all the <code>SecurityConstraint</code>s
defined
> in <code>web.xml</code>
>   * @param requestURI     : URI of the User's request
>   * @param method         : method of the User's request
>   *
>   * @return               : the custom <code>SecurityConstraint</code>
>   */
>  public SecurityConstraint mergeConstraintsForRequest(
>      SecurityConstraint[] allConstraints,
>      String requestURI,
>      String method) {
>
>   Map matchingConstraintsAndWebResources =
>       filterConstraintsByURI(allConstraints, requestURI);
>   if(matchingConstraintsAndWebResources == null) {
>    return null;
>   }
>   Collection matchingConstraints =
>       filterConstraintsByMethod(matchingConstraintsAndWebResources,
method);
>   if(matchingConstraints == null) {
>    return null;
>   }
>   // getAuthorizedRoles() takes care of setting the boolean allRoles
>   // and boolean authConstraint appropriately
>   SecurityConstraint mergedSecurityConstraint =
>       getAuthorizedRoles(matchingConstraints);
>   String userConstraint = getUserConstraint(matchingConstraints);
>   SecurityCollection mergedCollection = new SecurityCollection(
>       requestURI + "::" + method,
>       new String[] { requestURI },
>       new String[] { method } );
>   mergedSecurityConstraint.setDisplayName(requestURI + "::" + method);
>   mergedSecurityConstraint.setSecurityCollection(
>       new SecurityCollection[] { mergedCollection });
>   mergedSecurityConstraint.setUserConstraint(userConstraint);
>   return mergedSecurityConstraint;
>  }
>
>  /**
>   * Returns a <code>Map</code> containing the
> <code>SecurityConstraint</code>s
>   * and associated <code>SecurityCollection</code>s valid for the request
> URI (that is,
>   * all the <code>SecurityConstraint</code>s containing at least a
> <code>SecurityCollection</code>
>   * containing the <code>url-pattern</code> which is the best-matching
> pattern for the given URI,
>   * amongst all the <code>url-pattern</code>s defined in the webApp's
> <code>web.xml</code>),
>   * or null if there is no such <code>SecurityCollection</code> (and
> therfore, no such
>   * <code>SecurityConstraint</code>.
>   *
>   * The rules to determine the best-matching pattern for the given URI are
> defined in
>   * servlet-2_4-fr-spec.pdf, ch SRV 11-1 pp 85-86 :
>   *  1 - "The container will try to find an exact match of the path of the
> request to the
>   *        path of the servlet."
>   *  2 - "The container will recursively try to match the longest
> path-prefix."
>   *  3 - "If the last segment in the URL path contains an extension (e.g.
> .jsp), the servlet
>   *        container will try to match (...) the extension".
>   *
>   *
>   * @param allConstraints : all the <code>SecurityConstraint</code>s
defined
> in web.xml
>   * @param requestURI     : URI of the User's request
>   *
>   * @return               : a <code>Map</code> containing the
> <code>SecurityConstraint</code>s and
>   *                         associated <code>SecurityCollection</code>s
> valid for the request URI
>   */
>  public Map filterConstraintsByURI(SecurityConstraint[] allConstraints,
> String requestURI) {
>   if(logger.isDebugEnabled())
>    logger.debug("MergedConstraintBuilder.filterConstraintsByURI() - "
>     + "Checking SecurityConstraints " + allConstraints
>     + " against URI " + requestURI);
>   // Determining valid constraints, checking the constraints' url-patterns
> against the given requestURI
>   Map constraintsAndCollections = null;
>   boolean exactMatch = false;
>   for (int i = 0; i < allConstraints.length; i++) {
>    // First choice : "case-exact-match" url-patterns
>    List exactMatchingWebCollections =
>        allConstraints[i].getExactMatchingWebCollections(requestURI);
>    if(logger.isDebugEnabled())
>     logger.debug("MergedConstraintBuilder.filterConstraintsByURI() - "
>      + "List exactMatchingWebCollections obtained : " +
> exactMatchingWebCollections);
>    if(exactMatchingWebCollections != null) {
>     if(constraintsAndCollections == null) {
>      constraintsAndCollections = new HashMap();
>     }
>     constraintsAndCollections.put(allConstraints[i],
> exactMatchingWebCollections);
>    }
>   }
>   if(constraintsAndCollections == null) {
>    // Second choice : "pattern-match" url-patterns
>    // Determining the best-matching pattern (=the longest amongst matching

> patterns)
>    // We keep constraints containing at least one WebCollection containing
> the pattern
>    // and remove the others
>    String bestMatch = "";
>    for (int i = 0; i < allConstraints.length; i++) {
>     String constraintBestMatch =
allConstraints[i].getBestMatch(requestURI);
>     if(constraintBestMatch.length() > bestMatch.length()) {
>      bestMatch = constraintBestMatch;
>     }
>    }
>    if(logger.isDebugEnabled())
>     logger.debug("MergedConstraintBuilder.filterConstraintsByURI() - "
>      + "bestMatch obtained : " + bestMatch);
>    for (int i = 0; i < allConstraints.length; i++) {
>     List bestMatchingWebCollections =
> allConstraints[i].getMatchingWebCollections(bestMatch);
>     if(logger.isDebugEnabled())
>      logger.debug("MergedConstraintBuilder.filterConstraintsByURI() - "
>      + "partial bestMatchingWebCollections List obtained : " +
> bestMatchingWebCollections);
>     if(bestMatchingWebCollections != null) {
>      if(constraintsAndCollections == null) {
>       constraintsAndCollections = new HashMap();
>      }
>      if(logger.isDebugEnabled())
>       logger.debug("MergedConstraintBuilder.filterConstraintsByURI() - "
>       + "bestMatchingWebCollections List : " + bestMatchingWebCollections
>       + " stored for SecurityConstraint : " + allConstraints[i]);
>      constraintsAndCollections.put(allConstraints[i],
> bestMatchingWebCollections);
>     }
>    }
>   } else {
>    exactMatch = true;
>   }
>   if(constraintsAndCollections == null) {
>    // No matching constraint
>    if(logger.isDebugEnabled())
>     logger.debug("MergedConstraintBuilder.filterConstraintsByURI() - "
>     + "No SecurityConstraint restraining URI " + requestURI
>     + " amongst given constraints " + allConstraints);
>    return null;
>   }
>   return constraintsAndCollections;
>  }
>
>  /**
>   * Returns a <code>Collection</code> containing the
> <code>SecurityConstraint</code>s restraining tne
>   * use of the given HTTP method, or null if there is no such
> <code>SecurityConstraint</code>.
>   *
>   * A <code>SecurityConstraint</code> is restraining the use of a given
HTTP
> method if it contains
>   * at least a <code>SecurityColection</code> containing this HTTP method
> amongst the <code>String</code>s
>   * constituting its methods field (corresponding to the
> <code>http-method</code> element of the
>   * <code>web-resource-collection</code> element in the
> <code>web.xml</code>), or if its methods field is
>   * <code>null</code> or empty
>   *
>   * @param constraintsAndCollections : a <code>Map</code> containing the
> <code>SecurityConstraint</code>s
>   *                                    to analyze, and their associated
> <code>SecurityCollection</code>s
>   * @param method                    : the HTTP method against which the
> <code>SecurityConstraint</code>s
>   *                                    must be checked
>   *
>   * @return                          : a <code>Collection</code>
containing
> the
>   *                                    <code>SecurityConstraint</code>s
> valid for the given HTTP method
>   */
>  public Collection filterConstraintsByMethod(Map
constraintsAndCollections,
> String method) {
>   if(logger.isDebugEnabled())
>    logger.debug("MergedConstraintBuilder.filterConstraintsByMethod() - "
>    + "Checking Constraints-WebCollection Map " + constraintsAndCollections
>    + " against method " + method);
>   Set matchingConstraints = constraintsAndCollections.keySet();
>   Iterator matchingConstraintsIterator = matchingConstraints.iterator();
>   while (matchingConstraintsIterator.hasNext()) {
>    SecurityConstraint constraint = (SecurityConstraint)
> matchingConstraintsIterator.next();
>    List matchingWebCollections = (List)
> constraintsAndCollections.get(constraint);
>    Iterator matchingWebCollectionsIterator =
> matchingWebCollections.iterator();
>    boolean methodIsProtected = false;
>    // Pour chaque contrainte, il suffit de trouver une seule WebCollection
>    while (matchingWebCollectionsIterator.hasNext()) {
>     SecurityCollection collection = (SecurityCollection)
> matchingWebCollectionsIterator.next();
>     String[] constrainedMethods = collection.getMethods();
>     if(constrainedMethods == null || constrainedMethods.length == 0) {
>      methodIsProtected = true;
>      break;
>     }
>     for (int i = 0; i < constrainedMethods.length; i++) {
>      if(method.equals(constrainedMethods[i])) {
>       methodIsProtected = true;
>       break;
>      }
>     }
>     if(methodIsProtected) {
>      break;
>     }
>    }
>    if(!methodIsProtected) {
>     matchingConstraintsIterator.remove();
>    }
>   }
>   if(matchingConstraints.size() == 0) {
>
   System.out.println("MergedConstraintBuilder.filterConstraintsByMethod() -
> "
>     + "No SecurityConstraint restraining method " + method
>     + " in the Constraints-WebCollection Map");
>    return null;
>   }
>   return matchingConstraints;
>  }
>
>
>  /**
>   * Combines all the given <code>SecurityConstraint</code>s to determine
and
> return as a
>   * <code>String</code> the applying user-data-constraint
>   *
>   * The rules to combine user-data-constraints are given in
> servlet-2_4-fr-spec.pdf, ch SRV 12.8.1, p98 :
>   * "The combination of user-data-constraints that apply to a common
> urlpattern
>   * and http-method shall yield the union of connection types accepted by
>   * the individual constraints as acceptable connection types. A security
> constraint
>   * that does not contain a user-data-constraint shall combine with other
> userdata-
>   * constraints to cause the unprotected connection type to be an accepted
>   * connection type."
>   *
>   * @param matchingConstraints : The <code>Collection</code> of
> <code>SecurityConstraint</code>s
>   *                              to analyze and combine
>   *
>   * @return                    : A <code>String</code> containing the
> applying user-data-constraint
>   */
>  public String getUserConstraint(Collection matchingConstraints) {
>   SecurityConstraint mergedSecurityConstraint = new SecurityConstraint();
>   mergedSecurityConstraint.setUserConstraint("INTEGRAL");
>   Iterator matchingConstraintsIterator = matchingConstraints.iterator();
>   while (matchingConstraintsIterator.hasNext()) {
>    SecurityConstraint constraint = (SecurityConstraint)
> matchingConstraintsIterator.next();
>    String userConstraint = constraint.getUserConstraint();
>    if (userConstraint == null || userConstraint.equals("NONE")) {
>     mergedSecurityConstraint.setUserConstraint("NONE");
>     break;
>    } else if (userConstraint.equals("CONFIDENTIAL")) {
>     mergedSecurityConstraint.setUserConstraint("CONFIDENTIAL");
>    }
>   }
>   if (logger.isDebugEnabled())
>    logger.debug(
>        "MergedConstraintBuilder.getUserConstraint() - "
>            + "String userConstraint obtained : "
>            + mergedSecurityConstraint.getUserConstraint());
>   return mergedSecurityConstraint.getUserConstraint();
>  }
>
>
>  /**
>   * Combines all the given <code>SecurityConstraint</code>s to determine
and
> return as an
>   * array of <code>String</code>s the set of authorized roles
>   *
>   * The rules to combine roles are given in servlet-2_4-fr-spec.pdf, ch
SRV
> 12.8.1, pp97-98 :
>   * "The combination of authorization constraints that name roles or that
> imply
>   * roles via the name "*" shall yield the union of the role names in the
> individual
>   * constraints as permitted roles. A security constraint that does not
> contain an
>   * authorization constraint shall combine with authorization constraints
> that name or
>   * imply roles to allow unauthenticated access. The special case of an
> authorization
>   * constraint that names no roles shall combine with any other
constraints
> to override
>   * their affects and cause access to be precluded."
>   *
>   * @param matchingConstraints : The <code>Collection</code> of
> <code>SecurityConstraint</code>s
>   *                              to analyze and combine
>   *
>   * @return                    : A <code>SecurityConstraint</code>
> containing the authorized roles.
>   */
>  public SecurityConstraint getAuthorizedRoles(Collection
> matchingConstraints) {
>   SecurityConstraint mergedSecurityConstraint = new SecurityConstraint();
>   mergedSecurityConstraint.setAuthConstraint(true);
>   String[] groups = new String[0];
>   Iterator matchingConstraintsIterator = matchingConstraints.iterator();
>   while (matchingConstraintsIterator.hasNext()) {
>    SecurityConstraint constraint = (SecurityConstraint)
> matchingConstraintsIterator.next();
>    if(constraint.getAuthConstraint()) {
>     String[] roleNames = constraint.getRoleNames();
>     if(roleNames == null || roleNames.length == 0) {
>      mergedSecurityConstraint.setAuthConstraint(true);
>      mergedSecurityConstraint.setRoleNames(new String[0]);
>      break;
>     } else {
>      if (!mergedSecurityConstraint.getAuthConstraint() ||
> mergedSecurityConstraint.getAllRoles()) {
>       continue;
>      }
>      mergedSecurityConstraint.setAuthConstraint(true);
>      if(constraint.getAllRoles()) {
>       mergedSecurityConstraint.setRoleNames(new String[] { "*" } );
>       continue;
>      }
>      for(int j=0; j<roleNames.length; j++) {
>       String roleName = roleNames[j];
>       // Check if this role is already registered
>       for(int k=0; k<groups.length; k++) {
>        if(roleName.equals(groups[k]))
>         break;
>       }
>       // If not, add it to the role array
>       String[] newGroups = new String[groups.length + 1];
>       System.arraycopy(groups, 0, newGroups, 0, groups.length);
>       newGroups[groups.length] = roleName;
>       groups = newGroups;
>      }
>     }
>     mergedSecurityConstraint.setRoleNames(groups);
>    } else {
>     mergedSecurityConstraint.setAuthConstraint(false);
>    }
>   }
>   if (logger.isDebugEnabled())
>    logger.debug(
>        "MergedConstraintBuilder.getUserConstraint() - "
>            + "String[] groups obtained and added to the returned
> SecurityConstraint : "
>            + mergedSecurityConstraint);
>   return mergedSecurityConstraint;
>
>  }
>
> }
> --------------------------------------------------------------------------
--
> --------------------------------------------------------------------------
--
> --------------------------
>
>
>


----------------------------------------------------------------------------
----


>
> This message is intended only for the use of the person(s) listed above as
the intended recipient(s), and may contain information that is PRIVILEGED
and CONFIDENTIAL.  If you are not an intended recipient, you may not read,
copy, or distribute this message or any attachment. If you received this
communication in error, please notify us immediately by e-mail and then
delete all copies of this message and any attachments.
>
> In addition you should be aware that ordinary (unencrypted) e-mail sent
through the Internet is not secure. Do not send confidential or sensitive
information, such as social security numbers, account numbers, personal
identification numbers and passwords, to us via ordinary (unencrypted)
e-mail.
>
>


----------------------------------------------------------------------------
----


> ---------------------------------------------------------------------
> To unsubscribe, e-mail: tomcat-dev-unsubscribe@jakarta.apache.org
> For additional commands, e-mail: tomcat-dev-help@jakarta.apache.org


Re: Tomcat authorization handling seems not to function according to Servlet 2.4 Spec

Posted by Bill Barker <wb...@wilshire.com>.
Yes, this looks like it changed between pfd3 to fr :(.  Security-constraints
now work like 'grants' instead of 'constraints'.  IMHO, this make the 2.4
security model all but useless.
<whine>
It would be natural to configure something like:
  <security-constraint>
     <web-resource-collection>
        <web-resource-name>Client Area</web-resource-name>
        <url-pattern>/clients/*</url-pattern>
      </web-resource-collection>
      <auth-constraint>
         <!-- Any authenticated user -->
         <role-name>*</role-name>
      </auth-constraint>
  </security-constraint>
  <security-constraint>
     <web-resource-collection>
        <web-resource-name>Product1 Client Area</web-resource-name>
        <url-pattern>/clients/product1/*</url-pattern>
      </web-resource-collection>
      <auth-constraint>
         <!-- Any product1 user -->
         <role-name>product1</role-name>
      </auth-constraint>
  </security-constraint>
  <security-constraint>
     <web-resource-collection>
        <web-resource-name>Product2 Client Area</web-resource-name>
        <url-pattern>/clients/product2/*</url-pattern>
      </web-resource-collection>
      <auth-constraint>
         <!-- Any product2 user -->
         <role-name>product2</role-name>
      </auth-constraint>
  </security-constraint>

The way the 2.4 spec is written, all authenticated users have access to
everything under /myapp/clients/.  To get what I want is now a configuration
nightmare :(.
</whine>

Now back to fixing things.  I sort of like the idea of changing the Realm
interface so that 'hasUserDataPermissions' and 'hasResourcePermissions' take
a SecurityConstraint [].  However, after a GA release, this may be a bit
much.  Philippe's solution looks a bit over-kill to me, but I'm not going to
object if someone wants to commit it.  I'm thinking of moving the
header-setting stuff out of RealmBase and into AuthenticatorBase.  It may
cause some custom realms to stop working, but there shouldn't be that many
for TC 5 yet :).

Of course, I'm volunteering for the code-monkey (&copy; Pier) part of this.
Since anyway we go is a pretty big change, I just wanted to get a consensus
first.

Opinions, Comments, Flames?

----- Original Message ----- 
From: "philippe.leothaud" <ph...@wanadoo.fr>
To: <to...@jakarta.apache.org>
Sent: Monday, December 08, 2003 6:43 PM
Subject: Tomcat authorization handling seems not to function according to
Servlet 2.4 Spec


Hi all,

I am new to Tomcat's mailing lists, and I don't really know if this list is
the right place for such a post : excuse me if it is not the case.

I wonder if I didn't notice something which is not a real bug in Tomcat, as
it seems to do exactly what developers want it to do,
but more a difference between the implementation of authorization policy
(the handling of a Web Application web.xml
security-constraint elements) in Tomcat5 and what the Servlet 2.4 Spec says.

Example of the problem (from the Tomcat Jsp-examples WebApp) :

<?xml version='1.0' encoding='utf-8'?>
<tomcat-users>
  <role rolename="tomcat"/>
  <role rolename="role1"/>
  <role rolename="manager"/>
  <role rolename="admin"/>
  <user username="tomcat" password="tomcat" roles="tomcat"/>
  <user username="both" password="tomcat" roles="tomcat,role1"/>
  <user username="role1" password="tomcat" roles="role1"/>
  <user username="admin" password="tomcat" roles="admin,manager,tomcat"/>
</tomcat-users>

tomcat-users.xml

<security-constraint>
 <display-name>Example Security Constraint</display-name>
 <web-resource-collection>
  <web-resource-name>Protected Area</web-resource-name>
  <!-- Define the context-relative URL(s) to be protected -->
  <url-pattern>/security/protected/*</url-pattern>
  <!-- If you list http methods, only those methods are protected -->
  <http-method>DELETE</http-method>
  <http-method>GET</http-method>
  <http-method>POST</http-method>
  <http-method>PUT</http-method>
 </web-resource-collection>
 <auth-constraint>
  <!-- Anyone with one of the listed roles may access this area -->
  <role-name>tomcat</role-name>
 </auth-constraint>
</security-constraint>

<security-constraint>
 <display-name>Example Security Constraint</display-name>
 <web-resource-collection>
  <web-resource-name>Protected Area</web-resource-name>
  <!-- Define the context-relative URL(s) to be protected -->
  <url-pattern>/security/protected/*</url-pattern>
  <!-- If you list http methods, only those methods are protected -->
  <http-method>DELETE</http-method>
  <http-method>GET</http-method>
  <http-method>POST</http-method>
  <http-method>PUT</http-method>
 </web-resource-collection>
 <auth-constraint>
  <!-- Anyone with one of the listed roles may access this area -->
  <role-name>role1</role-name>
 </auth-constraint>
</security-constraint>

<!-- Default login configuration uses form-based authentication -->
<login-config>
 <auth-method>FORM</auth-method>
 <realm-name>Example Form-Based Authentication Area</realm-name>
 <form-login-config>
  <form-login-page>/security/protected/login.jsp</form-login-page>
  <form-error-page>/security/protected/error.jsp</form-error-page>
 </form-login-config>
</login-config>

<!-- Security roles referenced by this web application -->
<security-role>
 <role-name>role1</role-name>
</security-role>
<security-role>
 <role-name>tomcat</role-name>
</security-role>

webapps/jsp-examples/WEB-INF/web.xml (excerpt)

I've been adding  a new security-constraint element, separing the authorized
roles each in its security-constraint

According to what the Servlet 2.4 says (see below for exact reference), two
security constraints on the same
(url-pattern, http-method) should result in the addition of the given
authorizations and so in this case,
users "tomcat", "role1" and "both" should be authorized to access the
protected resource.

But here, it is the contrary : you can't access
http://10.160.4.205:8080/jsp-examples/security/protected/ under
"tomcat" or "role1" identity any more, but you can still using the "both"
identity : Tomcat has realized the intersection
of the authorizations instead of doing the union.


Analyze of the problem

After inverstigating a while in the code, here is what I noticed :

First,

In SecurityConstraint[] RealmBase.findSecurityConstraints(HttpRequest
request, Context context)
(the method begins at l. 445 of the org.apache.catalina.realm.RealmBase
file),

each and every SecurityConstraint (<=> security-constraint in web.xml)
containing a SecurityCollection
(<=> web-ressource-collection in web.xml) containing a url-pattern matching
the User's request URI
and defining a restriction on the http-method used by the user for his
request is retrieved, using

boolean SecurityConstraint.included(String uri, String method)
(method starts at line 343 of org.apache.catalina.deploy.SecurityConstraint)

While only SecurityConstraints containing SecurityCollections containing the
url-pattern which is the
best-match to the User's request URI amongst all the url-patterns defined in
web.xml should be retained first, and then amongst
these remaining constraints we shall keep only the ones defining a
restriction on the same method (or no restriction
on the method, as stated in servlet-2_4-fr-spec.pdf, ch SRV 12-8-3, pp
100-101)


Second

in public boolean hasResourcePermission(HttpRequest request,
                                       HttpResponse response,
                                       SecurityConstraint constraint,
                                       Context context)
(the method begins at line 501 of the org.apache.catalina.realm.RealmBase
file)

the restrictions on the authorized groups are analyzed, constraint after
constraint, and as soon as one constraint is not verified,

response.getResponse()).sendError(
    HttpServletResponse.SC_FORBIDDEN,
    sm.getString("realmBase.forbidden"));

is sent to the User : this means that at the contrary of what the spec says,
for a same
(http-method, url-pattern) couple, it's not the union of the authorizations
but the intersection that is realized.

Spec : The rules to combine roles are given in servlet-2_4-fr-spec.pdf, ch
SRV 12.8.1, pp97-98 :

 "The combination of authorization constraints that name roles or that imply
 roles via the name shall yield the union of the role names in the
individual
 constraints as permitted roles. A security constraint that does not contain
an
 authorization constraint shall combine with authorization constraints that
name or
 imply roles to allow unauthenticated access. The special case of an
authorization
 constraint that names no roles shall combine with any other constraints to
override
 their affects and cause access to be precluded."


Third

A similar problem as the second one accurs in the call to

public boolean hasUserDataPermission(HttpRequest request,
                                     HttpResponse response,
                                     SecurityConstraint constraint)
(the method begins at line 627 of the org.apache.catalina.realm.RealmBase
file)

As in the second point, constraints are examined one by one, instead of
determining globally the policy for all
the constraints applying for the same (http-method, url-patern)

Spec :The rules to combine user-data-constraints are given in
servlet-2_4-fr-spec.pdf, ch SRV 12.8.1, p98 :

 "The combination of user-data-constraints that apply to a common urlpattern
 and http-method shall yield the union of connection types accepted by
 the individual constraints as acceptable connection types. A security
constraint
 that does not contain a user-data-constraint shall combine with other
userdata-
 constraints to cause the unprotected connection type to be an accepted
 connection type."


Possible workaround

I've coded a simple workaround (see below), consisting mainly of a class,
MergedConstraintBuilder,
whose job is to build a fake SecurityConstraint for each(requestURI,
httpMethod) couple,
implementing the selection algorithms described in the spec (as I understood
it) :
 1 - select the best matching url-pattern and retain the SecurityCollections
containing this pattern (if any)
 2 - retaining only the constraint of this first set defining a constraint
for the http-method (if any)
 3 - determining the global user-data-contraint (ie transport protocol) for
the resulting set of constraints
 4 - determining the authorized use groups for the set

When the

MergedConstraintBuilder.getMergedConstraintForRequest(SecurityConstraint[]
allConstraints,
                                                      String requestURI,
                                                      String method)

method is called, I use successively these four algorithms to select the
applying SecurityConstraints, and then
I first build a fake org.apache.catalina.deploy.SecurityCollection, with the
following properties :
    - String name       :
    - String[] patterns :  an array of Strings containing only one String,
the request URI
    - String[] methods  :  an array of Strings containing only one String,
the request method

Once the SecurityCollection built, I construct over it a fake
SecurityConstraint with the folowing properties :
    - boolean allRoles                  :  appropriately set by the fourth
algorithm
    - boolean authConstraint            :  appropriately set by the fourth
algorithm
    - String[] roleNames                :  appropriately set by the fourth
algorithm
    - String userConstraint             :  the the global
user-data-contraint as returned by the third algorithm
    - SecurityCollection[] collections  :  an array containing a single
element, the previously determined

fake SecurityCollection

and I return it encapsulated in an array of SecurityConstraints, the goal of
tis encapsulation being to avoid breaking
org.apache.catalina.realm.RealmBase existing code.


In order to put this piece of code to work, we have to have
org.apache.catalina.realm.RealmBase invoke it :
I have so added MergedConstraintBuilder in the org.apache.catalina.realm
package, I've modified the
public SecurityConstraint[] findSecurityConstraints(HttpRequest request,
Context context) method of
RealmBase (see just below), and I also added three short utilities methods
in org.apache.catalina.deploy.SecurityCollection
(these utilities are in charge to retrieve the best matching url-pattern)

I didn't do extensive testing, but the spec examples work (and some more,
too ;))

I don't know if the difference with the spec is a will or not, so I don't
know if this will help

Anyway, it was fun

Philippe (philippe.leothaud@wanadoo.fr)

Note : the fake SecurityConstraints could actually be cached, so that the
computation
is done only once for a (URI, http-method) couple : I've got another version
of the MergedConstraintBuilder providing
an implementation of this strategy.

----------------------------------------------------------------------------
----------------------------------------------------------------------------
--------------------------
Changed SecurityConstraint[] findSecurityConstraints(HttpRequest request,
Context context)
method in org.apache.catalina.realm.RealmBase (starting at line 438)
----------------------------------------------------------------------------
----------------------------------------------------------------------------
--------------------------
    /** l.438 org.apache.catalina.realm.RealmBase
     *
     * Return the SecurityConstraints configured to guard the request URI
for
     * this request, or <code>null</code> if there is no such constraint.
     *
     * @param request Request we are processing
     * @param context Context the Request is mapped to
     */
    public SecurityConstraint[] findSecurityConstraints(HttpRequest request,
Context context) {

        // Are there any defined security constraints?
        SecurityConstraint constraints[] = context.findConstraints();
        if ((constraints == null) || (constraints.length == 0)) {
            if (log.isDebugEnabled())
             log.debug("  No applicable constraints defined");
            return (null);
        }

        HttpServletRequest hreq = (HttpServletRequest) request.getRequest();
        String uri = request.getDecodedRequestURI();
        String contextPath = hreq.getContextPath();
        if (contextPath.length() > 0) uri =
uri.substring(contextPath.length());
        String method = hreq.getMethod();

     MergedConstraintBuilder builder = new MergedConstraintBuilder ();
     return getMergedConstraintForRequest(allConstraints, uri, method);
        // Check each defined security constraint
    }
----------------------------------------------------------------------------
----------------------------------------------------------------------------
--------------------------


 ---------------------------------------------------------------------------
----------------------------------------------------------------------------
---------------------------
 Methods to be added to the org.apache.catalina.deploy.SecurityCollection
class
----------------------------------------------------------------------------
----------------------------------------------------------------------------
--------------------------
  /**
  * Builds and returns the <code>List</code> of all the
<code>SecurityCollection</code>s
  * part of this <code>SecurityConstraint</code> and containing amongst
their
  * <code>url-pattern</code>s at least one pattern matching exactly the
given requestURI.
  *
  * @param requestURI : the URI to match exactly
  * @return           : the <code>List</code> of the
<code>SecurityCollection</code>s
  *                     containing a pattern matching this URI
  */
 public List getExactMatchingWebCollections(String requestURI) {
  List exactMatchingCollections = null;
  for (int i = 0; i < collections.length; i++) {
   String patterns[] = collections[i].getPatterns();
   for (int j = 0; j < patterns.length; j++) {
    if (requestURI.equals(patterns[j])) {
     if(exactMatchingCollections == null) {
      exactMatchingCollections = new ArrayList();
     }
     exactMatchingCollections.add(collections[i]);
     break;
    }
   }
  }
  return exactMatchingWebCollections;
 }

 /**
  * Builds and returns the <code>List</code> of all the
<code>SecurityCollection</code>s
  * part of this <code>SecurityConstraint</code> and containing amongst
their
  * <code>url-pattern</code>s at least one pattern matching exactly the
given URI's bestMatch.
  *
  * @param bestMatch : the URI's bestMatch to match exactly
  * @return          : the <code>List</code> of the
<code>SecurityCollection</code>s
  *                    containing a pattern matching this URI's bestMatch
  */
 public List getMatchingWebCollections(String bestMatch) {
  List matchingCollections = null;
  for (int i = 0; i < collections.length; i++) {
   String patterns[] = collections[i].getPatterns();
   for (int j = 0; j < patterns.length; j++) {
    if (bestMatch.equals(patterns[j])) {
     if(matchingCollections == null) {
      matchingCollections = new ArrayList();
     }
     matchingCollections.add(collections[i]);
     break;
    }
   }
  }
  return matchingCollections;
 }

 /**
  * Gets <code>url-pattern</code> which is the best match to the given URI,
amongst
  * all the <code>SecurityCollection</code>s part of this
<code>SecurityConstraint</code>
  *
  * @param requestURI : the URI's to match best
  * @return           : the <code>String</code> representation of the
<code>url-pattern</code>
  *                     which is best matching the given URI, amongst all
the patterns of all the
  *                     <code>SecurityCollection</code>s of this
<code>SecurityConstraint</code>
  */
 public String getBestMatch(String requestURI) {
  String bestMatch = "";
  for (int i = 0; i < collections.length; i++) {
   String patterns[] = collections[i].getPatterns();
   for (int j = 0; j < patterns.length; j++) {
    if (matchPattern(requestURI, patterns[j])) {
     if(patterns[j].length() > bestMatch.length())
      bestMatch = patterns[j];
    }
   }
  }
  return bestMatch;
 }
----------------------------------------------------------------------------
----------------------------------------------------------------------------
--------------------------



----------------------------------------------------------------------------
----------------------------------------------------------------------------
--------------------------
Class MergedConstraintBuilder, to be added in the org.apache.catalina.realm
package
----------------------------------------------------------------------------
----------------------------------------------------------------------------
--------------------------
package org.apache.catalina.realm;

import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;

import org.apache.log4j.Logger;

 /**
  * Builds a custom <code>SecurityConstraint</code> merging all valid
<code>SecurityConstraint</code>s
  * for the method and URI that will be extracted from the given
<code>HttpServletRequest</code>, or
  * <code>null</code> if there is no such <code>SecurityConstraint</code>.
  *
  * The process to determine which <code>SecurityConstraint</code>s are
valid for a URI and a method
  * is defined in servlet-2_4-fr-spec.pdf, ch SRV 12-8-3, pp 100-101 :
  *  1 - "Select the constraints (if any) defined on the url-pattern that is
the
  *         best match to the request URI. If no constraints are selected,
the container shall
  *         accept the request." (i.e. the custom Constraint is null)
  *  2 - "Determine if the HTTP method of the request is constrained at the
selected pattern.
  *         If it is not, the request shall be accepted." (i.e. the custom
Constraint is null)
  *  3 -  Determine the user-data-constraint, given that : "The
characteristics of the connection
  *         on which the request was received must satisfy at least one of
the supported
  *         connection types defined by the constraints."
  *  4 -  Determine the array of authorized roles, given that : "The
authentication characteristics
  *         of the request must satisfy any authentication and role
requirements
  *         defined by the constraints."
  *
  * After applying this process, the custom <code>SecurityConstraint</code>
is created with the
  * following properties :
  *  - display-name = requestURI + "::" + requestMethod
  *  - SecurityCollection[] collection =
  *          new SecurityCollection[] {
  *              new SecurityCollection(requestURI + "::" + requestMethod,
  *          new String[] { requestURI },
  *          new String[] { requestMethod } ) }
  *  - boolean authConstraint
  *  - boolean allRoles
  *  - String[] authorizedRoles
  */
public class MergedConstraintBuilder {

 static Logger logger =
Logger.getLogger(MergedConstraintBuilder.class.getName());


 /**
  * Special method for integration with Catalina's RealmBase.
  *
  * @param allConstraints : all the <code>SecurityConstraint</code>s defined
in <code>web.xml</code>
  * @param req            : the request of the User
  *
  * @return               : the custom <code>SecurityConstraint</code>,
wrapped in an array
   *                                                   of
<code>SecurityConstraint</code>s
  */
 public SecurityConstraint[] getMergedConstraintForRequest(
     SecurityConstraint[] allConstraints,
     String requestURI,
     String method) {

  return new SecurityConstraint[] {
mergeConstraintsForRequest(allConstraints, requestURI, method) };

 }


 /**
  * Builds and returns a custom <code>SecurityConstraint</code> merging all
valid <code>SecurityConstraint</code>s
  * for the method and URI that will be extracted from the given
<code>HttpServletRequest</code>, or
  * <code>null</code> if there is no such <code>SecurityConstraint</code>.
  *
  * @param allConstraints : all the <code>SecurityConstraint</code>s defined
in <code>web.xml</code>
  * @param req            : the request of the User
  *
  * @return               : the custom <code>SecurityConstraint</code>
  */

 public SecurityConstraint mergeConstraintsForRequest(
     SecurityConstraint[] allConstraints,
     HttpServletRequest req) {

  // On determine l'URI contextuelle et la methode de la requete
  String requestURI = req.getRequestURI();
  String contextPath = req.getContextPath();
  if (contextPath.length() > 0) {
   requestURI = requestURI.substring(contextPath.length());
  }
  String method = req.getMethod();
  return mergeConstraintsForRequest(allConstraints, requestURI, method);

 }


 /**
  * Builds and returns a custom <code>SecurityConstraint</code> merging all
valid
  * <code>SecurityConstraint</code>s for the given method and URI, or
<code>null</code>
  * if there is no such <code>SecurityConstraint</code>
  *
  * @param allConstraints : all the <code>SecurityConstraint</code>s defined
in <code>web.xml</code>
  * @param requestURI     : URI of the User's request
  * @param method         : method of the User's request
  *
  * @return               : the custom <code>SecurityConstraint</code>
  */
 public SecurityConstraint mergeConstraintsForRequest(
     SecurityConstraint[] allConstraints,
     String requestURI,
     String method) {

  Map matchingConstraintsAndWebResources =
      filterConstraintsByURI(allConstraints, requestURI);
  if(matchingConstraintsAndWebResources == null) {
   return null;
  }
  Collection matchingConstraints =
      filterConstraintsByMethod(matchingConstraintsAndWebResources, method);
  if(matchingConstraints == null) {
   return null;
  }
  // getAuthorizedRoles() takes care of setting the boolean allRoles
  // and boolean authConstraint appropriately
  SecurityConstraint mergedSecurityConstraint =
      getAuthorizedRoles(matchingConstraints);
  String userConstraint = getUserConstraint(matchingConstraints);
  SecurityCollection mergedCollection = new SecurityCollection(
      requestURI + "::" + method,
      new String[] { requestURI },
      new String[] { method } );
  mergedSecurityConstraint.setDisplayName(requestURI + "::" + method);
  mergedSecurityConstraint.setSecurityCollection(
      new SecurityCollection[] { mergedCollection });
  mergedSecurityConstraint.setUserConstraint(userConstraint);
  return mergedSecurityConstraint;
 }

 /**
  * Returns a <code>Map</code> containing the
<code>SecurityConstraint</code>s
  * and associated <code>SecurityCollection</code>s valid for the request
URI (that is,
  * all the <code>SecurityConstraint</code>s containing at least a
<code>SecurityCollection</code>
  * containing the <code>url-pattern</code> which is the best-matching
pattern for the given URI,
  * amongst all the <code>url-pattern</code>s defined in the webApp's
<code>web.xml</code>),
  * or null if there is no such <code>SecurityCollection</code> (and
therfore, no such
  * <code>SecurityConstraint</code>.
  *
  * The rules to determine the best-matching pattern for the given URI are
defined in
  * servlet-2_4-fr-spec.pdf, ch SRV 11-1 pp 85-86 :
  *  1 - "The container will try to find an exact match of the path of the
request to the
  *        path of the servlet."
  *  2 - "The container will recursively try to match the longest
path-prefix."
  *  3 - "If the last segment in the URL path contains an extension (e.g.
.jsp), the servlet
  *        container will try to match (...) the extension".
  *
  *
  * @param allConstraints : all the <code>SecurityConstraint</code>s defined
in web.xml
  * @param requestURI     : URI of the User's request
  *
  * @return               : a <code>Map</code> containing the
<code>SecurityConstraint</code>s and
  *                         associated <code>SecurityCollection</code>s
valid for the request URI
  */
 public Map filterConstraintsByURI(SecurityConstraint[] allConstraints,
String requestURI) {
  if(logger.isDebugEnabled())
   logger.debug("MergedConstraintBuilder.filterConstraintsByURI() - "
    + "Checking SecurityConstraints " + allConstraints
    + " against URI " + requestURI);
  // Determining valid constraints, checking the constraints' url-patterns
against the given requestURI
  Map constraintsAndCollections = null;
  boolean exactMatch = false;
  for (int i = 0; i < allConstraints.length; i++) {
   // First choice : "case-exact-match" url-patterns
   List exactMatchingWebCollections =
       allConstraints[i].getExactMatchingWebCollections(requestURI);
   if(logger.isDebugEnabled())
    logger.debug("MergedConstraintBuilder.filterConstraintsByURI() - "
     + "List exactMatchingWebCollections obtained : " +
exactMatchingWebCollections);
   if(exactMatchingWebCollections != null) {
    if(constraintsAndCollections == null) {
     constraintsAndCollections = new HashMap();
    }
    constraintsAndCollections.put(allConstraints[i],
exactMatchingWebCollections);
   }
  }
  if(constraintsAndCollections == null) {
   // Second choice : "pattern-match" url-patterns
   // Determining the best-matching pattern (=the longest amongst matching
patterns)
   // We keep constraints containing at least one WebCollection containing
the pattern
   // and remove the others
   String bestMatch = "";
   for (int i = 0; i < allConstraints.length; i++) {
    String constraintBestMatch = allConstraints[i].getBestMatch(requestURI);
    if(constraintBestMatch.length() > bestMatch.length()) {
     bestMatch = constraintBestMatch;
    }
   }
   if(logger.isDebugEnabled())
    logger.debug("MergedConstraintBuilder.filterConstraintsByURI() - "
     + "bestMatch obtained : " + bestMatch);
   for (int i = 0; i < allConstraints.length; i++) {
    List bestMatchingWebCollections =
allConstraints[i].getMatchingWebCollections(bestMatch);
    if(logger.isDebugEnabled())
     logger.debug("MergedConstraintBuilder.filterConstraintsByURI() - "
     + "partial bestMatchingWebCollections List obtained : " +
bestMatchingWebCollections);
    if(bestMatchingWebCollections != null) {
     if(constraintsAndCollections == null) {
      constraintsAndCollections = new HashMap();
     }
     if(logger.isDebugEnabled())
      logger.debug("MergedConstraintBuilder.filterConstraintsByURI() - "
      + "bestMatchingWebCollections List : " + bestMatchingWebCollections
      + " stored for SecurityConstraint : " + allConstraints[i]);
     constraintsAndCollections.put(allConstraints[i],
bestMatchingWebCollections);
    }
   }
  } else {
   exactMatch = true;
  }
  if(constraintsAndCollections == null) {
   // No matching constraint
   if(logger.isDebugEnabled())
    logger.debug("MergedConstraintBuilder.filterConstraintsByURI() - "
    + "No SecurityConstraint restraining URI " + requestURI
    + " amongst given constraints " + allConstraints);
   return null;
  }
  return constraintsAndCollections;
 }

 /**
  * Returns a <code>Collection</code> containing the
<code>SecurityConstraint</code>s restraining tne
  * use of the given HTTP method, or null if there is no such
<code>SecurityConstraint</code>.
  *
  * A <code>SecurityConstraint</code> is restraining the use of a given HTTP
method if it contains
  * at least a <code>SecurityColection</code> containing this HTTP method
amongst the <code>String</code>s
  * constituting its methods field (corresponding to the
<code>http-method</code> element of the
  * <code>web-resource-collection</code> element in the
<code>web.xml</code>), or if its methods field is
  * <code>null</code> or empty
  *
  * @param constraintsAndCollections : a <code>Map</code> containing the
<code>SecurityConstraint</code>s
  *                                    to analyze, and their associated
<code>SecurityCollection</code>s
  * @param method                    : the HTTP method against which the
<code>SecurityConstraint</code>s
  *                                    must be checked
  *
  * @return                          : a <code>Collection</code> containing
the
  *                                    <code>SecurityConstraint</code>s
valid for the given HTTP method
  */
 public Collection filterConstraintsByMethod(Map constraintsAndCollections,
String method) {
  if(logger.isDebugEnabled())
   logger.debug("MergedConstraintBuilder.filterConstraintsByMethod() - "
   + "Checking Constraints-WebCollection Map " + constraintsAndCollections
   + " against method " + method);
  Set matchingConstraints = constraintsAndCollections.keySet();
  Iterator matchingConstraintsIterator = matchingConstraints.iterator();
  while (matchingConstraintsIterator.hasNext()) {
   SecurityConstraint constraint = (SecurityConstraint)
matchingConstraintsIterator.next();
   List matchingWebCollections = (List)
constraintsAndCollections.get(constraint);
   Iterator matchingWebCollectionsIterator =
matchingWebCollections.iterator();
   boolean methodIsProtected = false;
   // Pour chaque contrainte, il suffit de trouver une seule WebCollection
   while (matchingWebCollectionsIterator.hasNext()) {
    SecurityCollection collection = (SecurityCollection)
matchingWebCollectionsIterator.next();
    String[] constrainedMethods = collection.getMethods();
    if(constrainedMethods == null || constrainedMethods.length == 0) {
     methodIsProtected = true;
     break;
    }
    for (int i = 0; i < constrainedMethods.length; i++) {
     if(method.equals(constrainedMethods[i])) {
      methodIsProtected = true;
      break;
     }
    }
    if(methodIsProtected) {
     break;
    }
   }
   if(!methodIsProtected) {
    matchingConstraintsIterator.remove();
   }
  }
  if(matchingConstraints.size() == 0) {
   System.out.println("MergedConstraintBuilder.filterConstraintsByMethod() -
"
    + "No SecurityConstraint restraining method " + method
    + " in the Constraints-WebCollection Map");
   return null;
  }
  return matchingConstraints;
 }


 /**
  * Combines all the given <code>SecurityConstraint</code>s to determine and
return as a
  * <code>String</code> the applying user-data-constraint
  *
  * The rules to combine user-data-constraints are given in
servlet-2_4-fr-spec.pdf, ch SRV 12.8.1, p98 :
  * "The combination of user-data-constraints that apply to a common
urlpattern
  * and http-method shall yield the union of connection types accepted by
  * the individual constraints as acceptable connection types. A security
constraint
  * that does not contain a user-data-constraint shall combine with other
userdata-
  * constraints to cause the unprotected connection type to be an accepted
  * connection type."
  *
  * @param matchingConstraints : The <code>Collection</code> of
<code>SecurityConstraint</code>s
  *                              to analyze and combine
  *
  * @return                    : A <code>String</code> containing the
applying user-data-constraint
  */
 public String getUserConstraint(Collection matchingConstraints) {
  SecurityConstraint mergedSecurityConstraint = new SecurityConstraint();
  mergedSecurityConstraint.setUserConstraint("INTEGRAL");
  Iterator matchingConstraintsIterator = matchingConstraints.iterator();
  while (matchingConstraintsIterator.hasNext()) {
   SecurityConstraint constraint = (SecurityConstraint)
matchingConstraintsIterator.next();
   String userConstraint = constraint.getUserConstraint();
   if (userConstraint == null || userConstraint.equals("NONE")) {
    mergedSecurityConstraint.setUserConstraint("NONE");
    break;
   } else if (userConstraint.equals("CONFIDENTIAL")) {
    mergedSecurityConstraint.setUserConstraint("CONFIDENTIAL");
   }
  }
  if (logger.isDebugEnabled())
   logger.debug(
       "MergedConstraintBuilder.getUserConstraint() - "
           + "String userConstraint obtained : "
           + mergedSecurityConstraint.getUserConstraint());
  return mergedSecurityConstraint.getUserConstraint();
 }


 /**
  * Combines all the given <code>SecurityConstraint</code>s to determine and
return as an
  * array of <code>String</code>s the set of authorized roles
  *
  * The rules to combine roles are given in servlet-2_4-fr-spec.pdf, ch SRV
12.8.1, pp97-98 :
  * "The combination of authorization constraints that name roles or that
imply
  * roles via the name "*" shall yield the union of the role names in the
individual
  * constraints as permitted roles. A security constraint that does not
contain an
  * authorization constraint shall combine with authorization constraints
that name or
  * imply roles to allow unauthenticated access. The special case of an
authorization
  * constraint that names no roles shall combine with any other constraints
to override
  * their affects and cause access to be precluded."
  *
  * @param matchingConstraints : The <code>Collection</code> of
<code>SecurityConstraint</code>s
  *                              to analyze and combine
  *
  * @return                    : A <code>SecurityConstraint</code>
containing the authorized roles.
  */
 public SecurityConstraint getAuthorizedRoles(Collection
matchingConstraints) {
  SecurityConstraint mergedSecurityConstraint = new SecurityConstraint();
  mergedSecurityConstraint.setAuthConstraint(true);
  String[] groups = new String[0];
  Iterator matchingConstraintsIterator = matchingConstraints.iterator();
  while (matchingConstraintsIterator.hasNext()) {
   SecurityConstraint constraint = (SecurityConstraint)
matchingConstraintsIterator.next();
   if(constraint.getAuthConstraint()) {
    String[] roleNames = constraint.getRoleNames();
    if(roleNames == null || roleNames.length == 0) {
     mergedSecurityConstraint.setAuthConstraint(true);
     mergedSecurityConstraint.setRoleNames(new String[0]);
     break;
    } else {
     if (!mergedSecurityConstraint.getAuthConstraint() ||
mergedSecurityConstraint.getAllRoles()) {
      continue;
     }
     mergedSecurityConstraint.setAuthConstraint(true);
     if(constraint.getAllRoles()) {
      mergedSecurityConstraint.setRoleNames(new String[] { "*" } );
      continue;
     }
     for(int j=0; j<roleNames.length; j++) {
      String roleName = roleNames[j];
      // Check if this role is already registered
      for(int k=0; k<groups.length; k++) {
       if(roleName.equals(groups[k]))
        break;
      }
      // If not, add it to the role array
      String[] newGroups = new String[groups.length + 1];
      System.arraycopy(groups, 0, newGroups, 0, groups.length);
      newGroups[groups.length] = roleName;
      groups = newGroups;
     }
    }
    mergedSecurityConstraint.setRoleNames(groups);
   } else {
    mergedSecurityConstraint.setAuthConstraint(false);
   }
  }
  if (logger.isDebugEnabled())
   logger.debug(
       "MergedConstraintBuilder.getUserConstraint() - "
           + "String[] groups obtained and added to the returned
SecurityConstraint : "
           + mergedSecurityConstraint);
  return mergedSecurityConstraint;

 }

}
----------------------------------------------------------------------------
----------------------------------------------------------------------------
--------------------------