Explorar o código

SEC-1256: Added support for expression attributes in filter-security-metadata-source configuration.

Luke Taylor %!s(int64=16) %!d(string=hai) anos
pai
achega
5d486a51b6

+ 3 - 3
config/src/main/java/org/springframework/security/config/SecurityNamespaceHandler.java

@@ -8,7 +8,7 @@ import org.springframework.security.config.authentication.JdbcUserServiceBeanDef
 import org.springframework.security.config.authentication.UserServiceBeanDefinitionParser;
 import org.springframework.security.config.http.CustomFilterBeanDefinitionDecorator;
 import org.springframework.security.config.http.FilterChainMapBeanDefinitionDecorator;
-import org.springframework.security.config.http.FilterInvocationSecurityMetadataSourceBeanDefinitionParser;
+import org.springframework.security.config.http.FilterInvocationSecurityMetadataSourceParser;
 import org.springframework.security.config.http.HttpSecurityBeanDefinitionParser;
 import org.springframework.security.config.ldap.LdapProviderBeanDefinitionParser;
 import org.springframework.security.config.ldap.LdapServerBeanDefinitionParser;
@@ -39,8 +39,8 @@ public class SecurityNamespaceHandler extends NamespaceHandlerSupport {
         registerBeanDefinitionParser(Elements.AUTHENTICATION_PROVIDER, new AuthenticationProviderBeanDefinitionParser());
         registerBeanDefinitionParser(Elements.GLOBAL_METHOD_SECURITY, new GlobalMethodSecurityBeanDefinitionParser());
         registerBeanDefinitionParser(Elements.AUTHENTICATION_MANAGER, new AuthenticationManagerBeanDefinitionParser());
-        registerBeanDefinitionParser(Elements.FILTER_INVOCATION_DEFINITION_SOURCE, new FilterInvocationSecurityMetadataSourceBeanDefinitionParser());
-        registerBeanDefinitionParser(Elements.FILTER_SECURITY_METADATA_SOURCE, new FilterInvocationSecurityMetadataSourceBeanDefinitionParser());
+        registerBeanDefinitionParser(Elements.FILTER_INVOCATION_DEFINITION_SOURCE, new FilterInvocationSecurityMetadataSourceParser());
+        registerBeanDefinitionParser(Elements.FILTER_SECURITY_METADATA_SOURCE, new FilterInvocationSecurityMetadataSourceParser());
 
         // Decorators
         registerBeanDefinitionDecorator(Elements.INTERCEPT_METHODS, new InterceptMethodsBeanDefinitionDecorator());

+ 62 - 15
config/src/main/java/org/springframework/security/config/http/FilterInvocationSecurityMetadataSourceBeanDefinitionParser.java → config/src/main/java/org/springframework/security/config/http/FilterInvocationSecurityMetadataSourceParser.java

@@ -5,11 +5,17 @@ import java.util.List;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.parsing.BeanComponentDefinition;
 import org.springframework.beans.factory.support.BeanDefinitionBuilder;
 import org.springframework.beans.factory.support.ManagedMap;
-import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
+import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
+import org.springframework.beans.factory.xml.BeanDefinitionParser;
 import org.springframework.beans.factory.xml.ParserContext;
 import org.springframework.security.access.SecurityConfig;
+import org.springframework.security.config.Elements;
+import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
+import org.springframework.security.web.access.expression.ExpressionBasedFilterInvocationSecurityMetadataSource;
+import org.springframework.security.web.access.intercept.DefaultFilterInvocationSecurityMetadataSource;
 import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
 import org.springframework.security.web.access.intercept.RequestKey;
 import org.springframework.security.web.util.AntUrlPathMatcher;
@@ -24,18 +30,14 @@ import org.w3c.dom.Element;
  * @author Luke Taylor
  * @version $Id$
  */
-public class FilterInvocationSecurityMetadataSourceBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
-
+public class FilterInvocationSecurityMetadataSourceParser implements BeanDefinitionParser {
+    private static final String ATT_USE_EXPRESSIONS = "use-expressions";
     private static final String ATT_HTTP_METHOD = "method";
     private static final String ATT_PATTERN = "pattern";
     private static final String ATT_ACCESS = "access";
-    private static final Log logger = LogFactory.getLog(FilterInvocationSecurityMetadataSourceBeanDefinitionParser.class);
-
-    protected String getBeanClassName(Element element) {
-        return "org.springframework.security.web.access.intercept.DefaultFilterInvocationSecurityMetadataSource";
-    }
+    private static final Log logger = LogFactory.getLog(FilterInvocationSecurityMetadataSourceParser.class);
 
-    protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
+    public BeanDefinition parse(Element element, ParserContext parserContext) {
         List<Element> interceptUrls = DomUtils.getChildElementsByTagName(element, "intercept-url");
 
         // Check for attributes that aren't allowed in this context
@@ -49,17 +51,60 @@ public class FilterInvocationSecurityMetadataSourceBeanDefinitionParser extends
             }
         }
 
-        UrlMatcher matcher = HttpSecurityBeanDefinitionParser.createUrlMatcher(element);
+        BeanDefinition mds = createSecurityMetadataSource(interceptUrls, element, parserContext);
+
+        String id = element.getAttribute(AbstractBeanDefinitionParser.ID_ATTRIBUTE);
+
+        if (StringUtils.hasText(id)) {
+            parserContext.registerComponent(new BeanComponentDefinition(mds, id));
+            parserContext.getRegistry().registerBeanDefinition(id, mds);
+        }
+
+        return mds;
+    }
+
+    static BeanDefinition createSecurityMetadataSource(List<Element> interceptUrls, Element elt, ParserContext pc) {
+        UrlMatcher matcher = HttpSecurityBeanDefinitionParser.createUrlMatcher(elt);
         boolean convertPathsToLowerCase = (matcher instanceof AntUrlPathMatcher) && matcher.requiresLowerCaseUrl();
+        boolean useExpressions = isUseExpressions(elt);
+
+        ManagedMap<BeanDefinition, BeanDefinition> requestToAttributesMap = parseInterceptUrlsForFilterInvocationRequestMap(
+                interceptUrls, convertPathsToLowerCase, useExpressions, pc);
+        BeanDefinitionBuilder fidsBuilder;
 
-        ManagedMap<BeanDefinition, BeanDefinition> requestMap = parseInterceptUrlsForFilterInvocationRequestMap(
-                interceptUrls, convertPathsToLowerCase, false, parserContext);
+        if (useExpressions) {
+            Element expressionHandlerElt = DomUtils.getChildElementByTagName(elt, Elements.EXPRESSION_HANDLER);
+            String expressionHandlerRef = expressionHandlerElt == null ? null : expressionHandlerElt.getAttribute("ref");
 
-        builder.addConstructorArgValue(matcher);
-        builder.addConstructorArgValue(requestMap);
+            if (StringUtils.hasText(expressionHandlerRef)) {
+                logger.info("Using bean '" + expressionHandlerRef + "' as web SecurityExpressionHandler implementation");
+            } else {
+                BeanDefinition expressionHandler = BeanDefinitionBuilder.rootBeanDefinition(DefaultWebSecurityExpressionHandler.class).getBeanDefinition();
+                expressionHandlerRef = pc.getReaderContext().registerWithGeneratedName(expressionHandler);
+                pc.registerBeanComponent(new BeanComponentDefinition(expressionHandler, expressionHandlerRef));
+            }
+
+            fidsBuilder = BeanDefinitionBuilder.rootBeanDefinition(ExpressionBasedFilterInvocationSecurityMetadataSource.class);
+            fidsBuilder.addConstructorArgValue(matcher);
+            fidsBuilder.addConstructorArgValue(requestToAttributesMap);
+            fidsBuilder.addConstructorArgReference(expressionHandlerRef);
+        } else {
+            fidsBuilder = BeanDefinitionBuilder.rootBeanDefinition(DefaultFilterInvocationSecurityMetadataSource.class);
+            fidsBuilder.addConstructorArgValue(matcher);
+            fidsBuilder.addConstructorArgValue(requestToAttributesMap);
+        }
+
+        fidsBuilder.addPropertyValue("stripQueryStringFromUrls", matcher instanceof AntUrlPathMatcher);
+        fidsBuilder.getRawBeanDefinition().setSource(pc.extractSource(elt));
+
+        return fidsBuilder.getBeanDefinition();
+    }
+
+    static boolean isUseExpressions(Element elt) {
+        return "true".equals(elt.getAttribute(ATT_USE_EXPRESSIONS));
     }
 
-    static ManagedMap<BeanDefinition, BeanDefinition> parseInterceptUrlsForFilterInvocationRequestMap(List<Element> urlElts,
+    private static ManagedMap<BeanDefinition, BeanDefinition> parseInterceptUrlsForFilterInvocationRequestMap(List<Element> urlElts,
             boolean useLowerCasePaths, boolean useExpressions, ParserContext parserContext) {
 
         ManagedMap<BeanDefinition, BeanDefinition> filterInvocationDefinitionMap = new ManagedMap<BeanDefinition, BeanDefinition>();
@@ -114,4 +159,6 @@ public class FilterInvocationSecurityMetadataSourceBeanDefinitionParser extends
 
         return filterInvocationDefinitionMap;
     }
+
+
 }

+ 3 - 40
config/src/main/java/org/springframework/security/config/http/HttpConfigurationBuilder.java

@@ -78,7 +78,6 @@ class HttpConfigurationBuilder {
     private static final String ATT_DISABLE_URL_REWRITING = "disable-url-rewriting";
 
     private static final String ATT_ACCESS_MGR = "access-decision-manager-ref";
-    private static final String ATT_USE_EXPRESSIONS = "use-expressions";
     private static final String ATT_ONCE_PER_REQUEST = "once-per-request";
 
     private final Element httpElt;
@@ -415,45 +414,21 @@ class HttpConfigurationBuilder {
     }
 
     void createFilterSecurityInterceptor(BeanReference authManager) {
-        BeanDefinitionBuilder fidsBuilder;
-
-        boolean useExpressions = "true".equals(httpElt.getAttribute(ATT_USE_EXPRESSIONS));
-
-        ManagedMap<BeanDefinition,BeanDefinition> requestToAttributesMap =
-            parseInterceptUrlsForFilterInvocationRequestMap(DomUtils.getChildElementsByTagName(httpElt, Elements.INTERCEPT_URL),
-                    convertPathsToLowerCase, useExpressions, pc);
+        boolean useExpressions = FilterInvocationSecurityMetadataSourceParser.isUseExpressions(httpElt);
+        BeanDefinition securityMds = FilterInvocationSecurityMetadataSourceParser.createSecurityMetadataSource(interceptUrls, httpElt, pc);
 
         RootBeanDefinition accessDecisionMgr;
         ManagedList<BeanDefinition> voters =  new ManagedList<BeanDefinition>(2);
 
         if (useExpressions) {
-            Element expressionHandlerElt = DomUtils.getChildElementByTagName(httpElt, Elements.EXPRESSION_HANDLER);
-            String expressionHandlerRef = expressionHandlerElt == null ? null : expressionHandlerElt.getAttribute("ref");
-
-            if (StringUtils.hasText(expressionHandlerRef)) {
-                logger.info("Using bean '" + expressionHandlerRef + "' as web SecurityExpressionHandler implementation");
-            } else {
-                BeanDefinition expressionHandler = BeanDefinitionBuilder.rootBeanDefinition(EXPRESSION_HANDLER_CLASS).getBeanDefinition();
-                expressionHandlerRef = pc.getReaderContext().registerWithGeneratedName(expressionHandler);
-                pc.registerBeanComponent(new BeanComponentDefinition(expressionHandler, expressionHandlerRef));
-            }
-
-            fidsBuilder = BeanDefinitionBuilder.rootBeanDefinition(EXPRESSION_FIMDS_CLASS);
-            fidsBuilder.addConstructorArgValue(matcher);
-            fidsBuilder.addConstructorArgValue(requestToAttributesMap);
-            fidsBuilder.addConstructorArgReference(expressionHandlerRef);
             voters.add(new RootBeanDefinition(WebExpressionVoter.class));
         } else {
-            fidsBuilder = BeanDefinitionBuilder.rootBeanDefinition(DefaultFilterInvocationSecurityMetadataSource.class);
-            fidsBuilder.addConstructorArgValue(matcher);
-            fidsBuilder.addConstructorArgValue(requestToAttributesMap);
             voters.add(new RootBeanDefinition(RoleVoter.class));
             voters.add(new RootBeanDefinition(AuthenticatedVoter.class));
         }
         accessDecisionMgr = new RootBeanDefinition(AffirmativeBased.class);
         accessDecisionMgr.getPropertyValues().addPropertyValue("decisionVoters", voters);
         accessDecisionMgr.setSource(pc.extractSource(httpElt));
-        fidsBuilder.addPropertyValue("stripQueryStringFromUrls", matcher instanceof AntUrlPathMatcher);
 
         // Set up the access manager reference for http
         String accessManagerId = httpElt.getAttribute(ATT_ACCESS_MGR);
@@ -472,7 +447,7 @@ class HttpConfigurationBuilder {
             builder.addPropertyValue("observeOncePerRequest", Boolean.FALSE);
         }
 
-        builder.addPropertyValue("securityMetadataSource", fidsBuilder.getBeanDefinition());
+        builder.addPropertyValue("securityMetadataSource", securityMds);
         BeanDefinition fsiBean = builder.getBeanDefinition();
         String fsiId = pc.getReaderContext().registerWithGeneratedName(fsiBean);
         pc.registerBeanComponent(new BeanComponentDefinition(fsiBean,fsiId));
@@ -486,18 +461,6 @@ class HttpConfigurationBuilder {
         this.fsi = new RuntimeBeanReference(fsiId);
     }
 
-    /**
-     * Parses the filter invocation map which will be used to configure the FilterInvocationSecurityMetadataSource
-     * used in the security interceptor.
-     */
-    private static ManagedMap<BeanDefinition,BeanDefinition>
-    parseInterceptUrlsForFilterInvocationRequestMap(List<Element> urlElts,  boolean useLowerCasePaths,
-            boolean useExpressions, ParserContext parserContext) {
-
-        return FilterInvocationSecurityMetadataSourceBeanDefinitionParser.parseInterceptUrlsForFilterInvocationRequestMap(urlElts, useLowerCasePaths, useExpressions, parserContext);
-
-    }
-
     BeanReference getSessionStrategy() {
         return sessionStrategyRef;
     }

+ 16 - 8
config/src/test/java/org/springframework/security/config/http/FilterSecurityMetadataSourceBeanDefinitionParserTests.java

@@ -1,7 +1,6 @@
 package org.springframework.security.config.http;
 
 import static org.junit.Assert.*;
-import static org.mockito.Mockito.*;
 
 import java.util.List;
 
@@ -15,14 +14,13 @@ import org.springframework.security.access.ConfigAttribute;
 import org.springframework.security.access.SecurityConfig;
 import org.springframework.security.config.BeanIds;
 import org.springframework.security.config.ConfigTestUtils;
-import org.springframework.security.config.http.FilterInvocationSecurityMetadataSourceBeanDefinitionParser;
 import org.springframework.security.config.util.InMemoryXmlApplicationContext;
 import org.springframework.security.web.FilterInvocation;
+import org.springframework.security.web.access.expression.ExpressionBasedFilterInvocationSecurityMetadataSource;
 import org.springframework.security.web.access.intercept.DefaultFilterInvocationSecurityMetadataSource;
-import org.w3c.dom.Element;
 
 /**
- * Tests for {@link FilterInvocationSecurityMetadataSourceBeanDefinitionParser}.
+ * Tests for {@link FilterInvocationSecurityMetadataSourceParser}.
  * @author Luke Taylor
  * @version $Id$
  */
@@ -41,10 +39,6 @@ public class FilterSecurityMetadataSourceBeanDefinitionParserTests {
         appContext = new InMemoryXmlApplicationContext(context);
     }
 
-    @Test
-    public void beanClassNameIsCorrect() throws Exception {
-        assertEquals(DefaultFilterInvocationSecurityMetadataSource.class.getName(), new FilterInvocationSecurityMetadataSourceBeanDefinitionParser().getBeanClassName(mock(Element.class)));
-    }
 
     @Test
     public void parsingMinimalConfigurationIsSuccessful() {
@@ -58,6 +52,20 @@ public class FilterSecurityMetadataSourceBeanDefinitionParserTests {
         assertTrue(cad.contains(new SecurityConfig("ROLE_A")));
     }
 
+    @Test
+    public void expressionsAreSupported() {
+        setContext(
+                "<filter-security-metadata-source id='fids' use-expressions='true'>" +
+                "   <intercept-url pattern='/**' access=\"hasRole('ROLE_A')\" />" +
+                "</filter-security-metadata-source>");
+
+        ExpressionBasedFilterInvocationSecurityMetadataSource fids =
+            (ExpressionBasedFilterInvocationSecurityMetadataSource) appContext.getBean("fids");
+        List<? extends ConfigAttribute> cad = fids.getAttributes(createFilterInvocation("/anything", "GET"));
+        assertEquals(1, cad.size());
+        assertEquals("hasRole('ROLE_A')", cad.get(0).toString());
+    }
+
     // SEC-1201
     @Test
     public void interceptUrlsSupportPropertyPlaceholders() {