From ebada9fd124fb8d873171840f45f26a258c934f3 Mon Sep 17 00:00:00 2001 From: Luke Taylor Date: Tue, 29 Sep 2009 16:17:05 +0000 Subject: [PATCH] SEC-1229: Added support for parsing error URL in session-management --- ...oncurrentSessionsBeanDefinitionParser.java | 72 ------------------- .../config/http/HttpConfigurationBuilder.java | 23 ++++-- ...HttpSecurityBeanDefinitionParserTests.java | 30 +++----- 3 files changed, 27 insertions(+), 98 deletions(-) delete mode 100644 config/src/main/java/org/springframework/security/config/http/ConcurrentSessionsBeanDefinitionParser.java diff --git a/config/src/main/java/org/springframework/security/config/http/ConcurrentSessionsBeanDefinitionParser.java b/config/src/main/java/org/springframework/security/config/http/ConcurrentSessionsBeanDefinitionParser.java deleted file mode 100644 index 73cdfe0923..0000000000 --- a/config/src/main/java/org/springframework/security/config/http/ConcurrentSessionsBeanDefinitionParser.java +++ /dev/null @@ -1,72 +0,0 @@ -package org.springframework.security.config.http; - -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.parsing.BeanComponentDefinition; -import org.springframework.beans.factory.parsing.CompositeComponentDefinition; -import org.springframework.beans.factory.support.BeanDefinitionBuilder; -import org.springframework.beans.factory.support.BeanDefinitionRegistry; -import org.springframework.beans.factory.support.RootBeanDefinition; -import org.springframework.beans.factory.xml.BeanDefinitionParser; -import org.springframework.beans.factory.xml.ParserContext; -import org.springframework.security.authentication.ProviderManager; -import org.springframework.security.authentication.concurrent.ConcurrentSessionControllerImpl; -import org.springframework.security.authentication.concurrent.SessionRegistryImpl; -import org.springframework.security.web.authentication.concurrent.ConcurrentSessionFilter; -import org.springframework.util.StringUtils; -import org.w3c.dom.Element; - -/** - * Sets up support for concurrent session support control, creating {@link ConcurrentSessionFilter}, - * {@link SessionRegistryImpl} and {@link ConcurrentSessionControllerImpl}. The session controller is also registered - * with the default {@link ProviderManager} (which is automatically registered during namespace configuration). - * - * @author Luke Taylor - * @version $Id$ - */ -public class ConcurrentSessionsBeanDefinitionParser implements BeanDefinitionParser { - - static final String ATT_EXPIRY_URL = "expired-url"; - static final String ATT_SESSION_REGISTRY_ALIAS = "session-registry-alias"; - static final String ATT_SESSION_REGISTRY_REF = "session-registry-ref"; - - public BeanDefinition parse(Element element, ParserContext pc) { - CompositeComponentDefinition compositeDef = - new CompositeComponentDefinition(element.getTagName(), pc.extractSource(element)); - pc.pushContainingComponent(compositeDef); - - BeanDefinitionRegistry beanRegistry = pc.getRegistry(); - - String sessionRegistryId = element.getAttribute(ATT_SESSION_REGISTRY_REF); - - if (!StringUtils.hasText(sessionRegistryId)) { - // Register an internal SessionRegistryImpl if no external reference supplied. - RootBeanDefinition sessionRegistry = new RootBeanDefinition(SessionRegistryImpl.class); - sessionRegistryId = pc.getReaderContext().registerWithGeneratedName(sessionRegistry); - pc.registerComponent(new BeanComponentDefinition(sessionRegistry, sessionRegistryId)); - } - - String registryAlias = element.getAttribute(ATT_SESSION_REGISTRY_ALIAS); - if (StringUtils.hasText(registryAlias)) { - beanRegistry.registerAlias(sessionRegistryId, registryAlias); - } - - BeanDefinitionBuilder filterBuilder = - BeanDefinitionBuilder.rootBeanDefinition(ConcurrentSessionFilter.class); - filterBuilder.addPropertyReference("sessionRegistry", sessionRegistryId); - - Object source = pc.extractSource(element); - filterBuilder.getRawBeanDefinition().setSource(source); - filterBuilder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); - - String expiryUrl = element.getAttribute(ATT_EXPIRY_URL); - - if (StringUtils.hasText(expiryUrl)) { - WebConfigUtils.validateHttpRedirect(expiryUrl, pc, source); - filterBuilder.addPropertyValue("expiredUrl", expiryUrl); - } - - pc.popAndRegisterContainingComponent(); - - return filterBuilder.getBeanDefinition(); - } -} diff --git a/config/src/main/java/org/springframework/security/config/http/HttpConfigurationBuilder.java b/config/src/main/java/org/springframework/security/config/http/HttpConfigurationBuilder.java index 49d838d6bc..7b94090abf 100644 --- a/config/src/main/java/org/springframework/security/config/http/HttpConfigurationBuilder.java +++ b/config/src/main/java/org/springframework/security/config/http/HttpConfigurationBuilder.java @@ -37,6 +37,7 @@ import org.springframework.security.web.access.expression.WebExpressionVoter; import org.springframework.security.web.access.intercept.DefaultFilterInvocationSecurityMetadataSource; import org.springframework.security.web.access.intercept.FilterSecurityInterceptor; import org.springframework.security.web.access.intercept.RequestKey; +import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; import org.springframework.security.web.authentication.concurrent.ConcurrentSessionFilter; import org.springframework.security.web.context.HttpSessionSecurityContextRepository; import org.springframework.security.web.context.SecurityContextPersistenceFilter; @@ -192,7 +193,7 @@ class HttpConfigurationBuilder { void createSessionManagementFilters() { Element sessionMgmtElt = DomUtils.getChildElementByTagName(httpElt, Elements.SESSION_MANAGEMENT); - Element sessionCtrlEt = null; + Element sessionCtrlElt = null; String sessionFixationAttribute = null; String invalidSessionUrl = null; @@ -200,10 +201,10 @@ class HttpConfigurationBuilder { if (sessionMgmtElt != null) { sessionFixationAttribute = sessionMgmtElt.getAttribute(ATT_SESSION_FIXATION_PROTECTION); invalidSessionUrl = sessionMgmtElt.getAttribute(ATT_INVALID_SESSION_URL); - sessionCtrlEt = DomUtils.getChildElementByTagName(sessionMgmtElt, Elements.CONCURRENT_SESSIONS); + sessionCtrlElt = DomUtils.getChildElementByTagName(sessionMgmtElt, Elements.CONCURRENT_SESSIONS); - if (sessionCtrlEt != null) { - createConcurrencyControlFilterAndSessionRegistry(sessionCtrlEt); + if (sessionCtrlElt != null) { + createConcurrencyControlFilterAndSessionRegistry(sessionCtrlElt); } } @@ -214,22 +215,25 @@ class HttpConfigurationBuilder { boolean sessionFixationProtectionRequired = !sessionFixationAttribute.equals(OPT_SESSION_FIXATION_NO_PROTECTION); BeanDefinitionBuilder sessionStrategy; + String concurrencyErrorUrl = null; - if (sessionCtrlEt != null) { + if (sessionCtrlElt != null) { assert sessionRegistryRef != null; sessionStrategy = BeanDefinitionBuilder.rootBeanDefinition(ConcurrentSessionControlAuthenticatedSessionStrategy.class); sessionStrategy.addConstructorArgValue(sessionRegistryRef); - String maxSessions = sessionCtrlEt.getAttribute("max-sessions"); + String maxSessions = sessionCtrlElt.getAttribute("max-sessions"); if (StringUtils.hasText(maxSessions)) { sessionStrategy.addPropertyValue("maximumSessions", maxSessions); } - String exceptionIfMaximumExceeded = sessionCtrlEt.getAttribute("exception-if-maximum-exceeded"); + String exceptionIfMaximumExceeded = sessionCtrlElt.getAttribute("error-if-maximum-exceeded"); if (StringUtils.hasText(exceptionIfMaximumExceeded)) { sessionStrategy.addPropertyValue("exceptionIfMaximumExceeded", exceptionIfMaximumExceeded); + + concurrencyErrorUrl = sessionCtrlElt.getAttribute("error-url"); } } else if (sessionFixationProtectionRequired || StringUtils.hasText(invalidSessionUrl)) { sessionStrategy = BeanDefinitionBuilder.rootBeanDefinition(DefaultSessionAuthenticationStrategy.class); @@ -239,6 +243,11 @@ class HttpConfigurationBuilder { } BeanDefinitionBuilder sessionMgmtFilter = BeanDefinitionBuilder.rootBeanDefinition(SessionManagementFilter.class); + RootBeanDefinition failureHandler = new RootBeanDefinition(SimpleUrlAuthenticationFailureHandler.class); + if (StringUtils.hasText(concurrencyErrorUrl)) { + failureHandler.getPropertyValues().addPropertyValue("defaultFailureUrl", concurrencyErrorUrl); + } + sessionMgmtFilter.addPropertyValue("authenticationFailureHandler", failureHandler); sessionMgmtFilter.addConstructorArgValue(contextRepoRef); BeanDefinition strategyBean = sessionStrategy.getBeanDefinition(); diff --git a/config/src/test/java/org/springframework/security/config/http/HttpSecurityBeanDefinitionParserTests.java b/config/src/test/java/org/springframework/security/config/http/HttpSecurityBeanDefinitionParserTests.java index 4d1571a766..1f9f49f6fa 100644 --- a/config/src/test/java/org/springframework/security/config/http/HttpSecurityBeanDefinitionParserTests.java +++ b/config/src/test/java/org/springframework/security/config/http/HttpSecurityBeanDefinitionParserTests.java @@ -27,7 +27,6 @@ import org.springframework.security.access.ConfigAttribute; import org.springframework.security.access.SecurityConfig; import org.springframework.security.authentication.TestingAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.authentication.concurrent.ConcurrentLoginException; import org.springframework.security.authentication.concurrent.SessionRegistryImpl; import org.springframework.security.config.BeanIds; import org.springframework.security.config.PostProcessedMockUserDetailsService; @@ -71,7 +70,6 @@ import org.springframework.security.web.context.HttpSessionSecurityContextReposi import org.springframework.security.web.context.SecurityContextPersistenceFilter; import org.springframework.security.web.savedrequest.HttpSessionRequestCache; import org.springframework.security.web.savedrequest.RequestCacheAwareFilter; -import org.springframework.security.web.session.SessionAuthenticationStrategy; import org.springframework.security.web.session.SessionManagementFilter; import org.springframework.security.web.wrapper.SecurityContextHolderAwareRequestFilter; import org.springframework.util.ReflectionUtils; @@ -704,33 +702,27 @@ public class HttpSecurityBeanDefinitionParserTests { assertSame(sessionRegistry, sessionRegistryFromFormLoginFilter); } - @Test(expected=ConcurrentLoginException.class) + @Test public void concurrentSessionMaxSessionsIsCorrectlyConfigured() throws Exception { setContext( "" + " " + - " " + + " " + " " + "" + AUTH_PROVIDER_XML); - SessionAuthenticationStrategy seshStrategy = (SessionAuthenticationStrategy) FieldUtils.getFieldValue( - getFilter(SessionManagementFilter.class), "sessionStrategy"); + SessionManagementFilter seshStrategy = (SessionManagementFilter) getFilter(SessionManagementFilter.class); UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken("bob", "pass"); + SecurityContextHolder.getContext().setAuthentication(auth); // Register 2 sessions and then check a third // req.setSession(new MockHttpSession()); // auth.setDetails(new WebAuthenticationDetails(req)); - try { - seshStrategy.onAuthentication(auth, new MockHttpServletRequest(), new MockHttpServletResponse()); - } catch (ConcurrentLoginException e) { - fail("First login should be allowed"); - } - - try { - seshStrategy.onAuthentication(auth, new MockHttpServletRequest(), new MockHttpServletResponse()); - } catch (ConcurrentLoginException e) { - fail("Second login should be allowed"); - } - - seshStrategy.onAuthentication(auth, new MockHttpServletRequest(), new MockHttpServletResponse()); + MockHttpServletResponse response = new MockHttpServletResponse(); + seshStrategy.doFilter(new MockHttpServletRequest(), response, new MockFilterChain()); + assertNull(response.getRedirectedUrl()); + seshStrategy.doFilter(new MockHttpServletRequest(), response, new MockFilterChain()); + assertNull(response.getRedirectedUrl()); + seshStrategy.doFilter(new MockHttpServletRequest(), response, new MockFilterChain()); + assertEquals("/max-exceeded", response.getRedirectedUrl()); } @Test