Kaynağa Gözat

SEC-783: GlobalMethodSecurityBeanDefinitionParser should support AfterInvocationProviders
http://jira.springframework.org/browse/SEC-783. Added support for custom-after-invocation-provider

Luke Taylor 17 yıl önce
ebeveyn
işleme
d3a0f05de9

+ 5 - 0
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.
  * <p>
@@ -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";
+
 }

+ 20 - 2
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;
 		}
-    }    
+    }
 }

+ 24 - 0
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;
+	}
+
+}

+ 1 - 0
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";

+ 11 - 9
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);        
-    }
-    
+    }    
 }

+ 48 - 0
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;
+	}
+}

+ 1 - 0
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());        
     }
 }

+ 43 - 0
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(
+                "<global-method-security />" +
+                "<b:bean id='aip' class='org.springframework.security.config.MockAfterInvocationProvider'>" +
+                "    <custom-after-invocation-provider />" +
+                "</b:bean>" +
+                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);
+    }
+}

+ 24 - 0
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;
+	}
+
+}