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 ([EMAIL PROTECTED])

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;

 }

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

Reply via email to