From e872ceb0132a7c5df59d043377156534c81ea6fe Mon Sep 17 00:00:00 2001 From: maxcooper Date: Mon, 12 Aug 2002 01:34:28 +0000 Subject: [PATCH] fixed blocking requests to resources with no auth-constraint added http-method support --- .../org/securityfilter/config/SecurityConfig.java | 25 +- .../securityfilter/config/SecurityConstraint.java | 49 ++- .../config/WebResourceCollection.java | 43 +-- .../securityfilter/filter/MatchableURLPattern.java | 22 +- .../org/securityfilter/filter/SavedRequest.java | 96 ++++++ .../org/securityfilter/filter/SecurityFilter.java | 332 +++++++++++---------- .../filter/SecurityRequestWrapper.java | 81 +++-- 7 files changed, 386 insertions(+), 262 deletions(-) create mode 100644 src/share/org/securityfilter/filter/SavedRequest.java diff --git a/src/share/org/securityfilter/config/SecurityConfig.java b/src/share/org/securityfilter/config/SecurityConfig.java index ded86da..bbe244f 100644 --- a/src/share/org/securityfilter/config/SecurityConfig.java +++ b/src/share/org/securityfilter/config/SecurityConfig.java @@ -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 diff --git a/src/share/org/securityfilter/config/SecurityConstraint.java b/src/share/org/securityfilter/config/SecurityConstraint.java index 7818a05..9b8b1c7 100644 --- a/src/share/org/securityfilter/config/SecurityConstraint.java +++ b/src/share/org/securityfilter/config/SecurityConstraint.java @@ -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; } } diff --git a/src/share/org/securityfilter/config/WebResourceCollection.java b/src/share/org/securityfilter/config/WebResourceCollection.java index 7860e2b..b4f8ae5 100644 --- a/src/share/org/securityfilter/config/WebResourceCollection.java +++ b/src/share/org/securityfilter/config/WebResourceCollection.java @@ -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); } } diff --git a/src/share/org/securityfilter/filter/MatchableURLPattern.java b/src/share/org/securityfilter/filter/MatchableURLPattern.java index 215bc48..0f01268 100644 --- a/src/share/org/securityfilter/filter/MatchableURLPattern.java +++ b/src/share/org/securityfilter/filter/MatchableURLPattern.java @@ -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 index 0000000..f88089f --- /dev/null +++ b/src/share/org/securityfilter/filter/SavedRequest.java @@ -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 diff --git a/src/share/org/securityfilter/filter/SecurityFilter.java b/src/share/org/securityfilter/filter/SecurityFilter.java index 52e1c15..613d093 100644 --- a/src/share/org/securityfilter/filter/SecurityFilter.java +++ b/src/share/org/securityfilter/filter/SecurityFilter.java @@ -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.

+ * + * This is the part after the contextPath, with no query string.
+ * 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.

+ * + * 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(); } } diff --git a/src/share/org/securityfilter/filter/SecurityRequestWrapper.java b/src/share/org/securityfilter/filter/SecurityRequestWrapper.java index 542871f..958e20a 100644 --- a/src/share/org/securityfilter/filter/SecurityRequestWrapper.java +++ b/src/share/org/securityfilter/filter/SecurityRequestWrapper.java @@ -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(); + } } } -- 2.11.0