diff --git a/core/src/main/java/org/springframework/security/concurrent/ConcurrentSessionControllerImpl.java b/core/src/main/java/org/springframework/security/concurrent/ConcurrentSessionControllerImpl.java index b770d230b6..4435509701 100644 --- a/core/src/main/java/org/springframework/security/concurrent/ConcurrentSessionControllerImpl.java +++ b/core/src/main/java/org/springframework/security/concurrent/ConcurrentSessionControllerImpl.java @@ -158,4 +158,8 @@ public class ConcurrentSessionControllerImpl implements ConcurrentSessionControl public void setSessionRegistry(SessionRegistry sessionRegistry) { this.sessionRegistry = sessionRegistry; } + + public SessionRegistry getSessionRegistry() { + return sessionRegistry; + } } diff --git a/core/src/main/java/org/springframework/security/config/AuthenticationManagerBeanDefinitionParser.java b/core/src/main/java/org/springframework/security/config/AuthenticationManagerBeanDefinitionParser.java index 7176bbc3ca..4533d8cc0e 100644 --- a/core/src/main/java/org/springframework/security/config/AuthenticationManagerBeanDefinitionParser.java +++ b/core/src/main/java/org/springframework/security/config/AuthenticationManagerBeanDefinitionParser.java @@ -1,5 +1,6 @@ package org.springframework.security.config; +import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.beans.factory.xml.BeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.beans.factory.config.BeanDefinition; @@ -36,10 +37,15 @@ public class AuthenticationManagerBeanDefinitionParser implements BeanDefinition BeanIds.CONCURRENT_SESSION_CONTROLLER, element); authManager.getPropertyValues().addPropertyValue("sessionController", new RuntimeBeanReference(sessionControllerRef)); + RootBeanDefinition sessionRegistryInjector = new RootBeanDefinition(SessionRegistryInjectionBeanPostProcessor.class); + sessionRegistryInjector.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); + sessionRegistryInjector.getConstructorArgumentValues().addGenericArgumentValue(sessionControllerRef); + + parserContext.getRegistry().registerBeanDefinition(BeanIds.SESSION_REGISTRY_INJECTION_POST_PROCESSOR, sessionRegistryInjector); } parserContext.getRegistry().registerAlias(BeanIds.AUTHENTICATION_MANAGER, alias); return null; - } + } } diff --git a/core/src/main/java/org/springframework/security/config/BeanIds.java b/core/src/main/java/org/springframework/security/config/BeanIds.java index 7b029581d2..6160bcea63 100644 --- a/core/src/main/java/org/springframework/security/config/BeanIds.java +++ b/core/src/main/java/org/springframework/security/config/BeanIds.java @@ -18,6 +18,7 @@ public abstract class BeanIds { static final String CONTEXT_SOURCE_SETTING_POST_PROCESSOR = "_contextSettingPostProcessor"; static final String ENTRY_POINT_INJECTION_POST_PROCESSOR = "_entryPointInjectionBeanPostProcessor"; static final String USER_DETAILS_SERVICE_INJECTION_POST_PROCESSOR = "_userServiceInjectionPostProcessor"; + static final String SESSION_REGISTRY_INJECTION_POST_PROCESSOR = "_sessionRegistryInjectionPostProcessor"; static final String FILTER_CHAIN_POST_PROCESSOR = "_filterChainProxyPostProcessor"; static final String FILTER_LIST = "_filterChainList"; diff --git a/core/src/main/java/org/springframework/security/config/SessionRegistryInjectionBeanPostProcessor.java b/core/src/main/java/org/springframework/security/config/SessionRegistryInjectionBeanPostProcessor.java new file mode 100644 index 0000000000..8cb1744796 --- /dev/null +++ b/core/src/main/java/org/springframework/security/config/SessionRegistryInjectionBeanPostProcessor.java @@ -0,0 +1,94 @@ +package org.springframework.security.config; + +import java.util.ArrayList; +import java.util.List; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryAware; +import org.springframework.beans.factory.ListableBeanFactory; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.security.concurrent.ConcurrentSessionController; +import org.springframework.security.concurrent.ConcurrentSessionControllerImpl; +import org.springframework.security.concurrent.SessionRegistry; +import org.springframework.security.ui.AbstractProcessingFilter; +import org.springframework.security.ui.SessionFixationProtectionFilter; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Registered by the AuthenticationManagerBeanDefinitionParser if an external + * ConcurrentSessionController is set (and hence an external SessionRegistry). + * Its responsibility is to set the SessionRegistry on namespace-registered beans which require access + * to it. + *

+ * It will attempt to read the registry directly from the registered controller. If that fails, it will look in + * the application context for a registered SessionRegistry bean. + * + * See SEC-879. + * + * @author Luke Taylor + * @since 2.0.3 + */ +class SessionRegistryInjectionBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware { + private final Log logger = LogFactory.getLog(getClass()); + private ListableBeanFactory beanFactory; + private SessionRegistry sessionRegistry; + private final String controllerBeanName; + + SessionRegistryInjectionBeanPostProcessor(String controllerBeanName) { + this.controllerBeanName = controllerBeanName; + } + + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + if (BeanIds.FORM_LOGIN_FILTER.equals(beanName) || + BeanIds.OPEN_ID_FILTER.equals(beanName)) { + ((AbstractProcessingFilter) bean).setSessionRegistry(getSessionRegistry()); + } else if (BeanIds.SESSION_FIXATION_PROTECTION_FILTER.equals(beanName)) { + ((SessionFixationProtectionFilter)bean).setSessionRegistry(getSessionRegistry()); + } + + return bean; + } + + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + return bean; + } + + private SessionRegistry getSessionRegistry() { + if (sessionRegistry != null) { + return sessionRegistry; + } + + logger.info("Attempting to read SessionRegistry from registered ConcurrentSessionController bean"); + + ConcurrentSessionController controller = (ConcurrentSessionController) beanFactory.getBean(controllerBeanName); + + if (controller instanceof ConcurrentSessionControllerImpl) { + sessionRegistry = ((ConcurrentSessionControllerImpl)controller).getSessionRegistry(); + + return sessionRegistry; + } + + logger.info("ConcurrentSessionController is not a standard implementation. SessionRegistry could not be read from it. Looking for it in the context."); + + List sessionRegs = new ArrayList(beanFactory.getBeansOfType(SessionRegistry.class).values()); + + if (sessionRegs.size() == 0) { + throw new SecurityConfigurationException("concurrent-session-controller-ref was set but no SessionRegistry could be obtained from the application context."); + } + + if (sessionRegs.size() > 1) { + logger.warn("More than one SessionRegistry instance in application context. Possible configuration errors may result."); + } + + sessionRegistry = (SessionRegistry) sessionRegs.get(0); + + return sessionRegistry; + } + + public void setBeanFactory(BeanFactory beanFactory) throws BeansException { + this.beanFactory = (ListableBeanFactory) beanFactory; + } +} diff --git a/core/src/test/java/org/springframework/security/config/SessionRegistryInjectionBeanPostProcessorTests.java b/core/src/test/java/org/springframework/security/config/SessionRegistryInjectionBeanPostProcessorTests.java new file mode 100644 index 0000000000..f0c1554e0c --- /dev/null +++ b/core/src/test/java/org/springframework/security/config/SessionRegistryInjectionBeanPostProcessorTests.java @@ -0,0 +1,66 @@ +package org.springframework.security.config; + +import static org.junit.Assert.*; + +import org.junit.After; +import org.junit.Test; +import org.springframework.context.support.AbstractXmlApplicationContext; +import org.springframework.security.Authentication; +import org.springframework.security.AuthenticationException; +import org.springframework.security.concurrent.ConcurrentSessionController; +import org.springframework.security.util.FieldUtils; +import org.springframework.security.util.InMemoryXmlApplicationContext; + +/** + * + * @author Luke Taylor + */ +public class SessionRegistryInjectionBeanPostProcessorTests { + private AbstractXmlApplicationContext appContext; + + @After + public void closeAppContext() { + if (appContext != null) { + appContext.close(); + appContext = null; + } + } + + private void setContext(String context) { + appContext = new InMemoryXmlApplicationContext(context); + } + + @Test + public void sessionRegistryIsSetOnFiltersWhenUsingCustomControllerWithInternalRegistryBean() throws Exception { + setContext( + "" + + "" + + " " + + " " + + " " + + "" + + "" + + HttpSecurityBeanDefinitionParserTests.AUTH_PROVIDER_XML); + assertNotNull(FieldUtils.getFieldValue(appContext.getBean(BeanIds.SESSION_FIXATION_PROTECTION_FILTER), "sessionRegistry")); + assertNotNull(FieldUtils.getFieldValue(appContext.getBean(BeanIds.FORM_LOGIN_FILTER), "sessionRegistry")); + } + + @Test + public void sessionRegistryIsSetOnFiltersWhenUsingCustomControllerWithNonStandardController() throws Exception { + setContext( + "" + + "" + + "" + + "" + + HttpSecurityBeanDefinitionParserTests.AUTH_PROVIDER_XML); + assertNotNull(FieldUtils.getFieldValue(appContext.getBean(BeanIds.SESSION_FIXATION_PROTECTION_FILTER), "sessionRegistry")); + assertNotNull(FieldUtils.getFieldValue(appContext.getBean(BeanIds.FORM_LOGIN_FILTER), "sessionRegistry")); + } + + public static class MockConcurrentSessionController implements ConcurrentSessionController { + public void checkAuthenticationAllowed(Authentication request) throws AuthenticationException { + } + public void registerSuccessfulAuthentication(Authentication authentication) { + } + } +}