From 6c26e79a0f7e9331c7311e90333656b9057976ba Mon Sep 17 00:00:00 2001 From: Colin Sampaleanu Date: Fri, 9 Apr 2004 02:44:17 +0000 Subject: [PATCH] change AuthenticationProcessingFilter and SecurityEnforcementFilter to use Spring's WebApplicationContextUtils by defualt to find their config context. --- .../adapters/jboss/JbossAcegiLoginModule.java | 33 ++++---- changelog.txt | 7 ++ .../acegisecurity/AcegiSecurityException.java | 2 +- .../web/SecurityEnforcementFilter.java | 84 ++++++++++++++----- .../AuthenticationProcessingFilter.java | 82 +++++++++++++----- .../web/SecurityEnforcementFilterTests.java | 9 +- .../AuthenticationProcessingFilterTests.java | 9 +- test/.cvsignore | 2 + upgrade-04-05.txt | 23 +++++ 9 files changed, 185 insertions(+), 66 deletions(-) create mode 100644 test/.cvsignore create mode 100644 upgrade-04-05.txt diff --git a/adapters/jboss/src/main/java/org/acegisecurity/adapters/jboss/JbossAcegiLoginModule.java b/adapters/jboss/src/main/java/org/acegisecurity/adapters/jboss/JbossAcegiLoginModule.java index 2c592010dd..12704b7aaf 100644 --- a/adapters/jboss/src/main/java/org/acegisecurity/adapters/jboss/JbossAcegiLoginModule.java +++ b/adapters/jboss/src/main/java/org/acegisecurity/adapters/jboss/JbossAcegiLoginModule.java @@ -12,6 +12,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package net.sf.acegisecurity.adapters.jboss; import net.sf.acegisecurity.Authentication; @@ -44,7 +45,7 @@ import javax.security.auth.login.LoginException; /** * Adapter to enable JBoss to authenticate via the Acegi Security System for * Spring. - * + * *

* Returns a {@link PrincipalAcegiUserToken} to JBoss' authentication system, * which is subsequently available from @@ -55,11 +56,15 @@ import javax.security.auth.login.LoginException; * @version $Id$ */ public class JbossAcegiLoginModule extends AbstractServerLoginModule { + //~ Instance fields ======================================================== + private AuthenticationManager authenticationManager; private Principal identity; private String key; private char[] credential; + //~ Methods ================================================================ + public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) { super.initialize(subject, callbackHandler, sharedState, options); @@ -78,8 +83,8 @@ public class JbossAcegiLoginModule extends AbstractServerLoginModule { } if (Thread.currentThread().getContextClassLoader().getResource(appContextLocation) == null) { - throw new IllegalArgumentException("Cannot locate " + - appContextLocation); + throw new IllegalArgumentException("Cannot locate " + + appContextLocation); } ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(appContextLocation); @@ -104,8 +109,8 @@ public class JbossAcegiLoginModule extends AbstractServerLoginModule { if ((username == null) && (password == null)) { identity = null; - super.log.trace("Authenticating as unauthenticatedIdentity=" + - identity); + super.log.trace("Authenticating as unauthenticatedIdentity=" + + identity); } if (username == null) { @@ -145,8 +150,8 @@ public class JbossAcegiLoginModule extends AbstractServerLoginModule { } super.loginOk = true; - super.log.trace("User '" + identity + "' authenticated, loginOk=" + - loginOk); + super.log.trace("User '" + identity + "' authenticated, loginOk=" + + loginOk); return true; } @@ -157,7 +162,7 @@ public class JbossAcegiLoginModule extends AbstractServerLoginModule { protected Group[] getRoleSets() throws LoginException { SimpleGroup roles = new SimpleGroup("Roles"); - Group[] roleSets = { roles }; + Group[] roleSets = {roles}; if (this.identity instanceof Authentication) { Authentication user = (Authentication) this.identity; @@ -172,17 +177,17 @@ public class JbossAcegiLoginModule extends AbstractServerLoginModule { } protected String[] getUsernameAndPassword() throws LoginException { - String[] info = { null, null }; + String[] info = {null, null}; // prompt for a username and password if (callbackHandler == null) { - throw new LoginException("Error: no CallbackHandler available " + - "to collect authentication information"); + throw new LoginException("Error: no CallbackHandler available " + + "to collect authentication information"); } NameCallback nc = new NameCallback("User name: ", "guest"); PasswordCallback pc = new PasswordCallback("Password: ", false); - Callback[] callbacks = { nc, pc }; + Callback[] callbacks = {nc, pc}; String username = null; String password = null; @@ -202,8 +207,8 @@ public class JbossAcegiLoginModule extends AbstractServerLoginModule { } catch (java.io.IOException ioe) { throw new LoginException(ioe.toString()); } catch (UnsupportedCallbackException uce) { - throw new LoginException("CallbackHandler does not support: " + - uce.getCallback()); + throw new LoginException("CallbackHandler does not support: " + + uce.getCallback()); } info[0] = username; diff --git a/changelog.txt b/changelog.txt index 98d1560cc9..369f4875e2 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,10 @@ +Changes in version 0.5 (2004-xx-xx) +----------------------------------- +* AuthenticationProcessingFilter by default finds configuration context using Spring's WebApplicationContextUtils.getWebApplicationContext() +* AuthenticationProcessingFilter context may optionally be specified with 'contextConfigLocation' param (was previously 'appContextLocation') +* SecurityEnforcementFilter by default finds configuration context using Spring's WebApplicationContextUtils.getWebApplicationContext() +* SecurityEnforcementFilter context may optionally be specified with 'contextConfigLocation' param (was previously 'appContextLocation') + Changes in version 0.4 (2004-04-03) ----------------------------------- diff --git a/core/src/main/java/org/acegisecurity/AcegiSecurityException.java b/core/src/main/java/org/acegisecurity/AcegiSecurityException.java index c17d5b691a..af7f7d43f6 100644 --- a/core/src/main/java/org/acegisecurity/AcegiSecurityException.java +++ b/core/src/main/java/org/acegisecurity/AcegiSecurityException.java @@ -21,7 +21,7 @@ import org.springframework.core.NestedRuntimeException; /** * Abstract superclass for all exceptions thrown in the security package and * subpackages. - * + * *

* Note that this is a runtime (unchecked) exception. Security exceptions are * usually fatal; there is no reason for them to be checked. diff --git a/core/src/main/java/org/acegisecurity/intercept/web/SecurityEnforcementFilter.java b/core/src/main/java/org/acegisecurity/intercept/web/SecurityEnforcementFilter.java index c6d7b24dfc..86c6307cdb 100644 --- a/core/src/main/java/org/acegisecurity/intercept/web/SecurityEnforcementFilter.java +++ b/core/src/main/java/org/acegisecurity/intercept/web/SecurityEnforcementFilter.java @@ -22,8 +22,12 @@ import net.sf.acegisecurity.ui.webapp.AuthenticationProcessingFilter; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.web.context.support.WebApplicationContextUtils; + import java.io.IOException; import java.util.Map; @@ -41,12 +45,12 @@ import javax.servlet.http.HttpServletResponse; /** * Wraps requests to the {@link FilterSecurityInterceptor}. * - *

- * This filter is necessary because it provides an application context - * environment for the FilterSecurityInterceptor instance. + *

+ * This filter is necessary because it provides the bridge between incoming + * requests and the FilterSecurityInterceptor instance. *

* - *

+ *

* If a {@link AuthenticationException} is detected, the filter will redirect * to the loginFormUrl. This allows common handling of * authentication failures originating from any subclass of {@link @@ -60,33 +64,55 @@ import javax.servlet.http.HttpServletResponse; * security interceptor. *

* - *

+ *

+ * This filter works with a FilterSecurityInterceptor instance. By + * default, at init time, the filter will use Spring's {@link + * WebApplicationContextUtils#getWebApplicationContext(ServletContext sc)} + * method to obtain an ApplicationContext instance, inside which must be a + * configured FilterSecurityInterceptor instance. In the case + * where it is desireable for this filter to instantiate its own + * ApplicationContext instance from which to obtain the + * FilterSecurityInterceptor, the location of the config for this + * context may be specified with the optional + * contextConfigLocation init param. + *

+ * + *

* To use this filter, it is necessary to specify the following filter * initialization parameters: + *

* * - *

+ * * * @author Ben Alex + * @author colin sampaleanu * @version $Id$ */ public class SecurityEnforcementFilter implements Filter { //~ Static fields/initializers ============================================= + /** + * Name of (optional) servlet filter parameter that can specify the config + * location for a new ApplicationContext used to config this filter. + */ + public static final String CONFIG_LOCATION_PARAM = "contextConfigLocation"; private static final Log logger = LogFactory.getLog(SecurityEnforcementFilter.class); //~ Instance fields ======================================================== - protected ClassPathXmlApplicationContext ctx; protected FilterSecurityInterceptor securityInterceptor; /** @@ -94,11 +120,15 @@ public class SecurityEnforcementFilter implements Filter { * AuthenticationException is detected. */ protected String loginFormUrl; + private ApplicationContext ctx; + private boolean ourContext = false; //~ Methods ================================================================ public void destroy() { - ctx.close(); + if (ourContext && ctx instanceof ConfigurableApplicationContext) { + ((ConfigurableApplicationContext) ctx).close(); + } } public void doFilter(ServletRequest request, ServletResponse response, @@ -145,15 +175,15 @@ public class SecurityEnforcementFilter implements Filter { } public void init(FilterConfig filterConfig) throws ServletException { - String appContextLocation = filterConfig.getInitParameter( - "appContextLocation"); + String appContextLocation = filterConfig.getInitParameter(CONFIG_LOCATION_PARAM); - if ((appContextLocation == null) || "".equals(appContextLocation)) { - throw new ServletException("appContextLocation must be specified"); - } + if ((appContextLocation != null) && (appContextLocation.length() > 0)) { + ourContext = true; - if (Thread.currentThread().getContextClassLoader().getResource(appContextLocation) == null) { - throw new ServletException("Cannot locate " + appContextLocation); + if (Thread.currentThread().getContextClassLoader().getResource(appContextLocation) == null) { + throw new ServletException("Cannot locate " + + appContextLocation); + } } loginFormUrl = filterConfig.getInitParameter("loginFormUrl"); @@ -162,7 +192,21 @@ public class SecurityEnforcementFilter implements Filter { throw new ServletException("loginFormUrl must be specified"); } - ctx = new ClassPathXmlApplicationContext(appContextLocation); + try { + if (!ourContext) { + ctx = WebApplicationContextUtils + .getRequiredWebApplicationContext(filterConfig + .getServletContext()); + } else { + ctx = new ClassPathXmlApplicationContext(appContextLocation); + } + } catch (RuntimeException e) { + throw new ServletException( + "Error obtaining/creating ApplicationContext for config. Must be stored in ServletContext, or optionally '" + + CONFIG_LOCATION_PARAM + + "' param may be used to allow creation of new context by this filter. See root error for additional details", + e); + } Map beans = ctx.getBeansOfType(FilterSecurityInterceptor.class, true, true); diff --git a/core/src/main/java/org/acegisecurity/ui/webapp/AuthenticationProcessingFilter.java b/core/src/main/java/org/acegisecurity/ui/webapp/AuthenticationProcessingFilter.java index c9d4185e59..ba08b40d3e 100644 --- a/core/src/main/java/org/acegisecurity/ui/webapp/AuthenticationProcessingFilter.java +++ b/core/src/main/java/org/acegisecurity/ui/webapp/AuthenticationProcessingFilter.java @@ -23,8 +23,12 @@ import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.web.context.support.WebApplicationContextUtils; + import java.io.IOException; import java.util.Map; @@ -52,30 +56,39 @@ import javax.servlet.http.HttpServletResponse; * HttpSessionIntegrationFilter#ACEGI_SECURITY_AUTHENTICATION_KEY}. *

* - *

+ *

* Login forms must present two parameters to this filter: a username and * password. The filter will process the login against the authentication * environment that was configured from a Spring application context defined * in the filter initialization. *

* - *

+ *

* If authentication fails, the AuthenticationException will be * placed into the HttpSession with the attribute defined by * {@link #ACEGI_SECURITY_LAST_EXCEPTION_KEY}. *

* - *

+ *

+ * This filter works with an {@link AuthenticationManager} which is used to + * process each authentication request. By default, at init time, the filter + * will use Spring's {@link + * WebApplicationContextUtils#getWebApplicationContext(ServletContext sc)} + * method to obtain an ApplicationContext instance, inside which must be a + * configured AuthenticationManager instance. In the case where it is + * desireable for this filter to instantiate its own ApplicationContext + * instance from which to obtain the AuthenticationManager, the location of + * the config for this context may be specified with the optional + * appContextLocation init param. + *

+ * + *

* To use this filter, it is necessary to specify the following filter * initialization parameters: + *

* * - *

+ * * * @author Ben Alex + * @author colin sampaleanu * @version $Id$ */ public class AuthenticationProcessingFilter implements Filter { //~ Static fields/initializers ============================================= + /** + * Name of (optional) servlet filter parameter that can specify the config + * location for a new ApplicationContext used to config this filter. + */ + public static final String CONFIG_LOCATION_PARAM = "appContextLocation"; public static final String ACEGI_SECURITY_TARGET_URL_KEY = "ACEGI_SECURITY_TARGET_URL"; public static final String ACEGI_SECURITY_FORM_USERNAME_KEY = "j_username"; public static final String ACEGI_SECURITY_FORM_PASSWORD_KEY = "j_password"; @@ -108,8 +135,8 @@ public class AuthenticationProcessingFilter implements Filter { //~ Instance fields ======================================================== + private ApplicationContext ctx; private AuthenticationManager authenticationManager; - private ClassPathXmlApplicationContext ctx; /** Where to redirect the browser to if authentication fails */ private String authenticationFailureUrl; @@ -125,11 +152,14 @@ public class AuthenticationProcessingFilter implements Filter { * /j_acegi_security_check) */ private String filterProcessesUrl; + private boolean ourContext = false; //~ Methods ================================================================ public void destroy() { - ctx.close(); + if (ourContext && ctx instanceof ConfigurableApplicationContext) { + ((ConfigurableApplicationContext) ctx).close(); + } } public void doFilter(ServletRequest request, ServletResponse response, @@ -216,15 +246,15 @@ public class AuthenticationProcessingFilter implements Filter { } public void init(FilterConfig filterConfig) throws ServletException { - String appContextLocation = filterConfig.getInitParameter( - "appContextLocation"); + String appContextLocation = filterConfig.getInitParameter(CONFIG_LOCATION_PARAM); - if ((appContextLocation == null) || "".equals(appContextLocation)) { - throw new ServletException("appContextLocation must be specified"); - } + if ((appContextLocation != null) && (appContextLocation.length() > 0)) { + ourContext = true; - if (Thread.currentThread().getContextClassLoader().getResource(appContextLocation) == null) { - throw new ServletException("Cannot locate " + appContextLocation); + if (Thread.currentThread().getContextClassLoader().getResource(appContextLocation) == null) { + throw new ServletException("Cannot locate " + + appContextLocation); + } } defaultTargetUrl = filterConfig.getInitParameter("defaultTargetUrl"); @@ -248,7 +278,21 @@ public class AuthenticationProcessingFilter implements Filter { filterProcessesUrl = "/j_acegi_security_check"; } - ctx = new ClassPathXmlApplicationContext(appContextLocation); + try { + if (!ourContext) { + ctx = WebApplicationContextUtils + .getRequiredWebApplicationContext(filterConfig + .getServletContext()); + } else { + ctx = new ClassPathXmlApplicationContext(appContextLocation); + } + } catch (RuntimeException e) { + throw new ServletException( + "Error obtaining/creating ApplicationContext for config. Must be stored in ServletContext, or optionally '" + + CONFIG_LOCATION_PARAM + + "' param may be used to allow creation of new context by this filter. See root error for additional details", + e); + } Map beans = ctx.getBeansOfType(AuthenticationManager.class, true, true); diff --git a/core/src/test/java/org/acegisecurity/intercept/web/SecurityEnforcementFilterTests.java b/core/src/test/java/org/acegisecurity/intercept/web/SecurityEnforcementFilterTests.java index a32dcd3700..1aacc9364a 100644 --- a/core/src/test/java/org/acegisecurity/intercept/web/SecurityEnforcementFilterTests.java +++ b/core/src/test/java/org/acegisecurity/intercept/web/SecurityEnforcementFilterTests.java @@ -150,8 +150,7 @@ public class SecurityEnforcementFilterTests extends TestCase { } } - public void testStartupDetectsMissingAppContextLocation() - throws Exception { + public void testStartupDetectsMissingAppContext() throws Exception { MockFilterConfig config = new MockFilterConfig(); config.setInitParmeter("loginFormUrl", "/login.jsp"); @@ -161,8 +160,7 @@ public class SecurityEnforcementFilterTests extends TestCase { filter.init(config); fail("Should have thrown ServletException"); } catch (ServletException expected) { - assertEquals("appContextLocation must be specified", - expected.getMessage()); + assertTrue(expected.getMessage().startsWith("Error obtaining/creating ApplicationContext for config.")); } config.setInitParmeter("appContextLocation", ""); @@ -171,8 +169,7 @@ public class SecurityEnforcementFilterTests extends TestCase { filter.init(config); fail("Should have thrown ServletException"); } catch (ServletException expected) { - assertEquals("appContextLocation must be specified", - expected.getMessage()); + assertTrue(expected.getMessage().startsWith("Error obtaining/creating ApplicationContext for config.")); } } diff --git a/core/src/test/java/org/acegisecurity/ui/webapp/AuthenticationProcessingFilterTests.java b/core/src/test/java/org/acegisecurity/ui/webapp/AuthenticationProcessingFilterTests.java index 9b024cce48..0992b664cc 100644 --- a/core/src/test/java/org/acegisecurity/ui/webapp/AuthenticationProcessingFilterTests.java +++ b/core/src/test/java/org/acegisecurity/ui/webapp/AuthenticationProcessingFilterTests.java @@ -287,8 +287,7 @@ public class AuthenticationProcessingFilterTests extends TestCase { } } - public void testStartupDetectsMissingAppContextLocation() - throws Exception { + public void testStartupDetectsMissingAppContext() throws Exception { MockFilterConfig config = new MockFilterConfig(); config.setInitParmeter("defaultTargetUrl", "/"); config.setInitParmeter("authenticationFailureUrl", "/failed.jsp"); @@ -299,8 +298,7 @@ public class AuthenticationProcessingFilterTests extends TestCase { filter.init(config); fail("Should have thrown ServletException"); } catch (ServletException expected) { - assertEquals("appContextLocation must be specified", - expected.getMessage()); + assertTrue(expected.getMessage().startsWith("Error obtaining/creating ApplicationContext for config.")); } config.setInitParmeter("appContextLocation", ""); @@ -309,8 +307,7 @@ public class AuthenticationProcessingFilterTests extends TestCase { filter.init(config); fail("Should have thrown ServletException"); } catch (ServletException expected) { - assertEquals("appContextLocation must be specified", - expected.getMessage()); + assertTrue(expected.getMessage().startsWith("Error obtaining/creating ApplicationContext for config.")); } } diff --git a/test/.cvsignore b/test/.cvsignore new file mode 100644 index 0000000000..91696b3e12 --- /dev/null +++ b/test/.cvsignore @@ -0,0 +1,2 @@ +acegisecuritytest.properties +acegisecuritytest.script diff --git a/upgrade-04-05.txt b/upgrade-04-05.txt new file mode 100644 index 0000000000..2a8c240054 --- /dev/null +++ b/upgrade-04-05.txt @@ -0,0 +1,23 @@ +=============================================================================== + ACEGI SECURITY SYSTEM FOR SPRING - UPGRADING FROM 0.4 TO 0.5 +=============================================================================== + +Unfortunately, changes to the API and package locations were required. The +following should help most casual users of the project update their +applications: + +- By default, AuthenticationProcessingFilter and SecurityEnforcementFilter now + use Spring's WebApplicationContextUtils.getApplicationContext to load the + ApplicationContext in which their respective configs may be found. Ideally, + move your configuration for these filters from the separate contexts you were + using before, to the main context used by your webapp. Alternately, the old + mechanism of having the filter load its own specific context is still + supported, but the param specifying the location of this context has been + changed to match the equivalent param as used by Spring's ContextLoader class. + If you do still want to use this approach, just rename your param from + 'appContextLocation' to 'contextConfigLocation'. + + +We hope you find the new features useful in your projects. + +$Id$