From 3cfbdc7ede37009e53c1873878bed3390f62e8bb Mon Sep 17 00:00:00 2001 From: maxcooper Date: Mon, 7 Jul 2003 13:12:56 +0000 Subject: [PATCH] refactored BASIC and FORM authentication functionality out of SecurityFilter and into their own classes FORM based authentication is tested and functional, will test BASIC and make any needed fixes soon --- .../authenticator/Authenticator.java | 124 +++++++ .../authenticator/AuthenticatorFactory.java | 104 ++++++ .../authenticator/BasicAuthenticator.java | 258 +++++++++++++ .../authenticator/FormAuthenticator.java | 215 +++++++++++ .../org/securityfilter/filter/SecurityFilter.java | 408 ++++++--------------- .../filter/SecurityRequestWrapper.java | 59 ++- 6 files changed, 849 insertions(+), 319 deletions(-) create mode 100644 src/share/org/securityfilter/authenticator/Authenticator.java create mode 100644 src/share/org/securityfilter/authenticator/AuthenticatorFactory.java create mode 100644 src/share/org/securityfilter/authenticator/BasicAuthenticator.java create mode 100644 src/share/org/securityfilter/authenticator/FormAuthenticator.java diff --git a/src/share/org/securityfilter/authenticator/Authenticator.java b/src/share/org/securityfilter/authenticator/Authenticator.java new file mode 100644 index 0000000..05b7150 --- /dev/null +++ b/src/share/org/securityfilter/authenticator/Authenticator.java @@ -0,0 +1,124 @@ +/* + * $Header: /cvsroot/securityfilter/securityfilter/src/share/org/securityfilter/authenticator/Authenticator.java,v 1.1 2003/07/07 13:12:56 maxcooper Exp $ + * $Revision: 1.1 $ + * $Date: 2003/07/07 13:12:56 $ + * + * ==================================================================== + * 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 SECURITY FILTER PROJECT 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.authenticator; + +import org.securityfilter.config.SecurityConfig; +import org.securityfilter.filter.*; + +import javax.servlet.FilterConfig; +import javax.servlet.http.*; +import java.io.IOException; + +/** + * Authenticator - interface for a SecurityFilter authenticator module. Implementations correspond to an implementation + * method, such as FORM or BASIC (others are possible). + * + * @author Max Cooper (max@maxcooper.com) + * @version $Revision: 1.1 $ $Date: 2003/07/07 13:12:56 $ + */ +public interface Authenticator { + + /** + * Initialize this Authenticator. + * + * @param filterConfig + * @param securityConfig + */ + public void init(FilterConfig filterConfig, SecurityConfig securityConfig) throws Exception; + + /** + * Get the auth method string for this authentication scheme. + * + * @return the auth method string for this Authenticator + */ + public String getAuthMethod(); + + /** + * Process any login information that was included in the request, if any. + * Returns true if SecurityFilter should abort further processing after the method completes (for example, if a + * redirect was sent as part of the login processing). + * + * @param request + * @param response + * @return true if the filter should return after this method ends, false otherwise + */ + public boolean processLogin(SecurityRequestWrapper request, HttpServletResponse response) throws Exception; + + /** + * Show the login interface. + * + * @param request + * @param response + */ + public void showLogin(HttpServletRequest request, HttpServletResponse response) throws IOException; + + /** + * Return true if security checks should be bypassed for this request. + * + * Example: for FORM based authentication, the login and error pages should always be viewable without being + * authenticated, even if they would otherwise be blocked by a security constraint. + * + * @param request + * @return true if security should be bypassed, false otherwise + */ + public boolean bypassSecurityForThisRequest( + SecurityRequestWrapper request, + URLPatternMatcher patternMatcher + ) throws Exception; +} + +// ------------------------------------------------------------------------ +// EOF diff --git a/src/share/org/securityfilter/authenticator/AuthenticatorFactory.java b/src/share/org/securityfilter/authenticator/AuthenticatorFactory.java new file mode 100644 index 0000000..1c2f814 --- /dev/null +++ b/src/share/org/securityfilter/authenticator/AuthenticatorFactory.java @@ -0,0 +1,104 @@ +/* + * $Header: /cvsroot/securityfilter/securityfilter/src/share/org/securityfilter/authenticator/AuthenticatorFactory.java,v 1.1 2003/07/07 13:12:56 maxcooper Exp $ + * $Revision: 1.1 $ + * $Date: 2003/07/07 13:12:56 $ + * + * ==================================================================== + * 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 SECURITY FILTER PROJECT 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.authenticator; + +import org.securityfilter.config.SecurityConfig; + +import javax.servlet.FilterConfig; +import javax.servlet.http.HttpServletRequest; + +/** + * AuthenticatorFactory - this class will create Authenticator instance. + * + * It is designed to be easy to extend to add more Authenticator implementations, or to allow custom Authenticators to + * be specified in the config file and created here in this factory class. + * + * @author Max Cooper (max@maxcooper.com) + * @version $Revision: 1.1 $ $Date: 2003/07/07 13:12:56 $ + */ +public class AuthenticatorFactory { + + /** + * Create an Authenticator based on the specified configuration information. + * + * @param filterConfig + * @param securityConfig + * @return + * @throws Exception + */ + public static Authenticator createAuthenticator( + FilterConfig filterConfig, + SecurityConfig securityConfig + ) throws Exception { + Authenticator authenticator = null; + + String authMethod = securityConfig.getAuthMethod(); + if (HttpServletRequest.FORM_AUTH.equals(authMethod)) { + // FORM + authenticator = new FormAuthenticator(); + } else if (HttpServletRequest.BASIC_AUTH.equals(authMethod)) { + // BASIC + authenticator = new BasicAuthenticator(); + } else { + throw new Exception("No Authenticator available for auth method: " + authMethod); + } + authenticator.init(filterConfig, securityConfig); + return authenticator; + } +} + +// ------------------------------------------------------------------------ +// EOF diff --git a/src/share/org/securityfilter/authenticator/BasicAuthenticator.java b/src/share/org/securityfilter/authenticator/BasicAuthenticator.java new file mode 100644 index 0000000..7eb43fe --- /dev/null +++ b/src/share/org/securityfilter/authenticator/BasicAuthenticator.java @@ -0,0 +1,258 @@ +/* + * $Header: /cvsroot/securityfilter/securityfilter/src/share/org/securityfilter/authenticator/BasicAuthenticator.java,v 1.1 2003/07/07 13:12:56 maxcooper Exp $ + * $Revision: 1.1 $ + * $Date: 2003/07/07 13:12:56 $ + * + * ==================================================================== + * 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 SECURITY FILTER PROJECT 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.authenticator; + +import org.apache.commons.codec.binary.Base64; +import org.securityfilter.config.SecurityConfig; +import org.securityfilter.filter.*; +import org.securityfilter.realm.SecurityRealmInterface; + +import javax.servlet.FilterConfig; +import javax.servlet.http.*; +import java.io.IOException; +import java.security.Principal; + +/** + * BasicAuthenticator - authenticator implementation for the BASIC auth method. + * + * @author Daya Sharma (iamdaya@yahoo.com, billydaya@sbcglobal.net) + * @author Max Cooper (max@maxcooper.com) + * @version $Revision: 1.1 $ $Date: 2003/07/07 13:12:56 $ + */ +public class BasicAuthenticator implements Authenticator { + public static final String BASIC_WINDOW_SHOWN = "basic_window_shown"; + public static final String LOGIN_ATTEMPTS = "loginAttempts"; + protected static final String DUMMY_TOKEN = "dummyToken"; + + public static final Base64 base64Helper = new Base64(); + + protected String tooManyIncorrectLogins; + + protected SecurityRealmInterface realm; + + /** + * Initialize this Authenticator. + * + * @param filterConfig + * @param securityConfig + */ + public void init(FilterConfig filterConfig, SecurityConfig securityConfig) throws Exception { + realm = securityConfig.getRealm(); + tooManyIncorrectLogins = "Sorry you are having problems logging in, please try again"; + } + + /** + * Returns BASIC as the authentication method. + * + * @return BASIC + */ + public String getAuthMethod() { + return HttpServletRequest.BASIC_AUTH; + } + + /** + * Process any login information that was included in the request, if any. + * Returns true if SecurityFilter should abort further processing after the method completes (for example, if a + * redirect was sent as part of the login processing). + * + * @param request + * @param response + * @return true if the filter should return after this method ends, false otherwise + */ + public boolean processLogin(SecurityRequestWrapper request, HttpServletResponse response) throws Exception { + if (basicAuthentication(request)) { + request.getSession().removeAttribute(BASIC_WINDOW_SHOWN); + String username = parseUsername(request.getHeader("Authorization")); + String password = parsePassword(request.getHeader("Authorization")); + Principal principal = realm.authenticate(username, password); + if (principal != null) { + // login successful + // invalidate old session if the user was already authenticated + // NOTE: we may want to check if the user re-authenticated as the same user, currently + // the session will be invalidated even if the user authenticates as the same user. + request.setUserPrincipal(principal); + String continueToURL = SecurityFilter.getContinueToURL(request); + request.getSession().setAttribute(DUMMY_TOKEN, DUMMY_TOKEN); + // This is the url that the user was initially accessing before being prompted for login. + response.sendRedirect(response.encodeRedirectURL(continueToURL)); + } else { + // login failed + // show the basic authentication window again. + showLogin(request.getCurrentRequest(), response); + } + } + return false; + } + + /** + * Returns true if this request includes BASIC auth info. + * + * @param request + * @return + */ + private boolean basicAuthentication(HttpServletRequest request) { + return ( + request.getSession().getAttribute(BASIC_WINDOW_SHOWN) != null + && request.getHeader("Authorization") != null + ); + } + + /** + * Parse the username out of the BASIC authorization header string. + * @param authorization + * @return + */ + private String parseUsername(String authorization) { + String unencoded = decodeBasicAuthorizationString(authorization); + if (unencoded == null) { + return null; + } else { + int colon = unencoded.indexOf(':'); + if (colon < 0) { + return null; + } else { + return unencoded.substring(0, colon).trim(); + } + } + } + + /** + * Parse the password out of the BASIC authorization header string. + * @param authorization + * @return + */ + private String parsePassword(String authorization) { + String unencoded = decodeBasicAuthorizationString(authorization); + if (unencoded == null) { + return null; + } else { + int colon = unencoded.indexOf(':'); + if (colon < 0) { + return (null); + } else { + return unencoded.substring(colon + 1).trim(); + } + } + } + + /** + * Decode the BASIC authorization string. + * + * @param authorization + * @return + */ + private String decodeBasicAuthorizationString(String authorization) { + if (authorization == null || !authorization.toLowerCase().startsWith("basic ")) { + return null; + } else { + authorization = authorization.substring(6).trim(); + // Decode and parse the authorization credentials + return new String(base64Helper.decodeBase64(authorization.getBytes())); + } + } + + /** + * Show the login page. + * + * @param request the current request + * @param response the current response + */ + public void showLogin(HttpServletRequest request, HttpServletResponse response) throws IOException { + // save this request + SecurityFilter.saveRequestInformation(request); + + // redirect to login page + request.getSession().setAttribute(BASIC_WINDOW_SHOWN, "shown"); + int loginAttempts = 1; + if (request.getSession().getAttribute(LOGIN_ATTEMPTS) != null) { + loginAttempts = ((Integer) request.getSession().getAttribute(LOGIN_ATTEMPTS)).intValue(); + loginAttempts += 1; + } + // todo: we can put some useful message here, perhaps a internationlizable format of message. + String loginAttemptMessage = "Login attempt number " + loginAttempts; + String logo; + if (loginAttempts <= 3) { + String realm = String.valueOf(Math.random()); + if (loginAttempts < 2) { + logo = "Basic Auth with Security Filter"; + } else { + logo = loginAttemptMessage; + } + String blankLine = " "; + logo = blankLine + blankLine + blankLine + logo + blankLine + blankLine; + System.err.println("response.setHeader \"WWW-Authenticate\", \"BASIC realm=\"" + logo + realm + "\""); + response.setHeader("WWW-Authenticate", "BASIC realm=\"" + realm + logo + "\""); + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + request.getSession().setAttribute(LOGIN_ATTEMPTS, new Integer(loginAttempts)); + } else { + request.getSession().removeAttribute(LOGIN_ATTEMPTS); + response.sendError(HttpServletResponse.SC_UNAUTHORIZED, tooManyIncorrectLogins); + } + } + + /** + * All requests should be subject to security checking for BASIC authentication. + * + * @param request + * @return always false -- check all requests + */ + public boolean bypassSecurityForThisRequest(SecurityRequestWrapper request, URLPatternMatcher patternMatcher) { + return false; + } +} + +// ------------------------------------------------------------------------ +// EOF diff --git a/src/share/org/securityfilter/authenticator/FormAuthenticator.java b/src/share/org/securityfilter/authenticator/FormAuthenticator.java new file mode 100644 index 0000000..7b4204a --- /dev/null +++ b/src/share/org/securityfilter/authenticator/FormAuthenticator.java @@ -0,0 +1,215 @@ +/* + * $Header: /cvsroot/securityfilter/securityfilter/src/share/org/securityfilter/authenticator/FormAuthenticator.java,v 1.1 2003/07/07 13:12:56 maxcooper Exp $ + * $Revision: 1.1 $ + * $Date: 2003/07/07 13:12:56 $ + * + * ==================================================================== + * 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 SECURITY FILTER PROJECT 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.authenticator; + +import org.securityfilter.config.SecurityConfig; +import org.securityfilter.filter.*; +import org.securityfilter.realm.SecurityRealmInterface; + +import javax.servlet.FilterConfig; +import javax.servlet.http.*; +import java.io.IOException; +import java.security.Principal; + +/** + * FormAuthenticator - authenticator implementation for the FORM auth method. + * + * @author Max Cooper (max@maxcooper.com) + * @version $Revision: 1.1 $ $Date: 2003/07/07 13:12:56 $ + */ +public class FormAuthenticator implements Authenticator { + + public static final String LOGIN_SUBMIT_PATTERN_KEY = "loginSubmitPattern"; + public static final String DEFAULT_LOGIN_SUBMIT_PATTERN = "/j_security_check"; + protected String loginSubmitPattern; + + protected static final String FORM_USERNAME = "j_username"; + protected static final String FORM_PASSWORD = "j_password"; + + protected String loginPage; + protected URLPattern loginPagePattern; + + protected String errorPage; + protected URLPattern errorPagePattern; + + protected String defaultPage; + + protected SecurityRealmInterface realm; + + /** + * Initilize this Authenticator. + * + * @param filterConfig + * @param securityConfig + */ + public void init(FilterConfig filterConfig, SecurityConfig securityConfig) throws Exception { + + realm = securityConfig.getRealm(); + + // login submit pattern + loginSubmitPattern = filterConfig.getInitParameter(LOGIN_SUBMIT_PATTERN_KEY); + if (loginSubmitPattern == null) { + loginSubmitPattern = DEFAULT_LOGIN_SUBMIT_PATTERN; + } + + // default page + defaultPage = securityConfig.getDefaultPage(); + + URLPatternFactory patternFactory = new URLPatternFactory(); + + // login page + loginPage = securityConfig.getLoginPage(); + loginPagePattern = patternFactory.createURLPattern(loginPage, null, null, 0); + + // error page + errorPage = securityConfig.getErrorPage(); + errorPagePattern = patternFactory.createURLPattern(errorPage, null, null, 0); + } + + /** + * Returns FORM as the authentication method. + * + * @return FORM + */ + public String getAuthMethod() { + return HttpServletRequest.FORM_AUTH; + } + + /** + * Process any login information that was included in the request, if any. + * Returns true if SecurityFilter should abort further processing after the method completes (for example, if a + * redirect was sent as part of the login processing). + * + * @param request + * @param response + * @return true if the filter should return after this method ends, false otherwise + */ + public boolean processLogin(SecurityRequestWrapper request, HttpServletResponse response) throws Exception { + if (request.getMatchableURL().endsWith(loginSubmitPattern)) { + String username = request.getParameter(FORM_USERNAME); + String password = request.getParameter(FORM_PASSWORD); + Principal principal = realm.authenticate(username, password); + if (principal != null) { + // login successful + // invalidate old session if the user was already authenticated + // NOTE: we may want to check if the user re-authenticated as the same user, currently + // the session will be invalidated even if the user authenticates as the same user. + request.setUserPrincipal(principal); + String continueToURL = getContinueToURL(request); + // This is the url that the user was initially accessing before being prompted for login. + response.sendRedirect(response.encodeRedirectURL(continueToURL)); + } else { + // login failed + // set response status and forward to error page + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + request.getRequestDispatcher(errorPage).forward(request, response); + } + return true; + } + return false; + } + + /** + * Show the login page. + * + * @param request the current request + * @param response the current response + */ + public void showLogin( + HttpServletRequest request, + HttpServletResponse response + ) throws IOException { + // save this request + SecurityFilter.saveRequestInformation(request); + + // redirect to login page + response.sendRedirect(request.getContextPath() + loginPage); + return; + } + + /** + * FormAuthenticator has a special case where the user should be sent to a default page if the user + * spontaneously submits a login request. + * + * @param request + * @return a URL to send the user to after logging in + */ + private String getContinueToURL(HttpServletRequest request) { + String savedURL = SecurityFilter.getContinueToURL(request); + if (savedURL != null) { + return savedURL; + } else { + return request.getContextPath() + defaultPage; + } + } + + /** + * The login and error pages should be viewable, even if they would otherwise be blocked by a security constraint. + * + * @param request + * @return + */ + public boolean bypassSecurityForThisRequest( + SecurityRequestWrapper request, + URLPatternMatcher patternMatcher + ) throws Exception { + String requestURL = request.getMatchableURL(); + return patternMatcher.match(requestURL, loginPagePattern) || patternMatcher.match(requestURL, errorPagePattern); + } +} + +// ------------------------------------------------------------------------ +// EOF diff --git a/src/share/org/securityfilter/filter/SecurityFilter.java b/src/share/org/securityfilter/filter/SecurityFilter.java index 9e0ae99..11a80e8 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.20 2003/07/07 04:18:29 maxcooper Exp $ - * $Revision: 1.20 $ - * $Date: 2003/07/07 04:18:29 $ + * $Header: /cvsroot/securityfilter/securityfilter/src/share/org/securityfilter/filter/SecurityFilter.java,v 1.21 2003/07/07 13:12:57 maxcooper Exp $ + * $Revision: 1.21 $ + * $Date: 2003/07/07 13:12:57 $ * * ==================================================================== * The SecurityFilter Software License, Version 1.1 @@ -55,17 +55,12 @@ package org.securityfilter.filter; -import org.apache.commons.codec.binary.Base64; -import org.securityfilter.config.AuthConstraint; -import org.securityfilter.config.SecurityConfig; -import org.securityfilter.config.SecurityConstraint; -import org.securityfilter.config.WebResourceCollection; +import org.securityfilter.authenticator.*; +import org.securityfilter.config.*; import org.securityfilter.realm.SecurityRealmInterface; import javax.servlet.*; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; +import javax.servlet.http.*; import java.io.IOException; import java.net.URL; import java.security.Principal; @@ -77,47 +72,25 @@ import java.util.*; * @author Max Cooper (max@maxcooper.com) * @author Daya Sharma (iamdaya@yahoo.com, billydaya@sbcglobal.net) * @author Torgeir Veimo (torgeir@pobox.com) - * @version $Revision: 1.20 $ $Date: 2003/07/07 04:18:29 $ + * @version $Revision: 1.21 $ $Date: 2003/07/07 13:12:57 $ */ public class SecurityFilter implements Filter { 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 LOGIN_SUBMIT_PATTERN_KEY = "loginSubmitPattern"; - public static final String DEFAULT_LOGIN_SUBMIT_PATTERN = "/j_security_check"; - protected String loginSubmitPattern; - public static final String TRUE = "true"; - 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 ALREADY_PROCESSED = SecurityFilter.class.getName() + ".ALREADY_PROCESSED"; - public static final String BASIC_WINDOW_SHOWN = "basic_window_shown"; - public static final String LOGIN_ATTEMPTS = "loginAttempts"; - - protected static final String DUMMY_TOKEN = "dummyToken"; - - protected static final String FORM_USERNAME = "j_username"; - protected static final String FORM_PASSWORD = "j_password"; - - public static final Base64 base64Helper = new Base64(); + public static final String SAVED_REQUEST_URL = SecurityFilter.class.getName() + ".SAVED_REQUEST_URL"; + public static final String SAVED_REQUEST = SecurityFilter.class.getName() + ".SAVED_REQUEST"; protected FilterConfig config; protected SecurityRealmInterface realm; - protected String loginPage; - protected URLPattern loginPagePattern; - protected String errorPage; - protected URLPattern errorPagePattern; - protected String defaultPage; - protected URLPatternFactory patternFactory; protected List patternList; - - protected String authMethod; - protected boolean basic = false; - protected String tooManyIncorrectLogins; + protected URLPatternFactory patternFactory; + protected Authenticator authenticator; /** * Perform filtering operation, and optionally pass the request down the chain. @@ -146,37 +119,23 @@ public class SecurityFilter implements Filter { // 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); - // get saved request, if any (returns null if not applicable) SavedRequest savedRequest = getSavedRequest(hReq); // wrap request - wrappedRequest = new SecurityRequestWrapper(hReq, realm, savedRequest); + wrappedRequest = new SecurityRequestWrapper(hReq, savedRequest, realm, authenticator.getAuthMethod()); URLPattern match = null; try { - // check if this is a login form submittal - if (basicAuthentication(hReq)) { - hReq.getSession().removeAttribute(BASIC_WINDOW_SHOWN); - processLogin(wrappedRequest, hRes); - return; - } else if (requestURL.endsWith(loginSubmitPattern)) { - processLogin(wrappedRequest, hRes); + // check if this request includes login info + if (authenticator.processLogin(wrappedRequest, hRes)) { return; } - // only check the request for a security constraint match if it doesn't - // match the login page or error page patterns -- this allows requests for the - // login page and error pages to be viewed even when their URLs would otherwise - // be subject to a security constraint - if ( - !patternMatcher.match(requestURL, loginPagePattern) - && !patternMatcher.match(requestURL, errorPagePattern) - ) { + // match the url if the authenticator does not indicate that security should be bypassed + if (!authenticator.bypassSecurityForThisRequest(wrappedRequest, patternMatcher)) { // check if request matches security constraint - match = matchPattern(requestURL, wrappedRequest.getMethod(), patternMatcher); + match = matchPattern(wrappedRequest.getMatchableURL(), wrappedRequest.getMethod(), patternMatcher); } } catch (Exception e) { throw new ServletException("Error matching patterns", e); @@ -191,9 +150,10 @@ public class SecurityFilter implements Filter { 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 && hReq.getSession().getAttribute(DUMMY_TOKEN) == null) { + // todo: do we still need this DUMMY_TOKEN check for BASIC auth? + if (!roles.isEmpty() && principal == null /* && hReq.getSession().getAttribute(DUMMY_TOKEN) == null */) { // user needs to be authenticated - showLogin(hReq, hRes); + authenticator.showLogin(hReq, hRes); return; } else { boolean authorized = false; @@ -222,14 +182,6 @@ public class SecurityFilter implements Filter { chain.doFilter(request, response); } - private boolean basicAuthentication(HttpServletRequest hReq) { - return ( - authMethod.equalsIgnoreCase("basic") - && hReq.getSession().getAttribute(BASIC_WINDOW_SHOWN) != null - && hReq.getHeader("Authorization") != null - ); - } - /** * Initialize the SecurityFilter. * @@ -238,41 +190,29 @@ public class SecurityFilter implements Filter { public void init(FilterConfig config) throws ServletException { this.config = config; try { - patternFactory = new URLPatternFactory(); - // parse config file + // config file name String configFile = config.getInitParameter(CONFIG_FILE_KEY); if (configFile == null) { configFile = DEFAULT_CONFIG_FILE; } URL configURL = config.getServletContext().getResource(configFile); + // validate config file? - String validate = config.getInitParameter(VALIDATE_KEY); - // login submit pattern - loginSubmitPattern = config.getInitParameter(LOGIN_SUBMIT_PATTERN_KEY); - if (loginSubmitPattern == null) { - loginSubmitPattern = DEFAULT_LOGIN_SUBMIT_PATTERN; - } - SecurityConfig securityConfig = new SecurityConfig(TRUE.equalsIgnoreCase(validate)); + boolean validate = TRUE.equalsIgnoreCase(config.getInitParameter(VALIDATE_KEY)); + + SecurityConfig securityConfig = new SecurityConfig(validate); securityConfig.loadConfig(configURL); - // get config values + // get the realm realm = securityConfig.getRealm(); - defaultPage = securityConfig.getDefaultPage(); - - // get login and error page patterns - loginPage = securityConfig.getLoginPage(); - loginPagePattern = patternFactory.createURLPattern(loginPage, null, null, 0); - errorPage = securityConfig.getErrorPage(); - errorPagePattern = patternFactory.createURLPattern(errorPage, null, null, 0); - authMethod = securityConfig.getAuthMethod(); - // todo: support DIGEST and CERT authentication schemes. - if (authMethod.equalsIgnoreCase(HttpServletRequest.BASIC_AUTH)) { - basic = true; - } + + // create an Authenticator + authenticator = AuthenticatorFactory.createAuthenticator(config, securityConfig); // create pattern list + patternFactory = new URLPatternFactory(); patternList = new ArrayList(); int order = 1; List constraints = securityConfig.getSecurityConstraints(); @@ -309,22 +249,6 @@ public class SecurityFilter implements Filter { } /** - * Set the filter configuration, included for WebLogic 6 compatibility. - * - * @param config filter configuration object - */ - public void setFilterConfig(FilterConfig config) throws ServletException { - init(config); - } - - /** - * Get the filter config object, included for WebLogic 6 compatibility. - */ - public FilterConfig getFilterConfig() { - return config; - } - - /** * Find a match for the requested pattern & method, if any. * * @param pattern the pattern to match @@ -345,131 +269,6 @@ public class SecurityFilter implements Filter { } /** - * Show the login page. - * - * @param request the current request - * @param response the current response - * @exception IOException - * @exception ServletException - */ - protected void showLogin( - HttpServletRequest request, - HttpServletResponse response - ) throws IOException, ServletException { - // save this request - saveRequestInformation(request); - - // redirect to login page - if (basic) { - request.getSession().setAttribute(BASIC_WINDOW_SHOWN, "shown"); - int loginAttempts = 1; - if (request.getSession().getAttribute(LOGIN_ATTEMPTS) != null) { - loginAttempts = ((Integer) request.getSession().getAttribute(LOGIN_ATTEMPTS)).intValue(); - loginAttempts += 1; - } - // todo: we can put some useful message here, perhaps a internationlizable format of message. - tooManyIncorrectLogins = "Sorry you are having problems logging in, please try again"; - String loginAttemptMessage = "Login attempt number " + loginAttempts; - String logo; - if (loginAttempts <= 3) { - String realm = String.valueOf(Math.random()); - if (loginAttempts < 2) { - logo = "Basic Auth with Security Filter"; - } else { - logo = loginAttemptMessage; - } - String blankLine = " "; - logo = blankLine + blankLine + blankLine + logo + blankLine + blankLine; - System.err.println("response.setHeader \"WWW-Authenticate\", \"BASIC realm=\"" + logo + realm + "\""); - response.setHeader("WWW-Authenticate", "BASIC realm=\"" + realm + logo + "\""); - response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); - request.getSession().setAttribute(LOGIN_ATTEMPTS, new Integer(loginAttempts)); - } else { - request.getSession().removeAttribute(LOGIN_ATTEMPTS); - response.sendError(HttpServletResponse.SC_UNAUTHORIZED, tooManyIncorrectLogins); - } - } else { - response.sendRedirect(request.getContextPath() + loginPage); - return; - } - } - - /** - * Process a login form submittal. - * - * @param request the current request - * @param response the current response - * @exception IOException - * @exception ServletException - */ - protected void processLogin( - SecurityRequestWrapper request, - HttpServletResponse response - ) throws IOException, ServletException { - String username; - String password; - if (basic) { - username = parseUsername(request.getHeader("Authorization")); - password = parsePassword(request.getHeader("Authorization")); - } else { - username = request.getParameter(FORM_USERNAME); - password = request.getParameter(FORM_PASSWORD); - } - Principal principal = realm.authenticate(username, password); - if (principal != null) { - // login successful - // invalidate old session if the user was already authenticated - // NOTE: we may want to check if the user re-authenticated as the same user, currently - // the session will be invalidated even if the user authenticates as the same user. - request.setUserPrincipal(principal); - String continueToURL = getContinueToURL(request); - if (basic) { - // what does this do? - request.getSession().setAttribute(DUMMY_TOKEN, DUMMY_TOKEN); - } - // This is the url that the user was initially accessing before being prompted for login. - response.sendRedirect(response.encodeRedirectURL(continueToURL)); - } else { - // login failed - if (!basic) { - // set response status and forward to error page - response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); - request.getRequestDispatcher(errorPage).forward(request, response); - } else { - // show the basic authentication window again. - showLogin(request.getCurrentRequest(), response); - } - } - } - - /** - * 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 getContinueToURL(HttpServletRequest request) { - String savedURL = (String) request.getSession().getAttribute(SAVED_REQUEST_URL); - if (savedURL != null) { - return savedURL; - } else { - return request.getContextPath() + defaultPage; - } - } - - /** - * Save request information to re-use when the user is successfully authenticated. - * - * @param request the current request - */ - protected void saveRequestInformation(HttpServletRequest request) { - 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, return the SavedRequest and remove it from the session. * * @param request the current request @@ -493,23 +292,30 @@ public class SecurityFilter implements Filter { } } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // The following methods are provided as static utilities for use by SecurityFilter and other classes. // + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** - * Return a URL that can be matched against security URL patterns.

+ * 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. * - * This is the part after the contextPath, with the pathInfo, but without the query string.
- * http://server:8080/contextPath/someURL.jsp?param=value becomes /someURL.jsp + * @param request the current request + */ + public static String getContinueToURL(HttpServletRequest request) { + return (String) request.getSession().getAttribute(SAVED_REQUEST_URL); + } + + /** + * Save request information to re-use when the user is successfully authenticated. * - * @param request the request to construct a matchable URL for + * @param request the current request */ - private String getMatchableURL(HttpServletRequest request) { - // extract the servlet path portion that needs to be checked - String matchableURL = request.getServletPath(); - // add the pathInfo, as it needs to be part of the URL we check - String pathInfo = request.getPathInfo(); - if (pathInfo != null) { - matchableURL = matchableURL + pathInfo; - } - return matchableURL; + public static void saveRequestInformation(HttpServletRequest request) { + HttpSession session = request.getSession(); + session.setAttribute(SecurityFilter.SAVED_REQUEST_URL, getSaveableURL(request)); + session.setAttribute(SecurityFilter.SAVED_REQUEST, new SavedRequest(request)); } /** @@ -519,32 +325,15 @@ public class SecurityFilter implements Filter { * * @param request the request to construct a saveable URL for */ - private String getSaveableURL(HttpServletRequest request) { - String url = null; + private static String getSaveableURL(HttpServletRequest request) { StringBuffer saveableURL = null; - String protocol = request.getProtocol(); try { saveableURL = request.getRequestURL(); } catch (NoSuchMethodError e) { - // this is done to support app servers like orion 1.5.2 - // which have not implemented the servlet 2.3 specification but have implemented the final draft of 2.3 spec. - if (protocol.equals("HTTP/1.1")) { // todo: provide support for ftp, webdav protocol among others. - if (request.isSecure()) { - protocol = "https://"; - } else { - protocol = "http://"; - } - } - url = protocol + request.getServerName() + ":" + request.getServerPort() + request.getRequestURI(); - } - if (saveableURL == null) { - saveableURL = new StringBuffer(url); - } else { - // since HTTP is the same regardless of whether it runs on TCP or on SSL/TCP - if (protocol.equals("HTTP/1.1") && request.isSecure() && saveableURL.toString().startsWith("http://")) { - saveableURL.replace(0, 4, "https"); // todo: this needs to be tested extensively. - } + saveableURL = getRequestURL(request); } + // fix the protocol + fixProtocol(saveableURL, request); // add the query string, if any String queryString = request.getQueryString(); if (queryString != null) { @@ -553,57 +342,72 @@ public class SecurityFilter implements Filter { return saveableURL.toString(); } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // The following methods are provided for compatibility with various app servers. // + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** - * Parse the username out of the BASIC authorization header string. - * @param authorization - * @return + * Set the filter configuration, included for WebLogic 6 compatibility. + * + * @param config filter configuration object */ - private String parseUsername(String authorization) { - String unencoded = decodeBasicAuthorizationString(authorization); - if (unencoded == null) { - return null; - } else { - int colon = unencoded.indexOf(':'); - if (colon < 0) { - return null; - } else { - return unencoded.substring(0, colon).trim(); - } - } + public void setFilterConfig(FilterConfig config) throws ServletException { + init(config); } /** - * Parse the password out of the BASIC authorization header string. - * @param authorization - * @return + * Get the filter config object, included for WebLogic 6 compatibility. */ - private String parsePassword(String authorization) { - String unencoded = decodeBasicAuthorizationString(authorization); - if (unencoded == null) { - return null; - } else { - int colon = unencoded.indexOf(':'); - if (colon < 0) { - return (null); + public FilterConfig getFilterConfig() { + return config; + } + + /** + * Get the requestURL. + * This method is called when the app server fails to implement HttpServletRequest.getRequestURL(). + * Orion 1.5.2 is one such server. + */ + private static StringBuffer getRequestURL(HttpServletRequest request) { + String protocol = request.getProtocol(); + int port = request.getServerPort(); + String portString = ":" + port; + + // todo: this needs to be tested to see if it still an issue; remove it if it is not needed + // Set the portString to the empty string if the requrest came in on the default port. + // This will keep Netscape from dropping the session, which happens when the port is added where it wasn't before. + // This is not perfect, but most requests on the default ports will not be made with an explicit port number. + if (protocol.equals("HTTP/1.1")) { + if (!request.isSecure()) { + if (port == 80) { + portString = ""; + } } else { - return unencoded.substring(colon + 1).trim(); + if (port == 443) { + portString = ""; + } } } + + // construct the saveable URL string + return new StringBuffer(protocol + request.getServerName() + portString + request.getRequestURI()); } /** - * Decode the BASIC authorization string. + * Fix the protocol portion of an absolute url. Often, the protocol will be http: even for https: requests. + * + * todo: needs testing to make sure this is proper in all circumstances * - * @param authorization - * @return + * @param url + * @param request */ - private String decodeBasicAuthorizationString(String authorization) { - if (authorization == null || !authorization.toLowerCase().startsWith("basic ")) { - return null; - } else { - authorization = authorization.substring(6).trim(); - // Decode and parse the authorization credentials - return new String(base64Helper.decodeBase64(authorization.getBytes())); + private static void fixProtocol(StringBuffer url, HttpServletRequest request) { + // fix protocol, if needed (since HTTP is the same regardless of whether it runs on TCP or on SSL/TCP) + if ( + request.getProtocol().equals("HTTP/1.1") + && request.isSecure() + && url.toString().startsWith("http://") + ) { + url.replace(0, 4, "https"); } } } diff --git a/src/share/org/securityfilter/filter/SecurityRequestWrapper.java b/src/share/org/securityfilter/filter/SecurityRequestWrapper.java index 1803ed1..77077ef 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.8 2003/07/07 04:16:40 maxcooper Exp $ - * $Revision: 1.8 $ - * $Date: 2003/07/07 04:16:40 $ + * $Header: /cvsroot/securityfilter/securityfilter/src/share/org/securityfilter/filter/SecurityRequestWrapper.java,v 1.9 2003/07/07 13:12:57 maxcooper Exp $ + * $Revision: 1.9 $ + * $Date: 2003/07/07 13:12:57 $ * * ==================================================================== * The SecurityFilter Software License, Version 1.1 @@ -57,16 +57,11 @@ package org.securityfilter.filter; import org.securityfilter.realm.SecurityRealmInterface; -import javax.servlet.ServletInputStream; -import javax.servlet.ServletRequest; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletRequestWrapper; +import javax.servlet.*; +import javax.servlet.http.*; import java.io.IOException; import java.security.Principal; -import java.util.Collections; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.Map; +import java.util.*; /** * SecurityRequestWrapper @@ -74,7 +69,7 @@ import java.util.Map; * @author Max Cooper (max@maxcooper.com) * @author Daya Sharma (iamdaya@yahoo.com, billydaya@sbcglobal.net) * @author Torgeir Veimo (torgeir@pobox.com) - * @version $Revision: 1.8 $ $Date: 2003/07/07 04:16:40 $ + * @version $Revision: 1.9 $ $Date: 2003/07/07 13:12:57 $ */ public class SecurityRequestWrapper extends HttpServletRequestWrapper { public static final String PRINCIPAL_SESSION_KEY = SecurityRequestWrapper.class.getName() + ".PRINCIPAL"; @@ -82,6 +77,8 @@ public class SecurityRequestWrapper extends HttpServletRequestWrapper { private HttpServletRequest currentRequest; private SecurityRealmInterface realm; private SavedRequest savedRequest; + private String authType; + private String matchableURL; /** * Construct a new SecurityRequestWrapper. @@ -91,11 +88,18 @@ public class SecurityRequestWrapper extends HttpServletRequestWrapper { * @param savedRequest SavedRequest (usually null, unless this is the request * that invoked the authorization sequence) */ - public SecurityRequestWrapper(HttpServletRequest request, SecurityRealmInterface realm, SavedRequest savedRequest) { + public SecurityRequestWrapper( + HttpServletRequest request, + SavedRequest savedRequest, + SecurityRealmInterface realm, + String authType + ) { super(request); this.currentRequest = request; this.realm = realm; this.savedRequest = savedRequest; + this.authType = authType; + initMatchableURL(); } /** @@ -231,13 +235,11 @@ public class SecurityRequestWrapper extends HttpServletRequestWrapper { } /** - * Returns FORM_AUTH if the user has been authenticated, null otherwise. - * - * todo: add BASIC support + * Returns the auth type (e.g. FORM, BASIC, etc.). */ public String getAuthType() { if (getUserPrincipal() != null) { - return HttpServletRequest.FORM_AUTH; + return authType; } else { return null; } @@ -254,6 +256,29 @@ public class SecurityRequestWrapper extends HttpServletRequestWrapper { return super.getMethod(); } } + + /** + * Get a URL that can be matched against security URL patterns. + * + * This is the part after the contextPath, with the pathInfo, but without the query string. + * http://server:8080/contextPath/someURL.jsp?param=value becomes /someURL.jsp + */ + public String getMatchableURL() { + return matchableURL; + } + + /** + * Initilize the matchableURL. + */ + private void initMatchableURL() { + // extract the servlet path portion that needs to be checked + matchableURL = currentRequest.getServletPath(); + // add the pathInfo, as it needs to be part of the URL we check + String pathInfo = currentRequest.getPathInfo(); + if (pathInfo != null) { + matchableURL = matchableURL + pathInfo; + } + } } // ------------------------------------------------------------------------ -- 2.11.0