diff --git a/config/src/main/java/org/springframework/security/config/method/GlobalMethodSecurityBeanDefinitionParser.java b/config/src/main/java/org/springframework/security/config/method/GlobalMethodSecurityBeanDefinitionParser.java index 84f81e4df5..5a180e8db2 100644 --- a/config/src/main/java/org/springframework/security/config/method/GlobalMethodSecurityBeanDefinitionParser.java +++ b/config/src/main/java/org/springframework/security/config/method/GlobalMethodSecurityBeanDefinitionParser.java @@ -79,6 +79,7 @@ public class GlobalMethodSecurityBeanDefinitionParser implements BeanDefinitionP private static final String ATT_REF = "ref"; private static final String ATT_MODE = "mode"; private static final String ATT_ADVICE_ORDER = "order"; + private static final String ATT_META_DATA_SOURCE_REF = "metadata-source-ref"; public BeanDefinition parse(Element element, ParserContext pc) { CompositeComponentDefinition compositeDef = @@ -97,6 +98,13 @@ public class GlobalMethodSecurityBeanDefinitionParser implements BeanDefinitionP BeanDefinition preInvocationVoter = null; ManagedList afterInvocationProviders = new ManagedList(); + // Check for an external SecurityMetadataSource, which takes priority over other sources + String metaDataSourceId = element.getAttribute(ATT_META_DATA_SOURCE_REF); + + if (StringUtils.hasText(metaDataSourceId)) { + delegates.add(new RuntimeBeanReference(metaDataSourceId)); + } + if (prePostAnnotationsEnabled) { Element prePostElt = DomUtils.getChildElementByTagName(element, INVOCATION_HANDLING); Element expressionHandlerElt = DomUtils.getChildElementByTagName(element, EXPRESSION_HANDLER); diff --git a/config/src/main/resources/org/springframework/security/config/spring-security-3.1.rnc b/config/src/main/resources/org/springframework/security/config/spring-security-3.1.rnc index 54a83adb1b..11643f6889 100644 --- a/config/src/main/resources/org/springframework/security/config/spring-security-3.1.rnc +++ b/config/src/main/resources/org/springframework/security/config/spring-security-3.1.rnc @@ -226,6 +226,8 @@ global-method-security.attlist &= global-method-security.attlist &= ## Can be used to specify that AspectJ should be used instead of the default Spring AOP. If set, secured classes must be woven with the AnnotationSecurityAspect from the spring-security-aspects module. attribute mode {"aspectj"}? +global-method-security.attlist &= + attribute metadata-source-ref {xsd:token}? after-invocation-provider = ## Allows addition of extra AfterInvocationProvider beans which should be called by the MethodSecurityInterceptor created by global-method-security. diff --git a/config/src/main/resources/org/springframework/security/config/spring-security-3.1.xsd b/config/src/main/resources/org/springframework/security/config/spring-security-3.1.xsd index 61f79e126c..27f1baa6db 100644 --- a/config/src/main/resources/org/springframework/security/config/spring-security-3.1.xsd +++ b/config/src/main/resources/org/springframework/security/config/spring-security-3.1.xsd @@ -576,6 +576,7 @@ + diff --git a/config/src/test/java/org/springframework/security/config/method/GlobalMethodSecurityBeanDefinitionParserTests.java b/config/src/test/java/org/springframework/security/config/method/GlobalMethodSecurityBeanDefinitionParserTests.java index 489eae4223..800b1806d1 100644 --- a/config/src/test/java/org/springframework/security/config/method/GlobalMethodSecurityBeanDefinitionParserTests.java +++ b/config/src/test/java/org/springframework/security/config/method/GlobalMethodSecurityBeanDefinitionParserTests.java @@ -346,6 +346,28 @@ public class GlobalMethodSecurityBeanDefinitionParserTests { assertSame(ram, FieldUtils.getFieldValue(msi.getAdvice(), "runAsManager")); } + @Test + @SuppressWarnings("unchecked") + public void supportsExternalMetadataSource() throws Exception { + setContext( + "" + + "" + + " " + + "" + + "" + AUTH_PROVIDER_XML + ); + // External MDS should take precedence over PreAuthorize + SecurityContextHolder.getContext().setAuthentication(bob); + Foo foo = (Foo) appContext.getBean("target"); + try { + foo.foo(new SecurityConfig("A")); + fail("Bob can't invoke admin methods"); + } catch (AccessDeniedException expected) { + } + SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken("admin", "password")); + foo.foo(new SecurityConfig("A")); + } + private void setContext(String context) { appContext = new InMemoryXmlApplicationContext(context); } diff --git a/core/src/test/java/org/springframework/security/access/annotation/SecuredAnnotationSecurityMetadataDefinitionSourceTests.java b/core/src/test/java/org/springframework/security/access/annotation/SecuredAnnotationSecurityMetadataDefinitionSourceTests.java index 9f89d96a9e..56d5dfd30c 100644 --- a/core/src/test/java/org/springframework/security/access/annotation/SecuredAnnotationSecurityMetadataDefinitionSourceTests.java +++ b/core/src/test/java/org/springframework/security/access/annotation/SecuredAnnotationSecurityMetadataDefinitionSourceTests.java @@ -16,19 +16,18 @@ package org.springframework.security.access.annotation; import static org.junit.Assert.*; +import org.junit.*; +import org.springframework.security.access.ConfigAttribute; +import org.springframework.security.access.SecurityConfig; +import org.springframework.security.core.GrantedAuthority; + import java.lang.annotation.ElementType; -import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.reflect.Method; import java.util.*; -import org.junit.*; -import org.springframework.security.access.ConfigAttribute; -import org.springframework.security.access.SecurityConfig; -import org.springframework.security.core.GrantedAuthority; - /** * Tests for {@link org.springframework.security.access.annotation.SecuredAnnotationSecurityMetadataSource}