fixed blocking requests to resources with no auth-constraint
authormaxcooper <maxcooper>
Mon, 12 Aug 2002 01:34:28 +0000 (01:34 +0000)
committermaxcooper <maxcooper>
Mon, 12 Aug 2002 01:34:28 +0000 (01:34 +0000)
added http-method support

src/share/org/securityfilter/config/SecurityConfig.java
src/share/org/securityfilter/config/SecurityConstraint.java
src/share/org/securityfilter/config/WebResourceCollection.java
src/share/org/securityfilter/filter/MatchableURLPattern.java
src/share/org/securityfilter/filter/SavedRequest.java [new file with mode: 0644]
src/share/org/securityfilter/filter/SecurityFilter.java
src/share/org/securityfilter/filter/SecurityRequestWrapper.java

index ded86da..bbe244f 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * $Header: /cvsroot/securityfilter/securityfilter/src/share/org/securityfilter/config/SecurityConfig.java,v 1.2 2002/08/09 08:36:08 maxcooper Exp $
- * $Revision: 1.2 $
- * $Date: 2002/08/09 08:36:08 $
+ * $Header: /cvsroot/securityfilter/securityfilter/src/share/org/securityfilter/config/SecurityConfig.java,v 1.3 2002/08/12 01:34:28 maxcooper Exp $
+ * $Revision: 1.3 $
+ * $Date: 2002/08/12 01:34:28 $
  *
  * ====================================================================
  * The SecurityFilter Software License, Version 1.1
@@ -72,7 +72,7 @@ import java.util.List;
  *
  * @author Torgeir Veimo (torgeir@pobox.com)
  * @author Max Cooper (max@maxcooper.com)
- * @version $Revision: 1.2 $ $Date: 2002/08/09 08:36:08 $
+ * @version $Revision: 1.3 $ $Date: 2002/08/12 01:34:28 $
  */
 public class SecurityConfig {
 
@@ -168,6 +168,7 @@ public class SecurityConfig {
          this.realm = (SecurityRealmInterface) realm;
          lastRealm = realm;
       } else {
+         // TODO: allow addRealm signaure to take types besides Object -- will commons-beanutils help?
          // call lastRealm.setRealm(realm)
          Method addMethod = lastRealm.getClass().getMethod("setRealm", new Class[] { Object.class });
          addMethod.invoke(lastRealm, new Object[] { realm });
@@ -228,6 +229,17 @@ public class SecurityConfig {
          "addSecurityConstraint",
          "org.securityfilter.config.SecurityConstraint"
       );
+
+      // auth-constraint
+      digester.addObjectCreate(
+         "securityfilter-config/security-constraint/auth-constraint",
+         "org.securityfilter.config.AuthConstraint"
+      );
+      digester.addSetNext(
+         "securityfilter-config/security-constraint/auth-constraint",
+         "setAuthConstraint",
+         "org.securityfilter.config.AuthConstraint"
+      );
       digester.addCallMethod(
          "securityfilter-config/security-constraint/auth-constraint/role-name",
          "addRole",
@@ -245,11 +257,6 @@ public class SecurityConfig {
          "org.securityfilter.config.WebResourceCollection"
       );
       digester.addCallMethod(
-         "securityfilter-config/security-constraint/web-resource-collection/web-resource-name",
-         "setName",
-         0
-      );
-      digester.addCallMethod(
          "securityfilter-config/security-constraint/web-resource-collection/url-pattern",
          "addURLPattern",
          0
index 7818a05..9b8b1c7 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * $Header: /cvsroot/securityfilter/securityfilter/src/share/org/securityfilter/config/SecurityConstraint.java,v 1.2 2002/08/09 08:36:08 maxcooper Exp $
- * $Revision: 1.2 $
- * $Date: 2002/08/09 08:36:08 $
+ * $Header: /cvsroot/securityfilter/securityfilter/src/share/org/securityfilter/config/SecurityConstraint.java,v 1.3 2002/08/12 01:34:28 maxcooper Exp $
+ * $Revision: 1.3 $
+ * $Date: 2002/08/12 01:34:28 $
  *
  * ====================================================================
  * The SecurityFilter Software License, Version 1.1
@@ -63,35 +63,17 @@ import java.util.List;
  *
  * @author Max Cooper (max@maxcooper.com)
  * @author Torgeir Veimo (torgeir@pobox.com)
- * @version $Revision: 1.2 $ $Date: 2002/08/09 08:36:08 $
+ * @version $Revision: 1.3 $ $Date: 2002/08/12 01:34:28 $
  */
 public class SecurityConstraint {
-   protected String name;
-   protected List resourceCollections;
-   protected List roles;
+   private List resourceCollections;
+   private AuthConstraint authConstraint = null;
 
    /**
     * Constructor
     */
    public SecurityConstraint() {
       this.resourceCollections = new ArrayList();
-      this.roles = new ArrayList();
-   }
-
-   /**
-    * Set the security constraint name.
-    *
-    * @param name the name of this SecurityConstraint
-    */
-   public void setName(String name) {
-      this.name = name;
-   }
-
-   /**
-    * Return the name of this SecurityConstraint
-    */
-   public String getName() {
-      return this.name;
    }
 
    /**
@@ -106,26 +88,29 @@ public class SecurityConstraint {
    /**
     * Get the WebResourceCollections for this SecurityConstraint. The order of the list is the order in which the
     * WebResourceCollections appeared in the config file.
+    *
+    * @return a List of WebResourceCollections, an empty list if none were added
     */
    public List getWebResourceCollections() {
       return this.resourceCollections;
    }
 
    /**
-    * Add a role to this SecurityConstraint.
+    * Set the AuthConstraint.
     *
-    * @param role role to add
+    * @param authConstraint the AuthConstraint
     */
-   public void addRole(String role) {
-      roles.add(role);
+   public void setAuthConstraint(AuthConstraint authConstraint) {
+      this.authConstraint = authConstraint;
    }
 
    /**
-    * Get the roles that are authorized to access the WebResourceCollections in this SecurityConstraint.
-    * Returns an empty list if no roles were present in the config file.
+    * Get the AuthConstraint.
+    *
+    * @return an AuthConstraint object, or null if none has been set
     */
-   public List getRoles() {
-      return this.roles;
+   public AuthConstraint getAuthConstraint() {
+      return authConstraint;
    }
 }
 
index 7860e2b..b4f8ae5 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * $Header: /cvsroot/securityfilter/securityfilter/src/share/org/securityfilter/config/WebResourceCollection.java,v 1.2 2002/08/09 08:36:08 maxcooper Exp $
- * $Revision: 1.2 $
- * $Date: 2002/08/09 08:36:08 $
+ * $Header: /cvsroot/securityfilter/securityfilter/src/share/org/securityfilter/config/WebResourceCollection.java,v 1.3 2002/08/12 01:34:28 maxcooper Exp $
+ * $Revision: 1.3 $
+ * $Date: 2002/08/12 01:34:28 $
  *
  * ====================================================================
  * The SecurityFilter Software License, Version 1.1
@@ -59,38 +59,21 @@ import java.util.*;
 
 /**
  * WebResourceCollection represents a web-resource-collection from the security config file.
- * It has a name, a list of url patterns, and a list of http methods.
+ * It has a list of url patterns, and a list of http methods.
  *
  * @author Max Cooper (max@maxcooper.com)
- * @version $Revision: 1.2 $ $Date: 2002/08/09 08:36:08 $
+ * @version $Revision: 1.3 $ $Date: 2002/08/12 01:34:28 $
  */
 public class WebResourceCollection {
-   protected String name;
-   protected List patterns;
-   protected Collection methods;
+   private List urlPatterns;
+   private Collection httpMethods;
 
    /**
     * Constructor
     */
    public WebResourceCollection() {
-      patterns = Collections.synchronizedList(new ArrayList());
-      methods = Collections.synchronizedSet(new HashSet());
-   }
-
-   /**
-    * Set the name of this WebResourceCollection.
-    *
-    * @param name name of this WebResourceCollection
-    */
-   public void setName(String name) {
-      this.name = name;
-   }
-
-   /**
-    * Get the name of this WebResourceCollection.
-    */
-   public String getName() {
-      return this.name;
+      urlPatterns = new ArrayList();
+      httpMethods = new HashSet();
    }
 
    /**
@@ -99,14 +82,14 @@ public class WebResourceCollection {
     * @param pattern url pattern to add
     */
    public void addURLPattern(String pattern) {
-      patterns.add(pattern);
+      urlPatterns.add(pattern);
    }
 
    /**
     * Get a list of url patterns in the order they were added to this WebResourceCollection.
     */
    public List getURLPatterns() {
-      return Collections.unmodifiableList(patterns);
+      return Collections.unmodifiableList(urlPatterns);
    }
 
    /**
@@ -115,14 +98,14 @@ public class WebResourceCollection {
     * @param method http method to add
     */
    public void addHttpMethod(String method) {
-      methods.add(method);
+      httpMethods.add(method.toUpperCase());
    }
 
    /**
     * Get a collection of http methods for this WebResourceCollection.
     */
    public Collection getHttpMethods() {
-      return Collections.unmodifiableCollection(methods);
+      return Collections.unmodifiableCollection(httpMethods);
    }
 }
 
index 215bc48..0f01268 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * $Header: /cvsroot/securityfilter/securityfilter/src/share/org/securityfilter/filter/Attic/MatchableURLPattern.java,v 1.2 2002/08/09 08:36:07 maxcooper Exp $
- * $Revision: 1.2 $
- * $Date: 2002/08/09 08:36:07 $
+ * $Header: /cvsroot/securityfilter/securityfilter/src/share/org/securityfilter/filter/Attic/MatchableURLPattern.java,v 1.3 2002/08/12 01:34:28 maxcooper Exp $
+ * $Revision: 1.3 $
+ * $Date: 2002/08/12 01:34:28 $
  *
  * ====================================================================
  * The SecurityFilter Software License, Version 1.1
@@ -61,13 +61,15 @@ 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 (not yet implemented - just sorts
  * by the order field).
  *
  * @author Max Cooper (max@maxcooper.com)
- * @version $Revision: 1.2 $ $Date: 2002/08/09 08:36:07 $
+ * @version $Revision: 1.3 $ $Date: 2002/08/12 01:34:28 $
  */
 public class MatchableURLPattern implements Comparable {
    private String pattern;
@@ -105,10 +107,17 @@ public class MatchableURLPattern implements Comparable {
     * 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) {
-      return patternRE.match(URL);
+   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;
    }
 
    /**
@@ -184,6 +193,7 @@ public class MatchableURLPattern implements Comparable {
     * Return the pattern string in RE syntax form.
     *
     * TO-DO: validate that the conversion is proper for all pattern strings (probably needs some improvements).
+    * Would the jakarta-oro be more suited for matching than jakarta-regexp?
     */
    private String getConvertedPattern() {
       StringBuffer buf = new StringBuffer(pattern);
diff --git a/src/share/org/securityfilter/filter/SavedRequest.java b/src/share/org/securityfilter/filter/SavedRequest.java
new file mode 100644 (file)
index 0000000..f88089f
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * $Header: /cvsroot/securityfilter/securityfilter/src/share/org/securityfilter/filter/SavedRequest.java,v 1.1 2002/08/12 01:34:28 maxcooper Exp $
+ * $Revision: 1.1 $
+ * $Date: 2002/08/12 01:34:28 $
+ *
+ * ====================================================================
+ * 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 javax.servlet.http.HttpServletRequest;
+import java.util.Map;
+
+/**
+ * SavedRequest
+ *
+ * @author Max Cooper (max@maxcooper.com)
+ */
+public class SavedRequest {
+   private Map parameterMap;
+   private String method;
+
+   /**
+    * Constructor
+    *
+    * @param request the request to save
+    */
+   public SavedRequest(HttpServletRequest request) {
+      parameterMap = request.getParameterMap();
+      method = request.getMethod();
+   }
+
+   /**
+    * Get a map of parameters (names & values)
+    */
+   public Map getParameterMap() {
+      return parameterMap;
+   }
+
+   /**
+    * Get the HTTP method
+    */
+   public String getMethod() {
+      return method;
+   }
+}
+
+// ----------------------------------------------------------------------------
+// EOF
\ No newline at end of file
index 52e1c15..613d093 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * $Header: /cvsroot/securityfilter/securityfilter/src/share/org/securityfilter/filter/SecurityFilter.java,v 1.2 2002/08/09 08:36:07 maxcooper Exp $
- * $Revision: 1.2 $
- * $Date: 2002/08/09 08:36:07 $
+ * $Header: /cvsroot/securityfilter/securityfilter/src/share/org/securityfilter/filter/SecurityFilter.java,v 1.3 2002/08/12 01:34:28 maxcooper Exp $
+ * $Revision: 1.3 $
+ * $Date: 2002/08/12 01:34:28 $
  *
  * ====================================================================
  * The SecurityFilter Software License, Version 1.1
@@ -58,6 +58,7 @@ 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;
 import org.securityfilter.config.WebResourceCollection;
@@ -77,32 +78,112 @@ import java.util.*;
  *
  * @author Max Cooper (max@maxcooper.com)
  * @author Torgeir Veimo (torgeir@pobox.com)
- * @version $Revision: 1.2 $ $Date: 2002/08/09 08:36:07 $
+ * @version $Revision: 1.3 $ $Date: 2002/08/12 01:34:28 $
  */
-public class SecurityFilter implements javax.servlet.Filter {
-   public static final String CONTINUE_TO_URL = SecurityFilter.class.getName() + ".CONTINUE_TO_URL";
-   public static final String POSTED_PARAM_URL = SecurityFilter.class.getName() + ".POSTED_PARAM_URL";
-   public static final String POSTED_PARAM_MAP = SecurityFilter.class.getName() + ".POSTED_PARAM_MAP";
+public class SecurityFilter implements Filter {
+   public static final String SAVED_REQUEST_URL = SecurityFilter.class.getName() + ".SAVED_REQUEST_URL";
+   public static final String SAVED_REQUEST = SecurityFilter.class.getName() + ".SAVED_REQUEST";
 
    public static final String CONFIG_FILE_KEY = "config";
    public static final String DEFAULT_CONFIG_FILE = "/WEB-INF/securityfilter-config.xml";
    public static final String VALIDATE_KEY = "validate";
    public static final String TRUE = "true";
 
-   protected FilterConfig config;
-   protected ArrayList URLPatterns;
-   protected SecurityRealmInterface realm;
-   protected String loginPage;
-   protected String errorPage;
-   protected String defaultPage;
-   protected RE loginSubmitRE;
-   protected List patternList;
+   private FilterConfig config;
+   private ArrayList URLPatterns;
+   private SecurityRealmInterface realm;
+   private String loginPage;
+   private String errorPage;
+   private String defaultPage;
+   private RE loginSubmitRE;
+   private 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";
 
    /**
+    * Perform filtering operation, and optionally pass the request down the chain.
+    *
+    * @param request the current request
+    * @param response the current response
+    * @param chain request handler chain
+    * @exception IOException
+    * @exception ServletException
+    */
+   public void doFilter(
+      ServletRequest request,
+      ServletResponse response,
+      FilterChain chain
+   ) throws IOException, ServletException {
+
+      HttpServletRequest hReq = (HttpServletRequest) request;
+      HttpServletResponse hRes = (HttpServletResponse) response;
+      SecurityRequestWrapper wrappedRequest;
+
+      //System.out.println("\n--- request URL = " + hReq.getRequestURL().toString());
+
+      // if the request has already been wrapped by the filter, pass it through unchecked
+      if (request instanceof SecurityRequestWrapper) {
+         wrappedRequest = (SecurityRequestWrapper) request;
+      } else {
+         // get the part of the URL to check for matches
+         String requestURL = getMatchableURL(hReq);
+
+         // get saved request, if any (returns null if not applicable)
+         SavedRequest savedRequest = getSavedRequest(hReq);
+
+         // wrap request
+         wrappedRequest = new SecurityRequestWrapper(hReq, realm, savedRequest);
+
+         // check if this is a login form submittal
+         if (loginSubmitRE.match(requestURL)) {
+            processLogin(wrappedRequest, hRes);
+            return;
+         }
+
+         // check if request matches security constraint
+         MatchableURLPattern match = matchPattern(requestURL, wrappedRequest.getMethod());
+
+         // check security constraint, if any
+         if (match != null) {
+            // TODO: check user-data-constraint
+            // check auth constraint
+            AuthConstraint authConstraint = match.getSecurityConstraint().getAuthConstraint();
+            if (authConstraint != null) {
+               Collection roles = authConstraint.getRoles();
+               Principal principal = wrappedRequest.getUserPrincipal();
+               // if roles is empty, access will be blocked no matter who the user is, so skip the login
+               if (!roles.isEmpty() && principal == null) {
+                  // user needs to be authenticated
+                  showLogin(hReq, hRes);
+                  return;
+               } else {
+                  boolean authorized = false;
+                  for (Iterator i = roles.iterator(); i.hasNext() && !authorized;) {
+                     String role = (String) i.next();
+                     // TODO: if *, do you need to have at least one role to be authorized?
+                     // if so, we need to iterate through the roles defined in config file or change the
+                     // realm inteface to get a list of roles for the user (both solutions are undesireable)
+                     if ("*".equals(role) || realm.isUserInRole(principal, role)) {
+                        authorized = true;
+                     }
+                  }
+                  if (!authorized) {
+                     // user does not meet role constraint
+                     hRes.sendError(HttpServletResponse.SC_FORBIDDEN);
+                     return;
+                  }
+               }
+            }
+         }
+      }
+
+      // pass the request down the filter chain
+      chain.doFilter(wrappedRequest, hRes);
+   }
+
+   /**
     * Initialize the SecurityFilter.
     *
     * @param config filter configuration object
@@ -183,101 +264,22 @@ public class SecurityFilter implements javax.servlet.Filter {
    }
 
    /**
-    * Perform filtering operation, and optionally pass the request down the chain.
-    *
-    * @param request the current request
-    * @param response the current response
-    * @param chain request handler chain
-    * @exception IOException
-    * @exception ServletException
-    */
-   public void doFilter(
-      ServletRequest request,
-      ServletResponse response,
-      FilterChain chain
-   ) throws IOException, ServletException {
-
-      HttpServletRequest hReq = (HttpServletRequest) request;
-      HttpServletResponse hRes = (HttpServletResponse) response;
-      SecurityRequestWrapper wrappedRequest;
-
-      // if the request has already been wrapped by the filter, pass it through unchecked
-      if (request instanceof SecurityRequestWrapper) {
-         wrappedRequest = (SecurityRequestWrapper) request;
-      } else {
-         // extract the request URL portion that needs to be checked
-         String requestURL = hReq.getRequestURI();
-         // remove the contextPath
-         requestURL = requestURL.substring(hReq.getContextPath().length());
-
-         String pathInfo = hReq.getPathInfo();
-         if ("/".equals(requestURL) && pathInfo != null) {
-            requestURL = pathInfo;
-         }
-
-         // get posted parameter map, if any (returns null if not applicable)
-         Map parameterMap = getPostedParameterMap(hReq);
-
-         // wrap request
-         wrappedRequest = new SecurityRequestWrapper(hReq, realm, parameterMap);
-
-         // check if this is a login form submittal
-         if (loginSubmitRE.match(requestURL)) {
-            processLogin(wrappedRequest, hRes);
-            return;
-         }
-
-         // check if request matches security constraint
-         MatchableURLPattern match = matchPattern(requestURL);
-
-         // check constraint, if any
-         if (match != null) {
-            // check roles
-            Collection roles = match.getSecurityConstraint().getRoles();
-            if (!roles.isEmpty()) {
-               Principal principal = wrappedRequest.getUserPrincipal();
-               if (principal == null) {
-                  // user needs to be authenticated
-                  showLogin(hReq, hRes);
-                  return;
-               } else {
-                  boolean authorized = false;
-                  for (Iterator i = roles.iterator(); i.hasNext() && !authorized;) {
-                     String role = (String) i.next();
-                     if ("*".equals(role) || realm.isUserInRole(principal, role)) {
-                        authorized = true;
-                     }
-                  }
-                  if (!authorized) {
-                     // user does not meet role constraint
-                     hRes.sendError(HttpServletResponse.SC_FORBIDDEN);
-                     return;
-                  }
-               }
-            }
-         }
-      }
-
-      // pass the request down the filter chain
-      chain.doFilter(wrappedRequest, hRes);
-   }
-
-   /**
-    * Find a match for the requested URL, if any.
+    * Find a match for the requested URL & method, if any.
     *
     * @param URL the URL to match
+    * @param httpMethod the HTTP Method to match
     * @return the matching MatchableURLPattern object, or null if there is no match.
     */
-   protected MatchableURLPattern matchPattern(String URL) {
+   protected MatchableURLPattern matchPattern(String URL, String httpMethod) {
       // PERFORMANCE IMPROVEMENT OPPORTUNITY: cahce URL pattern matches
-      MatchableURLPattern pattern = null;
-      for (Iterator i = patternList.iterator(); i.hasNext() && (pattern == null);) {
-         MatchableURLPattern testPattern = (MatchableURLPattern) i.next();
-         if (testPattern.match(URL)) {
-            pattern = testPattern;
+      Iterator i = patternList.iterator();
+      while (i.hasNext()) {
+         MatchableURLPattern pattern = (MatchableURLPattern) i.next();
+         if (pattern.match(URL, httpMethod)) {
+            return pattern;
          }
       }
-      return pattern;
+      return null;
    }
 
    /**
@@ -292,14 +294,12 @@ public class SecurityFilter implements javax.servlet.Filter {
       HttpServletRequest request,
       HttpServletResponse response
    ) throws IOException, ServletException {
-      // save continue to URL
-      setContinueToURL(request);
-      // if this is a post, save request parameters
-      if (request.getMethod().equalsIgnoreCase("POST")) {
-         setIncludePostedParametersForThisURL(request);
-      }
+      //System.out.println("showLogin() called...");
+      // save this request
+      saveRequestInformation(request);
       // forward to login page
       request.getRequestDispatcher(loginPage).forward(request, response);
+      response.sendRedirect(request.getContextPath() + loginPage);
    }
 
    /**
@@ -314,13 +314,14 @@ public class SecurityFilter implements javax.servlet.Filter {
       SecurityRequestWrapper request,
       HttpServletResponse response
    ) throws IOException, ServletException {
+      //System.out.println("processLogin() called...");
       String username = request.getParameter(FORM_USERNAME);
       String password = request.getParameter(FORM_PASSWORD);
       Principal principal = realm.authenticate(username, password);
       if (principal != null) {
          // login successful
          request.setUserPrincipal(principal);
-         String continueToURL = getAndClearContinueToURL(request);
+         String continueToURL = getContinueToURL(request);
          response.sendRedirect(continueToURL);
       } else {
          // login failed, set response status and forward to error page
@@ -330,77 +331,96 @@ public class SecurityFilter implements javax.servlet.Filter {
    }
 
    /**
-    * Get the URL to continue to after successful login from the session, and then remove it from the session.
+    * Get the URL to continue to after successful login. This may be the SAVED_REQUEST_URL if the authorization
+    * sequence was initiated by the filter, or the default URL (as specified in the config file) if a login
+    * request was spontaneously submitted.
     *
     * @param request the current request
     */
-   protected String getAndClearContinueToURL(HttpServletRequest request) {
-      HttpSession session = request.getSession(true);
-      String continueToURL = (String) session.getAttribute(CONTINUE_TO_URL);
-      if (continueToURL == null) {
-         return request.getContextPath() + defaultPage;
+   protected String getContinueToURL(HttpServletRequest request) {
+      //System.out.println("getContinueToURL() called...");
+      HttpSession session = request.getSession();
+      String savedURL = (String) session.getAttribute(SAVED_REQUEST_URL);
+      if (savedURL != null) {
+         return savedURL;
       } else {
-         session.removeAttribute(CONTINUE_TO_URL);
-         return continueToURL;
+         return request.getContextPath() + defaultPage;
       }
    }
 
    /**
-    * Save the current URL and POSTed parameters.
+    * Save request information to re-use when the user is successfully authenticated.
     *
     * @param request the current request
     */
-   protected void setIncludePostedParametersForThisURL(HttpServletRequest request) {
-      HttpSession session = request.getSession(true);
-      session.setAttribute(SecurityFilter.POSTED_PARAM_URL, request.getRequestURL().toString());
-      session.setAttribute(SecurityFilter.POSTED_PARAM_MAP, request.getParameterMap());
+   protected void saveRequestInformation(HttpServletRequest request) {
+      //System.out.println("saveRequestInformation() called...");
+      HttpSession session = request.getSession();
+      session.setAttribute(SecurityFilter.SAVED_REQUEST_URL, getSaveableURL(request));
+      session.setAttribute(SecurityFilter.SAVED_REQUEST, new SavedRequest(request));
    }
 
    /**
-    * If this request matches the one we saved POSTed parameters, return a map of those parameters and remove this
-    * associated information (URL + parameter map) from the session).
+    * If this request matches the one we saved, return the SavedRequest and remove it from the session.
     *
     * @param request the current request
-    * @return usually null, but when the request matches the posted URL that initiated the login sequence a map
-    * of the request parameters from the original post is returned
+    * @return usually null, but when the request matches the posted URL that initiated the login sequence a
+    * SavedRequest object is returned.
     */
-   protected Map getPostedParameterMap(HttpServletRequest request) {
-      HttpSession session = request.getSession(true);
-      String postedURL = (String) session.getAttribute(SecurityFilter.POSTED_PARAM_URL);
-      if (postedURL != null && postedURL.equals(request.getRequestURL().toString())) {
-         // this is a request for the post that caused the login,
-         // return the map of posted params and remove it from the session
-         Map map = (Map) session.getAttribute(SecurityFilter.POSTED_PARAM_MAP);
-         session.removeAttribute(SecurityFilter.POSTED_PARAM_URL);
-         session.removeAttribute(SecurityFilter.POSTED_PARAM_MAP);
-         return map;
+   protected SavedRequest getSavedRequest(HttpServletRequest request) {
+      //System.out.println("getSavedRequest() called...");
+      HttpSession session = request.getSession();
+      String savedURL = (String) session.getAttribute(SecurityFilter.SAVED_REQUEST_URL);
+      if (savedURL != null && savedURL.equals(getSaveableURL(request))) {
+         // this is a request for the request that caused the login,
+         // return the SavedRequest and remove it from the session
+         SavedRequest savedRequest = (SavedRequest) session.getAttribute(SecurityFilter.SAVED_REQUEST);
+         session.removeAttribute(SecurityFilter.SAVED_REQUEST_URL);
+         session.removeAttribute(SecurityFilter.SAVED_REQUEST);
+         return savedRequest;
       } else {
          return null;
       }
    }
 
    /**
-    * Save the current URL in the session so that we can redirect to this URL upon successful login.
-    * This method will not save the URL if it matches the login page, login error page, or login submit URL.
+    * Return a URL that can be matched against security URL patterns.<p>
+    *
+    * This is the part after the contextPath, with no query string.<br>
+    * http://server:8080/contextPath/someURL.jsp?param=value becomes /someURL.jsp
     *
-    * @param request the current HttpServletRequest
+    * @param request the request to construct a matchable URL for
     */
-   protected void setContinueToURL(HttpServletRequest request) {
-      // only save if it isn't the login page, login error page, or the login submit URL
-      String currentURL = request.getServletPath();
-      if (
-         !loginSubmitRE.match(currentURL)
-         && !loginPage.equals(currentURL)
-         && !errorPage.equals(currentURL)
-      ) {
-         StringBuffer continueToURL = new StringBuffer(request.getRequestURI());
-
-         String queryString = request.getQueryString();
-         if (queryString != null) {
-            continueToURL.append("?" + queryString);
-         }
-         request.getSession().setAttribute(CONTINUE_TO_URL, continueToURL.toString());
+   private String getMatchableURL(HttpServletRequest request) {
+      // extract the request URL portion that needs to be checked
+      String matchableURL = request.getRequestURI();
+      //System.out.println("RequestURI = " + matchableURL);
+      // remove the contextPath
+      matchableURL = matchableURL.substring(request.getContextPath().length());
+      // use PathInfo if this request didn't match a servlet name
+      String pathInfo = request.getPathInfo();
+      //System.out.println("PathInfo = " + pathInfo);
+      if ("/".equals(matchableURL) && pathInfo != null) {
+         matchableURL = pathInfo;
+      }
+      return matchableURL;
+   }
+
+   /**
+    * Return a URL suitable for saving or matching against a saved URL.<p>
+    *
+    * This is the whole URL, plus the query string.
+    *
+    * @param request the request to construct a saveable URL for
+    */
+   private String getSaveableURL(HttpServletRequest request) {
+      StringBuffer saveableURL = request.getRequestURL();
+      // add the query string, if any
+      String queryString = request.getQueryString();
+      if (queryString != null) {
+         saveableURL.append("?" + queryString);
       }
+      return saveableURL.toString();
    }
 }
 
index 542871f..958e20a 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * $Header: /cvsroot/securityfilter/securityfilter/src/share/org/securityfilter/filter/SecurityRequestWrapper.java,v 1.2 2002/08/09 08:36:07 maxcooper Exp $
- * $Revision: 1.2 $
- * $Date: 2002/08/09 08:36:07 $
+ * $Header: /cvsroot/securityfilter/securityfilter/src/share/org/securityfilter/filter/SecurityRequestWrapper.java,v 1.3 2002/08/12 01:34:28 maxcooper Exp $
+ * $Revision: 1.3 $
+ * $Date: 2002/08/12 01:34:28 $
  *
  * ====================================================================
  * The SecurityFilter Software License, Version 1.1
@@ -73,28 +73,28 @@ import java.util.Map;
  *
  * @author Max Cooper (max@maxcooper.com)
  * @author Torgeir Veimo (torgeir@pobox.com)
- * @version $Revision: 1.2 $ $Date: 2002/08/09 08:36:07 $
+ * @version $Revision: 1.3 $ $Date: 2002/08/12 01:34:28 $
  */
 public class SecurityRequestWrapper extends HttpServletRequestWrapper {
    public static final String PRINCIPAL_SESSION_KEY = SecurityRequestWrapper.class.getName() + ".PRINCIPAL";
 
-   private HttpServletRequest request;
+   private HttpServletRequest currentRequest;
    private SecurityRealmInterface realm;
-   private Map postedParameterMap;
+   private SavedRequest savedRequest;
 
    /**
     * Construct a new SecurityRequestWrapper.
     *
     * @param request the request to wrap
     * @param realm the SecurityRealmInterface implementation
-    * @param postedParameterMap map of name-value pairs to present as request parameters (POSTed params from a previous
-    * request)
+    * @param savedRequest SavedRequest (usually null, unless this is the request
+    * that invoked the authorization sequence)
     */
-   public SecurityRequestWrapper(HttpServletRequest request, SecurityRealmInterface realm, Map postedParameterMap) {
+   public SecurityRequestWrapper(HttpServletRequest request, SecurityRealmInterface realm, SavedRequest savedRequest) {
       super(request);
-      this.request = request;
+      this.currentRequest = request;
       this.realm = realm;
-      this.postedParameterMap = postedParameterMap;
+      this.savedRequest = savedRequest;
    }
 
    /**
@@ -103,12 +103,12 @@ public class SecurityRequestWrapper extends HttpServletRequestWrapper {
     * @param s parameter name
     */
    public String getParameter(String s) {
-      if (postedParameterMap == null) {
-         return request.getParameter(s);
+      if (savedRequest == null) {
+         return currentRequest.getParameter(s);
       } else {
-         String value = request.getParameter(s);
+         String value = currentRequest.getParameter(s);
          if (value == null) {
-            String[] valueArray = (String[]) postedParameterMap.get(s);
+            String[] valueArray = (String[]) savedRequest.getParameterMap().get(s);
             if (valueArray != null) {
                value = valueArray[0];
             }
@@ -121,11 +121,11 @@ public class SecurityRequestWrapper extends HttpServletRequestWrapper {
     * Get a map of parameter values for this request.
     */
    public Map getParameterMap() {
-      if (postedParameterMap == null) {
-         return request.getParameterMap();
+      if (savedRequest == null) {
+         return currentRequest.getParameterMap();
       } else {
-         Map map = new HashMap(postedParameterMap);
-         map.putAll(request.getParameterMap());
+         Map map = new HashMap(savedRequest.getParameterMap());
+         map.putAll(currentRequest.getParameterMap());
          return Collections.unmodifiableMap(map);
       }
    }
@@ -134,8 +134,8 @@ public class SecurityRequestWrapper extends HttpServletRequestWrapper {
     * Get an enumeration of paramaeter names for this request.
     */
    public Enumeration getParameterNames() {
-      if (postedParameterMap == null) {
-         return request.getParameterNames();
+      if (savedRequest == null) {
+         return currentRequest.getParameterNames();
       } else {
          return Collections.enumeration(getParameterMap().keySet());
       }
@@ -147,12 +147,12 @@ public class SecurityRequestWrapper extends HttpServletRequestWrapper {
     * @param s parameter name
     */
    public String[] getParameterValues(String s) {
-      if (postedParameterMap == null) {
-         return request.getParameterValues(s);
+      if (savedRequest == null) {
+         return currentRequest.getParameterValues(s);
       } else {
-         String[] values = request.getParameterValues(s);
+         String[] values = currentRequest.getParameterValues(s);
          if (values == null) {
-            values = (String[]) postedParameterMap.get(s);
+            values = (String[]) savedRequest.getParameterMap().get(s);
          }
          return values;
       }
@@ -165,7 +165,7 @@ public class SecurityRequestWrapper extends HttpServletRequestWrapper {
     */
    public void setRequest(ServletRequest request) {
       super.setRequest(request);
-      this.request = (HttpServletRequest) request;
+      this.currentRequest = (HttpServletRequest) request;
    }
 
    /**
@@ -193,7 +193,7 @@ public class SecurityRequestWrapper extends HttpServletRequestWrapper {
     * Get a Principal object for the current user.
     */
    public Principal getUserPrincipal() {
-      return (Principal) request.getSession().getAttribute(PRINCIPAL_SESSION_KEY);
+      return (Principal) currentRequest.getSession().getAttribute(PRINCIPAL_SESSION_KEY);
    }
 
    /**
@@ -206,7 +206,7 @@ public class SecurityRequestWrapper extends HttpServletRequestWrapper {
    public ServletInputStream getInputStream() throws IOException {
       ServletInputStream stream = super.getInputStream();
       if (stream == null) {
-         stream = request.getInputStream();
+         stream = currentRequest.getInputStream();
       }
       return stream;
    }
@@ -219,7 +219,30 @@ public class SecurityRequestWrapper extends HttpServletRequestWrapper {
     * @param username the login name of the remote user for this session
     */
    public void setUserPrincipal(Principal principal) {
-      request.getSession().setAttribute(PRINCIPAL_SESSION_KEY, principal);
+      currentRequest.getSession().setAttribute(PRINCIPAL_SESSION_KEY, principal);
+   }
+
+   /**
+    * Returns FORM_AUTH if the user has been authenticated, null otherwise.
+    */
+   public String getAuthType() {
+      if (getUserPrincipal() != null) {
+         return HttpServletRequest.FORM_AUTH;
+      } else {
+         return null;
+      }
+   }
+
+   /**
+    * Returns the HTTP method used to make this request. If the savedRequest is non-null,
+    * the HTTP method of the saved request will be returned.
+    */
+   public String getMethod() {
+      if (savedRequest != null) {
+         return savedRequest.getMethod();
+      } else {
+         return super.getMethod();
+      }
    }
 }