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 295f3225f4..347589d8b3 100644 --- a/core/src/main/java/org/springframework/security/config/BeanIds.java +++ b/core/src/main/java/org/springframework/security/config/BeanIds.java @@ -1,5 +1,7 @@ package org.springframework.security.config; +import org.springframework.beans.factory.config.BeanDefinition; + /** * Contains all the default Bean IDs created by the namespace support in Spring Security 2. *

@@ -31,6 +33,7 @@ public abstract class BeanIds { public static final String CONCURRENT_SESSION_CONTROLLER = "_concurrentSessionController"; public static final String ACCESS_MANAGER = "_accessManager"; public static final String AUTHENTICATION_MANAGER = "_authenticationManager"; + public static final String AFTER_INVOCATION_MANAGER = "_afterInvocationManager"; public static final String FORM_LOGIN_FILTER = "_formLoginFilter"; public static final String FORM_LOGIN_ENTRY_POINT = "_formLoginEntryPoint"; public static final String OPEN_ID_FILTER = "_openIDFilter"; @@ -50,6 +53,7 @@ public abstract class BeanIds { public static final String SECURITY_CONTEXT_HOLDER_AWARE_REQUEST_FILTER = "_securityContextHolderAwareRequestFilter"; public static final String SESSION_FIXATION_PROTECTION_FILTER = "_sessionFixationProtectionFilter"; public static final String METHOD_SECURITY_INTERCEPTOR = "_methodSecurityInterceptor"; + public static final String METHOD_SECURITY_INTERCEPTOR_POST_PROCESSOR = "_methodSecurityInterceptorPostProcessor"; public static final String METHOD_DEFINITION_SOURCE_ADVISOR = "_methodDefinitionSourceAdvisor"; public static final String PROTECT_POINTCUT_POST_PROCESSOR = "_protectPointcutPostProcessor"; public static final String DELEGATING_METHOD_DEFINITION_SOURCE = "_delegatingMethodDefinitionSource"; @@ -62,4 +66,5 @@ public abstract class BeanIds { public static final String X509_AUTH_PROVIDER = "_x509AuthenitcationProvider"; public static final String PRE_AUTH_ENTRY_POINT = "_preAuthenticatedProcessingFilterEntryPoint"; public static final String REMEMBER_ME_SERVICES_INJECTION_POST_PROCESSOR = "_rememberMeServicesInjectionBeanPostProcessor"; + } diff --git a/core/src/main/java/org/springframework/security/config/ConfigUtils.java b/core/src/main/java/org/springframework/security/config/ConfigUtils.java index 491c036d7d..4ed59e7880 100644 --- a/core/src/main/java/org/springframework/security/config/ConfigUtils.java +++ b/core/src/main/java/org/springframework/security/config/ConfigUtils.java @@ -13,6 +13,7 @@ import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.ManagedList; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.security.afterinvocation.AfterInvocationProviderManager; import org.springframework.security.providers.ProviderManager; import org.springframework.security.userdetails.UserDetailsService; import org.springframework.security.vote.AffirmativeBased; @@ -114,7 +115,24 @@ public abstract class ConfigUtils { return (ManagedList) authManager.getPropertyValues().getPropertyValue("providers").getValue(); } - private static void registerFilterChainPostProcessorIfNecessary(ParserContext pc) { + static ManagedList getRegisteredAfterInvocationProviders(ParserContext parserContext) { + BeanDefinition manager = registerAfterInvocationProviderManagerIfNecessary(parserContext); + return (ManagedList) manager.getPropertyValues().getPropertyValue("providers").getValue(); + } + + private static BeanDefinition registerAfterInvocationProviderManagerIfNecessary(ParserContext parserContext) { + if(parserContext.getRegistry().containsBeanDefinition(BeanIds.AFTER_INVOCATION_MANAGER)) { + return parserContext.getRegistry().getBeanDefinition(BeanIds.AFTER_INVOCATION_MANAGER); + } + + BeanDefinition manager = new RootBeanDefinition(AfterInvocationProviderManager.class); + manager.getPropertyValues().addPropertyValue("providers", new ManagedList()); + parserContext.getRegistry().registerBeanDefinition(BeanIds.AFTER_INVOCATION_MANAGER, manager); + + return manager; + } + + private static void registerFilterChainPostProcessorIfNecessary(ParserContext pc) { if (pc.getRegistry().containsBeanDefinition(BeanIds.FILTER_CHAIN_POST_PROCESSOR)) { return; } @@ -158,5 +176,5 @@ public abstract class ConfigUtils { public void setFilters(List filters) { this.filters = filters; } - } + } } diff --git a/core/src/main/java/org/springframework/security/config/CustomAfterInvocationProviderBeanDefinitionDecorator.java b/core/src/main/java/org/springframework/security/config/CustomAfterInvocationProviderBeanDefinitionDecorator.java new file mode 100644 index 0000000000..ab522a0dd7 --- /dev/null +++ b/core/src/main/java/org/springframework/security/config/CustomAfterInvocationProviderBeanDefinitionDecorator.java @@ -0,0 +1,24 @@ +package org.springframework.security.config; + +import org.springframework.beans.factory.config.BeanDefinitionHolder; +import org.springframework.beans.factory.xml.BeanDefinitionDecorator; +import org.springframework.beans.factory.xml.ParserContext; +import org.w3c.dom.Node; + +/** + * Adds the decorated {@link org.springframework.security.afterinvocation.AfterInvocationProvider} to the + * AfterInvocationProviderManager's list. + * + * @author Luke Taylor + * @version $Id$ + * @since 2.0 + */ +public class CustomAfterInvocationProviderBeanDefinitionDecorator implements BeanDefinitionDecorator { + + public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder holder, ParserContext parserContext) { + ConfigUtils.getRegisteredAfterInvocationProviders(parserContext).add(holder.getBeanDefinition()); + + return holder; + } + +} diff --git a/core/src/main/java/org/springframework/security/config/Elements.java b/core/src/main/java/org/springframework/security/config/Elements.java index 1c6dfad992..e7d1ba2834 100644 --- a/core/src/main/java/org/springframework/security/config/Elements.java +++ b/core/src/main/java/org/springframework/security/config/Elements.java @@ -36,6 +36,7 @@ abstract class Elements { public static final String PORT_MAPPING = "port-mapping"; public static final String CUSTOM_FILTER = "custom-filter"; public static final String CUSTOM_AUTH_PROVIDER = "custom-authentication-provider"; + public static final String CUSTOM_AFTER_INVOCATION_PROVIDER = "custom-after-invocation-provider"; public static final String X509 = "x509"; public static final String FILTER_INVOCATION_DEFINITION_SOURCE = "filter-invocation-definition-source"; public static final String LDAP_PASSWORD_COMPARE = "password-compare"; diff --git a/core/src/main/java/org/springframework/security/config/GlobalMethodSecurityBeanDefinitionParser.java b/core/src/main/java/org/springframework/security/config/GlobalMethodSecurityBeanDefinitionParser.java index 0cc52d1f22..d7fd483d6f 100644 --- a/core/src/main/java/org/springframework/security/config/GlobalMethodSecurityBeanDefinitionParser.java +++ b/core/src/main/java/org/springframework/security/config/GlobalMethodSecurityBeanDefinitionParser.java @@ -47,7 +47,7 @@ class GlobalMethodSecurityBeanDefinitionParser implements BeanDefinitionParser { parserContext.getReaderContext().error("Cannot locate '" + className + "'", element); } } - + public BeanDefinition parse(Element element, ParserContext parserContext) { Object source = parserContext.extractSource(element); // The list of method metadata delegates @@ -142,22 +142,22 @@ class GlobalMethodSecurityBeanDefinitionParser implements BeanDefinitionParser { Element childElt = (Element) i.next(); String accessConfig = childElt.getAttribute(ATT_ACCESS); String expression = childElt.getAttribute(ATT_EXPRESSION); - + if (!StringUtils.hasText(accessConfig)) { parserContext.getReaderContext().error("Access configuration required", parserContext.extractSource(childElt)); } - + if (!StringUtils.hasText(expression)) { parserContext.getReaderContext().error("Pointcut expression required", parserContext.extractSource(childElt)); } - + ConfigAttributeDefinition def = new ConfigAttributeDefinition(StringUtils.commaDelimitedListToStringArray(accessConfig)); pointcutMap.put(expression, def); } return pointcutMap; } - + private void registerMethodSecurityInterceptor(ParserContext parserContext, String accessManagerId, Object source) { RootBeanDefinition interceptor = new RootBeanDefinition(MethodSecurityInterceptor.class); interceptor.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); @@ -167,9 +167,12 @@ class GlobalMethodSecurityBeanDefinitionParser implements BeanDefinitionParser { interceptor.getPropertyValues().addPropertyValue("authenticationManager", new RuntimeBeanReference(BeanIds.AUTHENTICATION_MANAGER)); interceptor.getPropertyValues().addPropertyValue("objectDefinitionSource", new RuntimeBeanReference(BeanIds.DELEGATING_METHOD_DEFINITION_SOURCE)); parserContext.getRegistry().registerBeanDefinition(BeanIds.METHOD_SECURITY_INTERCEPTOR, interceptor); - parserContext.registerComponent(new BeanComponentDefinition(interceptor, BeanIds.METHOD_SECURITY_INTERCEPTOR)); + parserContext.registerComponent(new BeanComponentDefinition(interceptor, BeanIds.METHOD_SECURITY_INTERCEPTOR)); + + parserContext.getRegistry().registerBeanDefinition(BeanIds.METHOD_SECURITY_INTERCEPTOR_POST_PROCESSOR, + new RootBeanDefinition(MethodSecurityInterceptorPostProcessor.class)); } - + private void registerAdvisor(ParserContext parserContext, Object source) { RootBeanDefinition advisor = new RootBeanDefinition(MethodDefinitionSourceAdvisor.class); advisor.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); @@ -178,6 +181,5 @@ class GlobalMethodSecurityBeanDefinitionParser implements BeanDefinitionParser { advisor.getConstructorArgumentValues().addGenericArgumentValue(new RuntimeBeanReference(BeanIds.DELEGATING_METHOD_DEFINITION_SOURCE)); parserContext.getRegistry().registerBeanDefinition(BeanIds.METHOD_DEFINITION_SOURCE_ADVISOR, advisor); - } - + } } diff --git a/core/src/main/java/org/springframework/security/config/MethodSecurityInterceptorPostProcessor.java b/core/src/main/java/org/springframework/security/config/MethodSecurityInterceptorPostProcessor.java new file mode 100644 index 0000000000..4485f2ab0f --- /dev/null +++ b/core/src/main/java/org/springframework/security/config/MethodSecurityInterceptorPostProcessor.java @@ -0,0 +1,48 @@ +package org.springframework.security.config; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryAware; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.security.AfterInvocationManager; +import org.springframework.security.intercept.method.aopalliance.MethodSecurityInterceptor; + +/** + * BeanPostProcessor which sets the AfterInvocationManager on the default MethodSecurityInterceptor, + * if one has been configured. + * + * @author Luke Taylor + * @version $Id$ + * + */ +public class MethodSecurityInterceptorPostProcessor implements BeanPostProcessor, BeanFactoryAware{ + private Log logger = LogFactory.getLog(getClass()); + + private BeanFactory beanFactory; + + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + if(!beanName.equals(BeanIds.METHOD_SECURITY_INTERCEPTOR)) { + return bean; + } + + MethodSecurityInterceptor interceptor = (MethodSecurityInterceptor) bean; + + if (beanFactory.containsBean(BeanIds.AFTER_INVOCATION_MANAGER)) { + logger.debug("Setting AfterInvocationManaer on MethodSecurityInterceptor"); + interceptor.setAfterInvocationManager((AfterInvocationManager) + beanFactory.getBean(BeanIds.AFTER_INVOCATION_MANAGER)); + } + + return bean; + } + + public Object postProcessAfterInitialization(Object bean, String beanName) { + return bean; + } + + public void setBeanFactory(BeanFactory beanFactory) throws BeansException { + this.beanFactory = beanFactory; + } +} diff --git a/core/src/main/java/org/springframework/security/config/SecurityNamespaceHandler.java b/core/src/main/java/org/springframework/security/config/SecurityNamespaceHandler.java index a0137ad165..3f5339323d 100644 --- a/core/src/main/java/org/springframework/security/config/SecurityNamespaceHandler.java +++ b/core/src/main/java/org/springframework/security/config/SecurityNamespaceHandler.java @@ -30,5 +30,6 @@ public class SecurityNamespaceHandler extends NamespaceHandlerSupport { registerBeanDefinitionDecorator(Elements.FILTER_CHAIN_MAP, new FilterChainMapBeanDefinitionDecorator()); registerBeanDefinitionDecorator(Elements.CUSTOM_FILTER, new OrderedFilterBeanDefinitionDecorator()); registerBeanDefinitionDecorator(Elements.CUSTOM_AUTH_PROVIDER, new CustomAuthenticationProviderBeanDefinitionDecorator()); + registerBeanDefinitionDecorator(Elements.CUSTOM_AFTER_INVOCATION_PROVIDER, new CustomAfterInvocationProviderBeanDefinitionDecorator()); } } diff --git a/core/src/test/java/org/springframework/security/config/CustomAfterInvocationProviderBeanDefinitionDecoratorTests.java b/core/src/test/java/org/springframework/security/config/CustomAfterInvocationProviderBeanDefinitionDecoratorTests.java new file mode 100644 index 0000000000..04bfc718c3 --- /dev/null +++ b/core/src/test/java/org/springframework/security/config/CustomAfterInvocationProviderBeanDefinitionDecoratorTests.java @@ -0,0 +1,43 @@ +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.afterinvocation.AfterInvocationProviderManager; +import org.springframework.security.intercept.method.aopalliance.MethodSecurityInterceptor; +import org.springframework.security.util.InMemoryXmlApplicationContext; + +public class CustomAfterInvocationProviderBeanDefinitionDecoratorTests { + private AbstractXmlApplicationContext appContext; + + @After + public void closeAppContext() { + if (appContext != null) { + appContext.close(); + appContext = null; + } + } + + @Test + public void customAuthenticationProviderIsAddedToInterceptor() { + setContext( + "" + + "" + + " " + + "" + + HttpSecurityBeanDefinitionParserTests.AUTH_PROVIDER_XML + ); + + MethodSecurityInterceptor msi = (MethodSecurityInterceptor) appContext.getBean(BeanIds.METHOD_SECURITY_INTERCEPTOR); + AfterInvocationProviderManager apm = (AfterInvocationProviderManager) msi.getAfterInvocationManager(); + assertNotNull(apm); + assertEquals(1, apm.getProviders().size()); + assertTrue(apm.getProviders().get(0) instanceof MockAfterInvocationProvider); + } + + private void setContext(String context) { + appContext = new InMemoryXmlApplicationContext(context); + } +} diff --git a/core/src/test/java/org/springframework/security/config/MockAfterInvocationProvider.java b/core/src/test/java/org/springframework/security/config/MockAfterInvocationProvider.java new file mode 100644 index 0000000000..a0e81c8fa8 --- /dev/null +++ b/core/src/test/java/org/springframework/security/config/MockAfterInvocationProvider.java @@ -0,0 +1,24 @@ +package org.springframework.security.config; + +import org.springframework.security.AccessDeniedException; +import org.springframework.security.Authentication; +import org.springframework.security.ConfigAttribute; +import org.springframework.security.ConfigAttributeDefinition; +import org.springframework.security.afterinvocation.AfterInvocationProvider; + +public class MockAfterInvocationProvider implements AfterInvocationProvider { + + public Object decide(Authentication authentication, Object object, ConfigAttributeDefinition config, Object returnedObject) + throws AccessDeniedException { + return returnedObject; + } + + public boolean supports(ConfigAttribute attribute) { + return true; + } + + public boolean supports(Class clazz) { + return true; + } + +}