diff --git a/config/src/main/java/org/springframework/security/config/SecurityNamespaceHandler.java b/config/src/main/java/org/springframework/security/config/SecurityNamespaceHandler.java index a139009821..c083f449fb 100644 --- a/config/src/main/java/org/springframework/security/config/SecurityNamespaceHandler.java +++ b/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()); diff --git a/config/src/main/java/org/springframework/security/config/http/FilterInvocationSecurityMetadataSourceBeanDefinitionParser.java b/config/src/main/java/org/springframework/security/config/http/FilterInvocationSecurityMetadataSourceParser.java similarity index 54% rename from config/src/main/java/org/springframework/security/config/http/FilterInvocationSecurityMetadataSourceBeanDefinitionParser.java rename to config/src/main/java/org/springframework/security/config/http/FilterInvocationSecurityMetadataSourceParser.java index b70b36ba7e..4d7b68b6d3 100644 --- a/config/src/main/java/org/springframework/security/config/http/FilterInvocationSecurityMetadataSourceBeanDefinitionParser.java +++ b/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); + private static final Log logger = LogFactory.getLog(FilterInvocationSecurityMetadataSourceParser.class); - protected String getBeanClassName(Element element) { - return "org.springframework.security.web.access.intercept.DefaultFilterInvocationSecurityMetadataSource"; - } - - protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { + public BeanDefinition parse(Element element, ParserContext parserContext) { List 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); - boolean convertPathsToLowerCase = (matcher instanceof AntUrlPathMatcher) && matcher.requiresLowerCaseUrl(); + BeanDefinition mds = createSecurityMetadataSource(interceptUrls, element, parserContext); - ManagedMap requestMap = parseInterceptUrlsForFilterInvocationRequestMap( - interceptUrls, convertPathsToLowerCase, false, parserContext); + String id = element.getAttribute(AbstractBeanDefinitionParser.ID_ATTRIBUTE); - builder.addConstructorArgValue(matcher); - builder.addConstructorArgValue(requestMap); + if (StringUtils.hasText(id)) { + parserContext.registerComponent(new BeanComponentDefinition(mds, id)); + parserContext.getRegistry().registerBeanDefinition(id, mds); + } + + return mds; } - static ManagedMap parseInterceptUrlsForFilterInvocationRequestMap(List urlElts, + static BeanDefinition createSecurityMetadataSource(List interceptUrls, Element elt, ParserContext pc) { + UrlMatcher matcher = HttpSecurityBeanDefinitionParser.createUrlMatcher(elt); + boolean convertPathsToLowerCase = (matcher instanceof AntUrlPathMatcher) && matcher.requiresLowerCaseUrl(); + boolean useExpressions = isUseExpressions(elt); + + ManagedMap requestToAttributesMap = parseInterceptUrlsForFilterInvocationRequestMap( + interceptUrls, convertPathsToLowerCase, useExpressions, pc); + BeanDefinitionBuilder fidsBuilder; + + if (useExpressions) { + Element expressionHandlerElt = DomUtils.getChildElementByTagName(elt, 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(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)); + } + + private static ManagedMap parseInterceptUrlsForFilterInvocationRequestMap(List urlElts, boolean useLowerCasePaths, boolean useExpressions, ParserContext parserContext) { ManagedMap filterInvocationDefinitionMap = new ManagedMap(); @@ -114,4 +159,6 @@ public class FilterInvocationSecurityMetadataSourceBeanDefinitionParser extends return filterInvocationDefinitionMap; } + + } 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 31e7297722..c565528d9d 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 @@ -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 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 voters = new ManagedList(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 - parseInterceptUrlsForFilterInvocationRequestMap(List urlElts, boolean useLowerCasePaths, - boolean useExpressions, ParserContext parserContext) { - - return FilterInvocationSecurityMetadataSourceBeanDefinitionParser.parseInterceptUrlsForFilterInvocationRequestMap(urlElts, useLowerCasePaths, useExpressions, parserContext); - - } - BeanReference getSessionStrategy() { return sessionStrategyRef; } diff --git a/config/src/test/java/org/springframework/security/config/http/FilterSecurityMetadataSourceBeanDefinitionParserTests.java b/config/src/test/java/org/springframework/security/config/http/FilterSecurityMetadataSourceBeanDefinitionParserTests.java index a8446ca122..08f82ba9ec 100644 --- a/config/src/test/java/org/springframework/security/config/http/FilterSecurityMetadataSourceBeanDefinitionParserTests.java +++ b/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( + "" + + " " + + ""); + + ExpressionBasedFilterInvocationSecurityMetadataSource fids = + (ExpressionBasedFilterInvocationSecurityMetadataSource) appContext.getBean("fids"); + List cad = fids.getAttributes(createFilterInvocation("/anything", "GET")); + assertEquals(1, cad.size()); + assertEquals("hasRole('ROLE_A')", cad.get(0).toString()); + } + // SEC-1201 @Test public void interceptUrlsSupportPropertyPlaceholders() {