switched to thread safe implementation using Jakarta-ORO
authormaxcooper <maxcooper>
Tue, 17 Dec 2002 15:09:55 +0000 (15:09 +0000)
committermaxcooper <maxcooper>
Tue, 17 Dec 2002 15:09:55 +0000 (15:09 +0000)
fixed various pattern matching bugs
redirects are now URL-encoded

src/share/org/securityfilter/filter/MatchableURLPattern.java [deleted file]
src/share/org/securityfilter/filter/MatchableURLPatternFactory.java [deleted file]
src/share/org/securityfilter/filter/SecurityFilter.java
src/share/org/securityfilter/filter/URLPattern.java [new file with mode: 0644]
src/share/org/securityfilter/filter/URLPatternFactory.java [new file with mode: 0644]
src/share/org/securityfilter/filter/URLPatternMatcher.java [new file with mode: 0644]

diff --git a/src/share/org/securityfilter/filter/MatchableURLPattern.java b/src/share/org/securityfilter/filter/MatchableURLPattern.java
deleted file mode 100644 (file)
index 492f25c..0000000
+++ /dev/null
@@ -1,326 +0,0 @@
-/*
- * $Header: /cvsroot/securityfilter/securityfilter/src/share/org/securityfilter/filter/Attic/MatchableURLPattern.java,v 1.7 2002/12/10 05:02:18 maxcooper Exp $
- * $Revision: 1.7 $
- * $Date: 2002/12/10 05:02:18 $
- *
- * ====================================================================
- * The SecurityFilter Software License, Version 1.1
- *
- * (this license is derived and fully compatible with the Apache Software
- * License - see http://www.apache.org/LICENSE.txt)
- *
- * Copyright (c) 2002 SecurityFilter.org. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * 3. The end-user documentation included with the redistribution,
- *    if any, must include the following acknowledgment:
- *       "This product includes software developed by
- *        SecurityFilter.org (http://www.securityfilter.org/)."
- *    Alternately, this acknowledgment may appear in the software itself,
- *    if and wherever such third-party acknowledgments normally appear.
- *
- * 4. The name "SecurityFilter" must not be used to endorse or promote
- *    products derived from this software without prior written permission.
- *    For written permission, please contact license@securityfilter.org .
- *
- * 5. Products derived from this software may not be called "SecurityFilter",
- *    nor may "SecurityFilter" appear in their name, without prior written
- *    permission of SecurityFilter.org.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
- * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- * ====================================================================
- */
-
-package org.securityfilter.filter;
-
-import org.apache.regexp.RE;
-import org.apache.regexp.RECompiler;
-import org.apache.regexp.RESyntaxException;
-import org.securityfilter.config.SecurityConstraint;
-import org.securityfilter.config.WebResourceCollection;
-
-import java.util.Collection;
-
-/**
- * MatchableURLPattern - Contains matchable URL pattern and the associated SecurityConstraint and WebResourceCollection
- * objects for the pattern. Also supports sorting according to the Servlet Spec v2.3.
- *
- * @author Max Cooper (max@maxcooper.com)
- * @version $Revision: 1.7 $ $Date: 2002/12/10 05:02:18 $
- */
-public class MatchableURLPattern implements Comparable {
-   private String pattern;
-   private RE patternRE;
-   private SecurityConstraint constraint;
-   private WebResourceCollection resourceCollection;
-   private int order;
-   private int patternType;
-   private int pathLength;
-
-   /**
-    * Construct a new MatchableURLPattern object.
-    *
-    * @param pattern the url pattern to match
-    * @param constraint the SecurityConstraint associated with this pattern
-    * @param resourceCollection the WebResourceCollection associated with this pattern
-    * @param order the order in which this pattern occurred in the configuration file
-    * @param compiler an RECompiler to use to compile this url pattern
-    *
-    * @see MatchableURLPatternFactory
-    */
-   public MatchableURLPattern(
-      String pattern,
-      SecurityConstraint constraint,
-      WebResourceCollection resourceCollection,
-      int order,
-      RECompiler compiler
-   ) throws RESyntaxException {
-      this.pattern = pattern;
-      this.constraint = constraint;
-      this.resourceCollection = resourceCollection;
-      this.order = order;
-      init(compiler);
-   }
-
-   /**
-    * Return true if this pattern matches the passed URL.
-    *
-    * @param URL the URL to attempt to match
-    * @param httpMethod the http method to attempt to match
-    * @return true if this pattern matches the URL, false otherwise
-    */
-   public boolean match(String URL, String httpMethod) {
-      if (patternRE.match(URL)) {
-         Collection methods = resourceCollection.getHttpMethods();
-         if (methods.isEmpty() || methods.contains(httpMethod.toUpperCase())) {
-            return true;
-         }
-      }
-      return false;
-   }
-
-   /**
-    * Pattern type for patterns that do not meet the PATH or EXTENSION pattern type specifications.
-    */
-   public static final int EXACT = 1;
-   /**
-    * Pattern type for PATH mappings. Starts with '/' and ends with '/*'.
-    */
-   public static final int PATH = 2;
-   /**
-    * Pattern type for PATH mappings
-    */
-   public static final int EXTENSION = 3;
-
-   /**
-    * Get the pattern type. The pattern type will be determined on the first call to this method.
-    *
-    * @return EXACT, PATH, or EXTENSION
-    */
-   public int getPatternType() {
-      return patternType;
-   }
-
-   /**
-    * Get the url pattern to match.
-    */
-   public String getPattern() {
-      return pattern;
-   }
-
-   /**
-    * Get the SecurityConstraint object associated with this pattern.
-    */
-   public SecurityConstraint getSecurityConstraint() {
-      return constraint;
-   }
-
-   /**
-    * Get the order value for this pattern (the order in which it appeared in the config file).
-    */
-   public int getOrder() {
-      return order;
-   }
-
-   /**
-    * Get the WebResourceCollection associated with this pattern.
-    */
-   public WebResourceCollection getWebResourceCollection() {
-      return resourceCollection;
-   }
-
-   /**
-    * Test if this pattern is equivalent to another pattern.
-    * This is implemented so that consistency with the compareTo method results can be maintained.
-    *
-    * @param obj the value to test equivalence with
-    * @return true if the passed object is an equivalent MatchableURLPattern, false if it is not a MatchableURLPattern
-    * or if it is not equivalent.
-    */
-   public boolean equals(Object obj) {
-      if (obj instanceof MatchableURLPattern) {
-         MatchableURLPattern otherPattern = (MatchableURLPattern) obj;
-         return (
-            constraint.equals(otherPattern.getSecurityConstraint())
-            && resourceCollection.equals(otherPattern.getWebResourceCollection())
-            && pattern.equals(otherPattern.getPattern())
-         );
-      }
-      return false;
-   }
-
-   /**
-    * Compares this MatchableURLPattern to another to support sorting.<p>
-    *
-    * The sort order is dictated by the servlet spec. EXACT patterns are first, followed by PATH patterns,
-    * followed by EXTENTION patterns. Ordering among PATH patterns is determined by path length, with the
-    * longer path coming first. If the path lengths are the same, or both are EXACT or EXTENSION patterns,
-    * ordering is determined by the order in which the pattern appeared in the config file.
-    *
-    * @param another another MatchableURLPattern to compare to
-    *
-    * @return a negative integer, zero, or a positive integer as this object is less than, equal to, or greater
-    * than the specified object.
-    *
-    * @exception ClassCastException thrown if o is not a MatchableURLPattern instance
-    */
-   public int compareTo(Object another) throws ClassCastException {
-      MatchableURLPattern otherPattern = (MatchableURLPattern) another;
-      // if the patterns are equivalent, ordering priority is equal
-      if (this.equals(otherPattern)) {
-         return 0;
-      } else {
-         int otherPatternType = otherPattern.getPatternType();
-         // if the pattern types are the same
-         if (patternType == otherPatternType) {
-            // if the type is PATH
-            if (patternType == MatchableURLPattern.PATH) {
-               int otherPathLength = otherPattern.getPathLength();
-               // if path lengths are different, the pattern with longer path length should be first
-               if (pathLength != otherPathLength) {
-                  return (otherPathLength - pathLength);
-               // path length are the same, the pattern with the smaller order should be first
-               } else {
-                  return (order - otherPattern.getOrder());
-               }
-            // for EXACT or EXTENSION, the pattern with the smaller order should be first
-            } else {
-               return (order - otherPattern.getOrder());
-            }
-         } else {
-            // pattern types are not the same, order should be EXACT, PATH, EXTENSION
-            return (patternType - otherPatternType);
-         }
-      }
-   }
-
-   /**
-    * Get the path length of the pattern. This is only valid when getPatternType() = PATH.<p>
-    * Examples:
-    * <ul>
-    *    <li>/* = 0</li>
-    *    <li>/path/* = 1</li>
-    *    <li>/really/long/path/* = 3</li>
-    * </ul>
-    *
-    * @return path length of this pattern
-    */
-   public int getPathLength() {
-      return pathLength;
-   }
-
-   /**
-    * Initialize pattern-related internal variables.
-    *
-    * @param compiler an RECompiler
-    *
-    * @exception RESyntaxException
-    */
-   private void init(RECompiler compiler) throws RESyntaxException {
-      // calculate the path length
-      pathLength = -1;
-      int pos = pattern.indexOf('/');
-      while (pos != -1) {
-         pathLength++;
-         pos = pattern.indexOf('/', pos + 1);
-      }
-      // determine the pattern type
-      if (pattern.startsWith("*.")) {
-         patternType = MatchableURLPattern.EXTENSION;
-      } else if (pattern.startsWith("/") && pattern.endsWith("/*")) {
-         patternType = MatchableURLPattern.PATH;
-      } else {
-         patternType = MatchableURLPattern.EXACT;
-      }
-      // initilize the patternRE
-      if (compiler == null) {
-         compiler = new RECompiler();
-      }
-      patternRE = new RE(compiler.compile(getConvertedPattern()));
-   }
-
-   /**
-    * Return the pattern string in RE syntax form.
-    *
-    * Would the jakarta-oro be more suited for matching than jakarta-regexp?
-    */
-   private String getConvertedPattern() {
-      StringBuffer buf = new StringBuffer(pattern);
-      int pos;
-      // escape '.' characters
-      pos = buf.toString().indexOf('.');
-      while (pos != -1) {
-         buf.insert(pos, "\\");
-         pos = buf.toString().indexOf('.', pos + 2);
-      }
-      // replace '*' chars in the pattern with '.*'
-      pos = buf.toString().indexOf('*');
-      while (pos != -1) {
-         buf.replace(pos, pos + 1, ".*");
-         pos = buf.toString().indexOf('*', pos + 2);
-      }
-      // adjustments for the different expression types
-      switch (patternType) {
-         case PATH:
-            // make sure it matches from the start of the string
-            buf.insert(0, '^');
-            // make sure /foo/* matches /foo and /foo/morestuff, but not /foobar
-            buf.insert(buf.length()-3, "($|");
-            buf.append(")");
-            break;
-         case EXTENSION:
-            buf.append('$');
-            break;
-         case EXACT:
-            buf.insert(0, '^');
-            buf.append('$');
-            break;
-      }
-      return buf.toString();
-   }
-}
-
-// ----------------------------------------------------------------------------
-// EOF
diff --git a/src/share/org/securityfilter/filter/MatchableURLPatternFactory.java b/src/share/org/securityfilter/filter/MatchableURLPatternFactory.java
deleted file mode 100644 (file)
index 51e8f99..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * $Header: /cvsroot/securityfilter/securityfilter/src/share/org/securityfilter/filter/Attic/MatchableURLPatternFactory.java,v 1.4 2002/12/09 10:17:12 maxcooper Exp $
- * $Revision: 1.4 $
- * $Date: 2002/12/09 10:17:12 $
- *
- * ====================================================================
- * The SecurityFilter Software License, Version 1.1
- *
- * (this license is derived and fully compatible with the Apache Software
- * License - see http://www.apache.org/LICENSE.txt)
- *
- * Copyright (c) 2002 SecurityFilter.org. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * 3. The end-user documentation included with the redistribution,
- *    if any, must include the following acknowledgment:
- *       "This product includes software developed by
- *        SecurityFilter.org (http://www.securityfilter.org/)."
- *    Alternately, this acknowledgment may appear in the software itself,
- *    if and wherever such third-party acknowledgments normally appear.
- *
- * 4. The name "SecurityFilter" must not be used to endorse or promote
- *    products derived from this software without prior written permission.
- *    For written permission, please contact license@securityfilter.org .
- *
- * 5. Products derived from this software may not be called "SecurityFilter",
- *    nor may "SecurityFilter" appear in their name, without prior written
- *    permission of SecurityFilter.org.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
- * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- * ====================================================================
- */
-
-package org.securityfilter.filter;
-
-import org.apache.regexp.RECompiler;
-import org.apache.regexp.RESyntaxException;
-import org.securityfilter.config.SecurityConstraint;
-import org.securityfilter.config.WebResourceCollection;
-
-/**
- * MatchableURLPatternFactory creates MatchableURLPattern instances. It keeps an RECompiler to use for the creation
- * of a set of instances.
- *
- * @author Max Cooper (max@maxcooper.com)
- * @version $Revision: 1.4 $ $Date: 2002/12/09 10:17:12 $
- */
-public class MatchableURLPatternFactory {
-   private RECompiler compiler;
-
-   /**
-    * Constructor
-    */
-   public MatchableURLPatternFactory() {
-      compiler = new RECompiler();
-   }
-
-   /**
-    * Create a MatchableURLPattern instance.
-    *
-    * @param pattern url pattern in config file syntax
-    * @param constraint SecurityConstraint object to associate with this pattern
-    * @param resourceCollection WebResourceCollection to associate with this pattern
-    * @param order order in which this pattern appeared in the config file
-    *
-    * @exception RESyntaxException throws exception if pattern cannot be compiled after conversion to RE syntax
-    */
-   public MatchableURLPattern createMatchableURLPattern(
-      String pattern,
-      SecurityConstraint constraint,
-      WebResourceCollection resourceCollection,
-      int order
-   ) throws RESyntaxException {
-      return new MatchableURLPattern(pattern, constraint, resourceCollection, order, compiler);
-   }
-}
-
-// ----------------------------------------------------------------------------
-// EOF
index 8aa21a5..fb8b2a4 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * $Header: /cvsroot/securityfilter/securityfilter/src/share/org/securityfilter/filter/SecurityFilter.java,v 1.9 2002/12/09 10:17:12 maxcooper Exp $
- * $Revision: 1.9 $
- * $Date: 2002/12/09 10:17:12 $
+ * $Header: /cvsroot/securityfilter/securityfilter/src/share/org/securityfilter/filter/SecurityFilter.java,v 1.10 2002/12/17 15:10:00 maxcooper Exp $
+ * $Revision: 1.10 $
+ * $Date: 2002/12/17 15:10:00 $
  *
  * ====================================================================
  * The SecurityFilter Software License, Version 1.1
@@ -55,9 +55,6 @@
 
 package org.securityfilter.filter;
 
-import org.apache.regexp.RE;
-import org.apache.regexp.RECompiler;
-import org.apache.regexp.RESyntaxException;
 import org.securityfilter.config.AuthConstraint;
 import org.securityfilter.config.SecurityConfig;
 import org.securityfilter.config.SecurityConstraint;
@@ -78,7 +75,7 @@ import java.util.*;
  *
  * @author Max Cooper (max@maxcooper.com)
  * @author Torgeir Veimo (torgeir@pobox.com)
- * @version $Revision: 1.9 $ $Date: 2002/12/09 10:17:12 $
+ * @version $Revision: 1.10 $ $Date: 2002/12/17 15:10:00 $
  */
 public class SecurityFilter implements Filter {
    public static final String SAVED_REQUEST_URL = SecurityFilter.class.getName() + ".SAVED_REQUEST_URL";
@@ -90,17 +87,18 @@ public class SecurityFilter implements Filter {
    public static final String VALIDATE_KEY = "validate";
    public static final String TRUE = "true";
 
-   private FilterConfig config;
-   private SecurityRealmInterface realm;
-   private String loginPage;
-   private String errorPage;
-   private String defaultPage;
-   private RE loginSubmitRE;
-   private List patternList;
+   protected FilterConfig config;
+   protected SecurityRealmInterface realm;
+   protected String loginPage;
+   protected String errorPage;
+   protected String defaultPage;
+   protected URLPatternFactory patternFactory;
+   protected URLPattern loginSubmitPattern;
+   protected List patternList;
 
-   private static final String FORM_USERNAME = "j_username";
-   private static final String FORM_PASSWORD = "j_password";
-   private static final String FORM_SUBMIT_PATTERN = ".*/j_security_check";
+   protected static final String FORM_USERNAME = "j_username";
+   protected static final String FORM_PASSWORD = "j_password";
+   protected static final String FORM_SUBMIT_PATTERN = "*/j_security_check";
 
    /**
     * Perform filtering operation, and optionally pass the request down the chain.
@@ -126,6 +124,9 @@ public class SecurityFilter implements Filter {
          // set an attribute on this request to indicate that it has already been processed
          request.setAttribute(ALREADY_PROCESSED, "true");
 
+         // get a URLPatternMatcher to use for this thread
+         URLPatternMatcher patternMatcher = patternFactory.createURLPatternMatcher();
+
          // get the part of the URL to check for matches
          String requestURL = getMatchableURL(hReq);
 
@@ -135,14 +136,19 @@ public class SecurityFilter implements Filter {
          // wrap request
          wrappedRequest = new SecurityRequestWrapper(hReq, realm, savedRequest);
 
-         // check if this is a login form submittal
-         if (loginSubmitRE.match(requestURL)) {
-            processLogin(wrappedRequest, hRes);
-            return;
-         }
+         URLPattern match = null;
+         try {
+            // check if this is a login form submittal
+            if (patternMatcher.match(requestURL, loginSubmitPattern)) {
+               processLogin(wrappedRequest, hRes);
+               return;
+            }
 
-         // check if request matches security constraint
-         MatchableURLPattern match = matchPattern(requestURL, wrappedRequest.getMethod());
+            // check if request matches security constraint
+            match = matchPattern(requestURL, wrappedRequest.getMethod(), patternMatcher);
+         } catch (Exception e) {
+            throw new ServletException("Error matching patterns", e);
+         }
 
          // check security constraint, if any
          if (match != null) {
@@ -189,11 +195,17 @@ public class SecurityFilter implements Filter {
     *
     * @param config filter configuration object
     */
-   public void init(FilterConfig config) {
+   public void init(FilterConfig config) throws ServletException {
       this.config = config;
       try {
+         patternFactory = new URLPatternFactory();
+
          // login submit RE
-         loginSubmitRE = new RE(new RECompiler().compile(FORM_SUBMIT_PATTERN));
+         try {
+            loginSubmitPattern = patternFactory.createURLPattern(FORM_SUBMIT_PATTERN, null, null, 0);
+         } catch (Exception e) {
+            throw new ServletException("Error creating login submit pattern", e);
+         }
 
          // parse config file
          String configFile = config.getInitParameter(CONFIG_FILE_KEY);
@@ -213,7 +225,6 @@ public class SecurityFilter implements Filter {
 
          // create pattern list
          patternList = new ArrayList();
-         MatchableURLPatternFactory patternFactory = new MatchableURLPatternFactory();
          int order = 1;
          List constraints = securityConfig.getSecurityConstraints();
          for (Iterator cIter = constraints.iterator(); cIter.hasNext();) {
@@ -221,7 +232,7 @@ public class SecurityFilter implements Filter {
             for (Iterator rIter = constraint.getWebResourceCollections().iterator(); rIter.hasNext();) {
                WebResourceCollection resourceCollection = (WebResourceCollection) rIter.next();
                for (Iterator pIter = resourceCollection.getURLPatterns().iterator(); pIter.hasNext();) {
-                  MatchableURLPattern pattern = patternFactory.createMatchableURLPattern(
+                  URLPattern pattern = patternFactory.createURLPattern(
                      (String) pIter.next(),
                      constraint,
                      resourceCollection,
@@ -233,12 +244,12 @@ public class SecurityFilter implements Filter {
          }
          Collections.sort(patternList);
 
-      } catch (RESyntaxException rese) {
-         System.err.println("invalid regular expression pattern: " + rese);
       } catch (java.io.IOException ioe) {
          System.err.println("unable to parse input: " + ioe);
       } catch (org.xml.sax.SAXException se) {
          System.err.println("unable to parse input: " + se);
+      } catch (Exception e) {
+         System.err.println("invalid regular expression pattern: " + e);
       }
    }
 
@@ -253,7 +264,7 @@ public class SecurityFilter implements Filter {
     *
     * @param config filter configuration object
     */
-   public void setFilterConfig(FilterConfig config) {
+   public void setFilterConfig(FilterConfig config) throws ServletException {
       init(config);
    }
 
@@ -265,19 +276,20 @@ public class SecurityFilter implements Filter {
    }
 
    /**
-    * Find a match for the requested URL & method, if any.
+    * Find a match for the requested pattern & method, if any.
     *
-    * @param URL the URL to match
+    * @param pattern the pattern to match
     * @param httpMethod the HTTP Method to match
-    * @return the matching MatchableURLPattern object, or null if there is no match.
+    * @param matcher the thread-local URLPatternMatcher object
+    * @return the matching URLPattern object, or null if there is no match.
     */
-   protected MatchableURLPattern matchPattern(String URL, String httpMethod) {
-      // PERFORMANCE IMPROVEMENT OPPORTUNITY: cahce URL pattern matches
+   protected URLPattern matchPattern(String pattern, String httpMethod, URLPatternMatcher matcher) throws Exception {
+      // PERFORMANCE IMPROVEMENT OPPORTUNITY: cahce pattern matches
       Iterator i = patternList.iterator();
       while (i.hasNext()) {
-         MatchableURLPattern pattern = (MatchableURLPattern) i.next();
-         if (pattern.match(URL, httpMethod)) {
-            return pattern;
+         URLPattern urlPattern = (URLPattern) i.next();
+         if (matcher.match(pattern, httpMethod, urlPattern)) {
+            return urlPattern;
          }
       }
       return null;
@@ -298,7 +310,7 @@ public class SecurityFilter implements Filter {
       // save this request
       saveRequestInformation(request);
       // redirect to login page
-      response.sendRedirect(request.getContextPath() + loginPage);
+      response.sendRedirect(response.encodeRedirectURL(request.getContextPath() + loginPage));
    }
 
    /**
@@ -320,7 +332,7 @@ public class SecurityFilter implements Filter {
          // login successful
          request.setUserPrincipal(principal);
          String continueToURL = getContinueToURL(request);
-         response.sendRedirect(continueToURL);
+         response.sendRedirect(response.encodeRedirectURL(continueToURL));
       } else {
          // login failed, set response status and forward to error page
          response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
diff --git a/src/share/org/securityfilter/filter/URLPattern.java b/src/share/org/securityfilter/filter/URLPattern.java
new file mode 100644 (file)
index 0000000..940c1da
--- /dev/null
@@ -0,0 +1,333 @@
+/*
+ * $Header: /cvsroot/securityfilter/securityfilter/src/share/org/securityfilter/filter/URLPattern.java,v 1.1 2002/12/17 15:10:00 maxcooper Exp $
+ * $Revision: 1.1 $
+ * $Date: 2002/12/17 15:10:00 $
+ *
+ * ====================================================================
+ * The SecurityFilter Software License, Version 1.1
+ *
+ * (this license is derived and fully compatible with the Apache Software
+ * License - see http://www.apache.org/LICENSE.txt)
+ *
+ * Copyright (c) 2002 SecurityFilter.org. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ *    if any, must include the following acknowledgment:
+ *       "This product includes software developed by
+ *        SecurityFilter.org (http://www.securityfilter.org/)."
+ *    Alternately, this acknowledgment may appear in the software itself,
+ *    if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The name "SecurityFilter" must not be used to endorse or promote
+ *    products derived from this software without prior written permission.
+ *    For written permission, please contact license@securityfilter.org .
+ *
+ * 5. Products derived from this software may not be called "SecurityFilter",
+ *    nor may "SecurityFilter" appear in their name, without prior written
+ *    permission of SecurityFilter.org.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ */
+
+package org.securityfilter.filter;
+
+import org.apache.oro.text.regex.Pattern;
+import org.apache.oro.text.regex.PatternCompiler;
+import org.apache.oro.text.regex.Perl5Compiler;
+import org.securityfilter.config.SecurityConstraint;
+import org.securityfilter.config.WebResourceCollection;
+
+/**
+ * URLPattern - Contains matchable URL pattern and the associated
+ * SecurityConstraint and WebResourceCollection objects for the pattern.
+ * Also supports sorting according to the Servlet Spec v2.3.
+ *
+ * @author Max Cooper (max@maxcooper.com)
+ * @version $Revision: 1.1 $ $Date: 2002/12/17 15:10:00 $
+ */
+public class URLPattern implements Comparable {
+   /**
+    * Pattern type for PATH_TYPE mappings. Starts with '/' and ends with '/*'.
+    */
+   public static final int PATH_TYPE = 1;
+   /**
+    * Pattern type for EXTENSION_TYPE mappings. Starts with *.
+    */
+   public static final int EXTENSION_TYPE = 2;
+   /**
+    * Pattern type for patterns that do not meet the PATH_TYPE
+    * or EXTENSION_TYPE pattern type specifications.
+    */
+   public static final int EXACT_TYPE = 3;
+
+   protected String pattern;
+   protected String convertedPattern;
+   protected Pattern compiledPattern;
+   protected SecurityConstraint constraint;
+   protected WebResourceCollection resourceCollection;
+   protected int order;
+   protected int patternType;
+   protected int pathLength;
+
+   /**
+    * Construct a new URLPattern object.
+    *
+    * @param pattern the url pattern to match
+    * @param constraint the SecurityConstraint associated with this pattern
+    * @param resourceCollection the WebResourceCollection associated with this pattern
+    * @param order the order in which this pattern occurred in the configuration file
+    * @param compiler a PatternCompiler to use to compile this url pattern
+    *
+    * @see URLPatternFactory
+    */
+   public URLPattern(
+      String pattern,
+      SecurityConstraint constraint,
+      WebResourceCollection resourceCollection,
+      int order,
+      PatternCompiler compiler
+   ) throws Exception {
+      this.pattern = pattern;
+      this.constraint = constraint;
+      this.resourceCollection = resourceCollection;
+      this.order = order;
+      initPatternType();
+      initPathLength();
+      initConvertedPattern();
+      initCompiledPattern(compiler);
+   }
+
+   /**
+    * Get the url pattern to match.
+    */
+   public String getPattern() {
+      return pattern;
+   }
+
+   /**
+    * Get the compiled version of this pattern.
+    *
+    * @return compiled version of this pattern
+    */
+   public Pattern getCompiledPattern() {
+      return compiledPattern;
+   }
+
+   /**
+    * Get the pattern type. The pattern type will be determined on the first call to this method.
+    *
+    * @return EXACT, PATH, or EXTENSION
+    */
+   public int getPatternType() {
+      return patternType;
+   }
+
+   /**
+    * Get the path length of the pattern. This is only valid when getPatternType() = PATH.<p>
+    * Examples:
+    * <ul>
+    *    <li>/* = 0</li>
+    *    <li>/path/* = 1</li>
+    *    <li>/really/long/path/* = 3</li>
+    * </ul>
+    *
+    * @return path length of this pattern
+    */
+   public int getPathLength() {
+      return pathLength;
+   }
+
+   /**
+    * Get the SecurityConstraint object associated with this pattern.
+    */
+   public SecurityConstraint getSecurityConstraint() {
+      return constraint;
+   }
+
+   /**
+    * Get the order value for this pattern (the order in which it appeared in the config file).
+    */
+   public int getOrder() {
+      return order;
+   }
+
+   /**
+    * Get the WebResourceCollection associated with this pattern.
+    */
+   public WebResourceCollection getWebResourceCollection() {
+      return resourceCollection;
+   }
+
+   /**
+    * Initialize the patternType protected member.
+    */
+   protected void initPatternType() {
+      if (pattern.startsWith("*.")) {
+         patternType = URLPattern.EXTENSION_TYPE;
+      } else if (pattern.startsWith("/") && pattern.endsWith("/*")) {
+         patternType = URLPattern.PATH_TYPE;
+      } else {
+         patternType = URLPattern.EXACT_TYPE;
+      }
+   }
+
+   /**
+    * Initialize the pathLength protected member.
+    */
+   protected void initPathLength() {
+      pathLength = -1;
+      int pos = pattern.indexOf('/');
+      while (pos != -1) {
+         pathLength++;
+         pos = pattern.indexOf('/', pos + 1);
+      }
+   }
+
+   /**
+    * Initialize the convertedPattern protected member.
+    */
+   protected void initConvertedPattern() {
+      StringBuffer buf = new StringBuffer(pattern);
+      int pos;
+      // escape '.' characters
+      pos = buf.toString().indexOf('.');
+      while (pos != -1) {
+         buf.insert(pos, "\\");
+         pos = buf.toString().indexOf('.', pos + 2);
+      }
+      // replace '*' chars in the compiledPattern with '.*'
+      pos = buf.toString().indexOf('*');
+      while (pos != -1) {
+         buf.replace(pos, pos + 1, ".*");
+         pos = buf.toString().indexOf('*', pos + 2);
+      }
+      // replace '/' chars with '/+' to match one or more consecutive slashes
+      pos = buf.toString().indexOf('/');
+      while (pos != -1) {
+         buf.replace(pos, pos + 1, "/+");
+         pos = buf.toString().indexOf('/', pos + 2);
+      }
+      // adjustments for the different expression types
+      switch (patternType) {
+         case PATH_TYPE:
+            // make sure it matches from the start of the string
+            buf.insert(0, '^');
+            // make sure /foo/* matches /foo and /foo/morestuff, but not /foobar
+            buf.insert(buf.length()-4, "(");
+            buf.append(")?$");
+            break;
+         case EXTENSION_TYPE:
+            buf.append('$');
+            break;
+         case EXACT_TYPE:
+            buf.insert(0, '^');
+            buf.append('$');
+            break;
+      }
+      convertedPattern = buf.toString();
+   }
+
+   /**
+    * Initialize the compiledPattern protected member.
+    *
+    * @param compiler
+    * @throws Exception
+    */
+   protected void initCompiledPattern(PatternCompiler compiler) throws Exception {
+      compiledPattern = compiler.compile(convertedPattern, Perl5Compiler.READ_ONLY_MASK);
+   }
+
+   /**
+    * Test if this pattern is equivalent to another pattern.
+    * This is implemented so that consistency with the compareTo method results can be maintained.
+    *
+    * @param obj the value to test equivalence with
+    * @return true if the passed object is an equivalent URLPattern, false if it is not a URLPattern
+    * or if it is not equivalent.
+    */
+   public boolean equals(Object obj) {
+      if (obj instanceof URLPattern) {
+         URLPattern otherPattern = (URLPattern) obj;
+         return (
+            constraint.equals(otherPattern.getSecurityConstraint())
+            && resourceCollection.equals(otherPattern.getWebResourceCollection())
+            && pattern.equals(otherPattern.getPattern())
+         );
+      }
+      return false;
+   }
+
+   /**
+    * Compares this URLPattern to another to support sorting.<p>
+    *
+    * The sort order is dictated by the servlet spec. EXACT_TYPE patterns are first,
+    * followed by PATH_TYPE patterns, followed by EXTENTION_TYPE patterns. Ordering
+    * among PATH_TYPE patterns is determined by path length, with the longer path
+    * coming first. If the path lengths are the same, or both are EXACT_TYPE or
+    * EXTENSION_TYPE patterns, ordering is determined by the order in which the
+    * pattern appeared in the config file.
+    *
+    * @param another another URLPattern to compare to
+    *
+    * @return a negative integer, zero, or a positive integer as this object is
+    * less than, equal to, or greater than the specified object.
+    *
+    * @exception ClassCastException thrown if o is not a URLPattern instance
+    */
+   public int compareTo(Object another) throws ClassCastException {
+      URLPattern otherPattern = (URLPattern) another;
+      // if the patterns are equivalent, ordering priority is equal
+      if (this.equals(otherPattern)) {
+         return 0;
+      } else {
+         int otherPatternType = otherPattern.getPatternType();
+         // if the compiledPattern types are the same
+         if (patternType == otherPatternType) {
+            // if the type is PATH_TYPE
+            if (patternType == URLPattern.PATH_TYPE) {
+               int otherPathLength = otherPattern.getPathLength();
+               // if path lengths are different, the compiledPattern with longer path length should be first
+               if (pathLength != otherPathLength) {
+                  return (otherPathLength - pathLength);
+               // path length are the same, the compiledPattern with the smaller order should be first
+               } else {
+                  return (order - otherPattern.getOrder());
+               }
+            // for EXACT_TYPE or EXTENSION_TYPE, the compiledPattern with the smaller order should be first
+            } else {
+               return (order - otherPattern.getOrder());
+            }
+         } else {
+            // compiledPattern types are not the same, order should be EXACT_TYPE, PATH_TYPE, EXTENSION_TYPE
+            return (patternType - otherPatternType);
+         }
+      }
+   }
+}
+
+// ----------------------------------------------------------------------------
+// EOF
diff --git a/src/share/org/securityfilter/filter/URLPatternFactory.java b/src/share/org/securityfilter/filter/URLPatternFactory.java
new file mode 100644 (file)
index 0000000..d68e5ee
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * $Header: /cvsroot/securityfilter/securityfilter/src/share/org/securityfilter/filter/URLPatternFactory.java,v 1.1 2002/12/17 15:10:01 maxcooper Exp $
+ * $Revision: 1.1 $
+ * $Date: 2002/12/17 15:10:01 $
+ *
+ * ====================================================================
+ * The SecurityFilter Software License, Version 1.1
+ *
+ * (this license is derived and fully compatible with the Apache Software
+ * License - see http://www.apache.org/LICENSE.txt)
+ *
+ * Copyright (c) 2002 SecurityFilter.org. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ *    if any, must include the following acknowledgment:
+ *       "This product includes software developed by
+ *        SecurityFilter.org (http://www.securityfilter.org/)."
+ *    Alternately, this acknowledgment may appear in the software itself,
+ *    if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The name "SecurityFilter" must not be used to endorse or promote
+ *    products derived from this software without prior written permission.
+ *    For written permission, please contact license@securityfilter.org .
+ *
+ * 5. Products derived from this software may not be called "SecurityFilter",
+ *    nor may "SecurityFilter" appear in their name, without prior written
+ *    permission of SecurityFilter.org.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ */
+
+package org.securityfilter.filter;
+
+import org.apache.oro.text.regex.PatternCompiler;
+import org.apache.oro.text.regex.Perl5Compiler;
+import org.securityfilter.config.SecurityConstraint;
+import org.securityfilter.config.WebResourceCollection;
+
+/**
+ * URLPatternFactory creates URLPattern instances. It keeps a Perl5PatternCompiler to use
+ * for the creation of a set of instances.
+ *
+ * @author Max Cooper (max@maxcooper.com)
+ * @version $Revision: 1.1 $ $Date: 2002/12/17 15:10:01 $
+ */
+public class URLPatternFactory {
+   protected PatternCompiler compiler;
+
+   /**
+    * Constructor
+    */
+   public URLPatternFactory() {
+      compiler = new Perl5Compiler();
+   }
+
+   /**
+    * Create a URLPatternMatcher object that is compatible with the URLPattern
+    * objects created by this Facotry class.
+    *
+    * @return a URLPatternMatcher object compatible with the URLPatterns created by this class
+    */
+   public URLPatternMatcher createURLPatternMatcher() {
+      return new URLPatternMatcher();
+   }
+
+   /**
+    * Create a URLPattern instance.
+    *
+    * @param pattern url pattern in config file syntax
+    * @param constraint SecurityConstraint object to associate with this pattern
+    * @param resourceCollection WebResourceCollection to associate with this pattern
+    * @param order order in which this pattern appeared in the config file
+    *
+    * @exception Exception
+    */
+   public URLPattern createURLPattern(
+      String pattern,
+      SecurityConstraint constraint,
+      WebResourceCollection resourceCollection,
+      int order
+   ) throws Exception {
+      return new URLPattern(pattern, constraint, resourceCollection, order, compiler);
+   }
+}
+
+// ----------------------------------------------------------------------------
+// EOF
diff --git a/src/share/org/securityfilter/filter/URLPatternMatcher.java b/src/share/org/securityfilter/filter/URLPatternMatcher.java
new file mode 100644 (file)
index 0000000..c5307a9
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * $Header: /cvsroot/securityfilter/securityfilter/src/share/org/securityfilter/filter/URLPatternMatcher.java,v 1.1 2002/12/17 15:10:01 maxcooper Exp $
+ * $Revision: 1.1 $
+ * $Date: 2002/12/17 15:10:01 $
+ *
+ * ====================================================================
+ * The SecurityFilter Software License, Version 1.1
+ *
+ * (this license is derived and fully compatible with the Apache Software
+ * License - see http://www.apache.org/LICENSE.txt)
+ *
+ * Copyright (c) 2002 SecurityFilter.org. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ *    if any, must include the following acknowledgment:
+ *       "This product includes software developed by
+ *        SecurityFilter.org (http://www.securityfilter.org/)."
+ *    Alternately, this acknowledgment may appear in the software itself,
+ *    if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The name "SecurityFilter" must not be used to endorse or promote
+ *    products derived from this software without prior written permission.
+ *    For written permission, please contact license@securityfilter.org .
+ *
+ * 5. Products derived from this software may not be called "SecurityFilter",
+ *    nor may "SecurityFilter" appear in their name, without prior written
+ *    permission of SecurityFilter.org.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ */
+
+package org.securityfilter.filter;
+
+import org.apache.oro.text.regex.PatternMatcher;
+import org.apache.oro.text.regex.Perl5Matcher;
+
+import java.util.Collection;
+
+/**
+ * URLPatternMatcher - A non-thread safe object to be used to match a request
+ * pattern with URLPattern objects.
+ *
+ * @author Max Cooper (max@maxcooper.com)
+ * @version $Revision: 1.1 $ $Date: 2002/12/17 15:10:01 $
+ */
+public class URLPatternMatcher {
+   private PatternMatcher patternMatcher;
+
+   /**
+    * Constructor
+    */
+   public URLPatternMatcher() {
+      patternMatcher = new Perl5Matcher();
+   }
+
+   /**
+    * Test to see if a string pattern matches a URLPattern.
+    *
+    * @param pattern a String pattern to check for a match
+    * @param urlPattern a URLPattern object to match against
+    * @return true if the pattern matched the urlPattern, false otherwise
+    * @throws Exception
+    */
+   public boolean match(String pattern, URLPattern urlPattern) throws Exception {
+      return patternMatcher.matches(pattern, urlPattern.getCompiledPattern());
+   }
+
+   /**
+    * Test to see if a string pattern and HTTP method matches a URLPattern.
+    *
+    * @param pattern a String pattern to check for a match
+    * @param httpMethod an HTTP pattern to check for a match
+    * @param urlPattern a URLPattern object to match against
+    * @return true if the pattern matched the urlPattern, false otherwise
+    * @throws Exception
+    */
+   public boolean match(String pattern, String httpMethod, URLPattern urlPattern) throws Exception {
+      if (match(pattern, urlPattern)) {
+         Collection methods = urlPattern.getWebResourceCollection().getHttpMethods();
+         if (methods.isEmpty() || methods.contains(httpMethod.toUpperCase())) {
+
+            return true;
+         }
+      }
+      return false;
+   }
+}
+
+// ----------------------------------------------------------------------------
+// EOF