From 2f67bb30322da8255a3abb5f3597671beaed0acd Mon Sep 17 00:00:00 2001 From: Luke Taylor Date: Sun, 30 Oct 2011 21:51:02 +0000 Subject: [PATCH] SEC-1847: Add authentication-manager-ref attribute to http and global-method-security namespace elements. --- ...enticationManagerBeanDefinitionParser.java | 28 +++++++++-------- .../AuthenticationManagerFactoryBean.java | 5 ++-- .../HttpSecurityBeanDefinitionParser.java | 30 ++++++++++++------- ...balMethodSecurityBeanDefinitionParser.java | 20 +++++++++---- .../security/config/spring-security-3.1.rnc | 16 ++++++++-- .../security/config/spring-security-3.1.xsd | 24 ++++++++++++++- .../config/http/MiscHttpConfigTests.groovy | 15 ++++++++++ .../config/http/RememberMeConfigTests.groovy | 5 ++-- ...ationManagerBeanDefinitionParserTests.java | 2 +- 9 files changed, 109 insertions(+), 36 deletions(-) diff --git a/config/src/main/java/org/springframework/security/config/authentication/AuthenticationManagerBeanDefinitionParser.java b/config/src/main/java/org/springframework/security/config/authentication/AuthenticationManagerBeanDefinitionParser.java index 9d95b59b19..f49bf01483 100644 --- a/config/src/main/java/org/springframework/security/config/authentication/AuthenticationManagerBeanDefinitionParser.java +++ b/config/src/main/java/org/springframework/security/config/authentication/AuthenticationManagerBeanDefinitionParser.java @@ -38,8 +38,13 @@ public class AuthenticationManagerBeanDefinitionParser implements BeanDefinition private static final String ATT_ERASE_CREDENTIALS = "erase-credentials"; public BeanDefinition parse(Element element, ParserContext pc) { - Assert.state(!pc.getRegistry().containsBeanDefinition(BeanIds.AUTHENTICATION_MANAGER), - "AuthenticationManager has already been registered!"); + String id = element.getAttribute("id"); + + if (!StringUtils.hasText(id)) { + Assert.state(!pc.getRegistry().containsBeanDefinition(BeanIds.AUTHENTICATION_MANAGER), + "Global AuthenticationManager has already been registered!"); + id = BeanIds.AUTHENTICATION_MANAGER; + } pc.pushContainingComponent(new CompositeComponentDefinition(element.getTagName(), pc.extractSource(element))); BeanDefinitionBuilder providerManagerBldr = BeanDefinitionBuilder.rootBeanDefinition(ProviderManager.class); @@ -72,9 +77,9 @@ public class AuthenticationManagerBeanDefinitionParser implements BeanDefinition } else { BeanDefinition provider = resolver.resolve(providerElt.getNamespaceURI()).parse(providerElt, pc); Assert.notNull(provider, "Parser for " + providerElt.getNodeName() + " returned a null bean definition"); - String id = pc.getReaderContext().generateBeanName(provider); - pc.registerBeanComponent(new BeanComponentDefinition(provider, id)); - providers.add(new RuntimeBeanReference(id)); + String providerId = pc.getReaderContext().generateBeanName(provider); + pc.registerBeanComponent(new BeanComponentDefinition(provider, providerId)); + providers.add(new RuntimeBeanReference(providerId)); } } } @@ -91,16 +96,15 @@ public class AuthenticationManagerBeanDefinitionParser implements BeanDefinition // Add the default event publisher BeanDefinition publisher = new RootBeanDefinition(DefaultAuthenticationEventPublisher.class); - String id = pc.getReaderContext().generateBeanName(publisher); - pc.registerBeanComponent(new BeanComponentDefinition(publisher, id)); - providerManagerBldr.addPropertyReference("authenticationEventPublisher", id); + String pubId = pc.getReaderContext().generateBeanName(publisher); + pc.registerBeanComponent(new BeanComponentDefinition(publisher, pubId)); + providerManagerBldr.addPropertyReference("authenticationEventPublisher", pubId); - pc.registerBeanComponent( - new BeanComponentDefinition(providerManagerBldr.getBeanDefinition(), BeanIds.AUTHENTICATION_MANAGER)); + pc.registerBeanComponent(new BeanComponentDefinition(providerManagerBldr.getBeanDefinition(), id)); if (StringUtils.hasText(alias)) { - pc.getRegistry().registerAlias(BeanIds.AUTHENTICATION_MANAGER, alias); - pc.getReaderContext().fireAliasRegistered(BeanIds.AUTHENTICATION_MANAGER, alias, pc.extractSource(element)); + pc.getRegistry().registerAlias(id, alias); + pc.getReaderContext().fireAliasRegistered(id, alias, pc.extractSource(element)); } pc.popAndRegisterContainingComponent(); diff --git a/config/src/main/java/org/springframework/security/config/authentication/AuthenticationManagerFactoryBean.java b/config/src/main/java/org/springframework/security/config/authentication/AuthenticationManagerFactoryBean.java index 4ddf15681e..b2c265599f 100644 --- a/config/src/main/java/org/springframework/security/config/authentication/AuthenticationManagerFactoryBean.java +++ b/config/src/main/java/org/springframework/security/config/authentication/AuthenticationManagerFactoryBean.java @@ -19,8 +19,9 @@ import org.springframework.security.config.BeanIds; */ public class AuthenticationManagerFactoryBean implements FactoryBean, BeanFactoryAware { private BeanFactory bf; - public static final String MISSING_BEAN_ERROR_MESSAGE = "Did you forget to add an element " + - "to your configuration (with child elements) ?"; + public static final String MISSING_BEAN_ERROR_MESSAGE = "Did you forget to add a gobal element " + + "to your configuration (with child elements)? Alternatively you can use the " + + "authentication-manager-ref attribute on your and elements."; public AuthenticationManager getObject() throws Exception { try { diff --git a/config/src/main/java/org/springframework/security/config/http/HttpSecurityBeanDefinitionParser.java b/config/src/main/java/org/springframework/security/config/http/HttpSecurityBeanDefinitionParser.java index a6c4553a12..ce4a364b4a 100644 --- a/config/src/main/java/org/springframework/security/config/http/HttpSecurityBeanDefinitionParser.java +++ b/config/src/main/java/org/springframework/security/config/http/HttpSecurityBeanDefinitionParser.java @@ -23,7 +23,6 @@ import org.springframework.security.config.Elements; import org.springframework.security.config.authentication.AuthenticationManagerFactoryBean; import org.springframework.security.web.DefaultSecurityFilterChain; import org.springframework.security.web.FilterChainProxy; -import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.util.AnyRequestMatcher; import org.springframework.util.StringUtils; import org.springframework.util.xml.DomUtils; @@ -41,6 +40,7 @@ import java.util.*; public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser { private static final Log logger = LogFactory.getLog(HttpSecurityBeanDefinitionParser.class); + private static final String ATT_AUTHENTICATION_MANAGER_REF = "authentication-manager-ref"; private static final String ATT_REQUEST_MATCHER_REF = "request-matcher-ref"; static final String ATT_PATH_PATTERN = "pattern"; static final String ATT_HTTP_METHOD = "method"; @@ -190,22 +190,32 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser { } /** - * Creates the internal AuthenticationManager bean which uses the externally registered (global) one as - * a parent. + * Creates the internal AuthenticationManager bean which uses either the externally registered (global) one as + * a parent or the bean specified by "authentication-manager-ref". * - * All the providers registered by this <http> block will be registered with the internal - * authentication manager. + * All the providers registered by this <http> block will be registered with the internal authentication + * manager. */ private BeanReference createAuthenticationManager(Element element, ParserContext pc, ManagedList authenticationProviders) { + String parentMgrRef = element.getAttribute(ATT_AUTHENTICATION_MANAGER_REF); BeanDefinitionBuilder authManager = BeanDefinitionBuilder.rootBeanDefinition(ProviderManager.class); authManager.addConstructorArgValue(authenticationProviders); - authManager.addConstructorArgValue(new RootBeanDefinition(AuthenticationManagerFactoryBean.class)); - RootBeanDefinition clearCredentials = new RootBeanDefinition(MethodInvokingFactoryBean.class); - clearCredentials.getPropertyValues().addPropertyValue("targetObject", new RootBeanDefinition(AuthenticationManagerFactoryBean.class)); - clearCredentials.getPropertyValues().addPropertyValue("targetMethod", "isEraseCredentialsAfterAuthentication"); - authManager.addPropertyValue("eraseCredentialsAfterAuthentication", clearCredentials); + if (StringUtils.hasText(parentMgrRef)) { + authManager.addConstructorArgValue(new RuntimeBeanReference(parentMgrRef)); + } else { + RootBeanDefinition amfb = new RootBeanDefinition(AuthenticationManagerFactoryBean.class); + amfb.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); + String amfbId = pc.getReaderContext().generateBeanName(amfb); + pc.registerBeanComponent(new BeanComponentDefinition(amfb, amfbId)); + RootBeanDefinition clearCredentials = new RootBeanDefinition(MethodInvokingFactoryBean.class); + clearCredentials.getPropertyValues().addPropertyValue("targetObject", new RuntimeBeanReference(amfbId)); + clearCredentials.getPropertyValues().addPropertyValue("targetMethod", "isEraseCredentialsAfterAuthentication"); + + authManager.addConstructorArgValue(new RuntimeBeanReference(amfbId)); + authManager.addPropertyValue("eraseCredentialsAfterAuthentication", clearCredentials); + } authManager.getRawBeanDefinition().setSource(pc.extractSource(element)); BeanDefinition authMgrBean = authManager.getBeanDefinition(); 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 b3939c0c0e..88e5547223 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 @@ -69,6 +69,7 @@ public class GlobalMethodSecurityBeanDefinitionParser implements BeanDefinitionP private final Log logger = LogFactory.getLog(getClass()); + private static final String ATT_AUTHENTICATION_MANAGER_REF = "authentication-manager-ref"; private static final String ATT_ACCESS = "access"; private static final String ATT_EXPRESSION = "expression"; private static final String ATT_ACCESS_MGR = "access-decision-manager-ref"; @@ -204,8 +205,10 @@ public class GlobalMethodSecurityBeanDefinitionParser implements BeanDefinitionP accessManagerId = registerAccessManager(pc, jsr250Enabled, preInvocationVoter); } + String authMgrRef = element.getAttribute(ATT_AUTHENTICATION_MANAGER_REF); + String runAsManagerId = element.getAttribute(ATT_RUN_AS_MGR); - BeanReference interceptor = registerMethodSecurityInterceptor(pc, accessManagerId, runAsManagerId, + BeanReference interceptor = registerMethodSecurityInterceptor(pc, authMgrRef, accessManagerId, runAsManagerId, metadataSource, afterInvocationProviders, source, useAspectJ); if (useAspectJ) { @@ -307,7 +310,7 @@ public class GlobalMethodSecurityBeanDefinitionParser implements BeanDefinitionP return pointcutMap; } - private BeanReference registerMethodSecurityInterceptor(ParserContext pc, String accessManagerId, + private BeanReference registerMethodSecurityInterceptor(ParserContext pc, String authMgrRef, String accessManagerId, String runAsManagerId, BeanReference metadataSource, List afterInvocationProviders, Object source, boolean useAspectJ) { BeanDefinitionBuilder bldr = @@ -315,7 +318,9 @@ public class GlobalMethodSecurityBeanDefinitionParser implements BeanDefinitionP AspectJMethodSecurityInterceptor.class : MethodSecurityInterceptor.class); bldr.getRawBeanDefinition().setSource(source); bldr.addPropertyReference("accessDecisionManager", accessManagerId); - bldr.addPropertyValue("authenticationManager", new RootBeanDefinition(AuthenticationManagerDelegator.class)); + RootBeanDefinition authMgr = new RootBeanDefinition(AuthenticationManagerDelegator.class); + authMgr.getConstructorArgumentValues().addGenericArgumentValue(authMgrRef); + bldr.addPropertyValue("authenticationManager", authMgr); bldr.addPropertyValue("securityMetadataSource", metadataSource); if (StringUtils.hasText(runAsManagerId)) { @@ -367,13 +372,18 @@ public class GlobalMethodSecurityBeanDefinitionParser implements BeanDefinitionP private AuthenticationManager delegate; private final Object delegateMonitor = new Object(); private BeanFactory beanFactory; + private final String authMgrBean; + + AuthenticationManagerDelegator(String authMgrBean) { + this.authMgrBean = StringUtils.hasText(authMgrBean) ? authMgrBean : BeanIds.AUTHENTICATION_MANAGER; + } public Authentication authenticate(Authentication authentication) throws AuthenticationException { synchronized(delegateMonitor) { if (delegate == null) { - Assert.state(beanFactory != null, "BeanFactory must be set to resolve " + BeanIds.AUTHENTICATION_MANAGER); + Assert.state(beanFactory != null, "BeanFactory must be set to resolve " + authMgrBean); try { - delegate = beanFactory.getBean(BeanIds.AUTHENTICATION_MANAGER, ProviderManager.class); + delegate = beanFactory.getBean(authMgrBean, ProviderManager.class); } catch (NoSuchBeanDefinitionException e) { if (BeanIds.AUTHENTICATION_MANAGER.equals(e.getBeanName())) { throw new NoSuchBeanDefinitionException(BeanIds.AUTHENTICATION_MANAGER, 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 de57a115f3..bc4e78da6c 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 @@ -40,6 +40,10 @@ user-service-ref = ## A reference to a user-service (or UserDetailsService bean) Id attribute user-service-ref {xsd:token} +authentication-manager-ref = + ## A reference to an AuthenticationManager bean + attribute authentication-manager-ref {xsd:token} + data-source-ref = ## A reference to a DataSource bean attribute data-source-ref {xsd:token} @@ -228,6 +232,9 @@ global-method-security.attlist &= attribute mode {"aspectj"}? global-method-security.attlist &= attribute metadata-source-ref {xsd:token}? +global-method-security.attlist &= + authentication-manager-ref? + after-invocation-provider = ## Allows addition of extra AfterInvocationProvider beans which should be called by the MethodSecurityInterceptor created by global-method-security. @@ -319,9 +326,10 @@ http.attlist &= ## Prevents the jsessionid parameter from being added to rendered URLs. attribute disable-url-rewriting {xsd:boolean}? http.attlist &= - ## Exposes the list of filters defined by this configuration under this bean name in the application context. May be used by + ## Exposes the list of filters defined by this configuration under this bean name in the application context. name? - +http.attlist &= + authentication-manager-ref? access-denied-handler = ## Defines the access-denied strategy that should be used. An access denied page can be defined or a reference to an AccessDeniedHandler instance. @@ -618,7 +626,9 @@ authentication-manager = ## Registers the AuthenticationManager instance and allows its list of AuthenticationProviders to be defined. Also allows you to define an alias to allow you to reference the AuthenticationManager in your own beans. element authentication-manager {authman.attlist & authentication-provider* & ldap-authentication-provider*} authman.attlist &= - ## The alias you wish to use for the AuthenticationManager bean + id? +authman.attlist &= + ## An alias you wish to use for the AuthenticationManager bean (not required it you are using a specific id) attribute alias {xsd:token}? authman.attlist &= ## If set to true, the AuthenticationManger will attempt to clear any credentials data in the returned Authentication object, once the user has been authenticated. 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 298c1b7305..d28de75a56 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 @@ -98,6 +98,13 @@ + + + + A reference to an AuthenticationManager bean + + + @@ -577,6 +584,11 @@ + + + A reference to an AuthenticationManager bean + + @@ -804,6 +816,11 @@ A bean identifier, used for referring to the bean elsewhere in the context. + + + A reference to an AuthenticationManager bean + + @@ -1357,9 +1374,14 @@ + + + A bean identifier, used for referring to the bean elsewhere in the context. + + - The alias you wish to use for the AuthenticationManager bean + An alias you wish to use for the AuthenticationManager bean (not required it you are using a specific id) diff --git a/config/src/test/groovy/org/springframework/security/config/http/MiscHttpConfigTests.groovy b/config/src/test/groovy/org/springframework/security/config/http/MiscHttpConfigTests.groovy index 03bd9e013e..353c878487 100644 --- a/config/src/test/groovy/org/springframework/security/config/http/MiscHttpConfigTests.groovy +++ b/config/src/test/groovy/org/springframework/security/config/http/MiscHttpConfigTests.groovy @@ -54,6 +54,7 @@ import org.springframework.security.access.PermissionEvaluator import org.springframework.security.core.Authentication import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler import org.springframework.security.web.util.AntPathRequestMatcher +import org.springframework.security.authentication.AuthenticationManager class MiscHttpConfigTests extends AbstractHttpConfigTests { def 'Minimal configuration parses'() { @@ -679,6 +680,20 @@ class MiscHttpConfigTests extends AbstractHttpConfigTests { expect: getFilter(FilterSecurityInterceptor.class).accessDecisionManager.decisionVoters[3] instanceof WebExpressionVoter } + + def customAuthenticationManagerIsSupported() { + xml.http('auto-config': 'true', 'authentication-manager-ref': 'am') + xml.'b:bean'(id: 'am', 'class': MockAuthenticationManager.class.name) + createAppContext("") + expect: + getFilter(UsernamePasswordAuthenticationFilter.class).authenticationManager.parent instanceof MockAuthenticationManager + } +} + +class MockAuthenticationManager implements AuthenticationManager { + Authentication authenticate(Authentication authentication) { + return null + } } class MockPermissionEvaluator implements PermissionEvaluator { diff --git a/config/src/test/groovy/org/springframework/security/config/http/RememberMeConfigTests.groovy b/config/src/test/groovy/org/springframework/security/config/http/RememberMeConfigTests.groovy index 998b2fe954..72c54dc1a2 100644 --- a/config/src/test/groovy/org/springframework/security/config/http/RememberMeConfigTests.groovy +++ b/config/src/test/groovy/org/springframework/security/config/http/RememberMeConfigTests.groovy @@ -69,10 +69,11 @@ class RememberMeConfigTests extends AbstractHttpConfigTests { List logoutHandlers = FieldUtils.getFieldValue(getFilter(LogoutFilter.class), "handlers"); Map ams = appContext.getBeansOfType(ProviderManager.class); - ams.remove(BeanIds.AUTHENTICATION_MANAGER); - RememberMeAuthenticationProvider rmp = (ams.values() as List)[0].providers[1]; + ProviderManager am = (ams.values() as List).find { it instanceof ProviderManager && it.providers.size() == 2} + RememberMeAuthenticationProvider rmp = am.providers.find { it instanceof RememberMeAuthenticationProvider} expect: + rmp != null 5000 == FieldUtils.getFieldValue(rememberMeServices(), "tokenValiditySeconds") // SEC-909 logoutHandlers.size() == 2 diff --git a/config/src/test/java/org/springframework/security/config/authentication/AuthenticationManagerBeanDefinitionParserTests.java b/config/src/test/java/org/springframework/security/config/authentication/AuthenticationManagerBeanDefinitionParserTests.java index b419261a74..73d7830aa6 100644 --- a/config/src/test/java/org/springframework/security/config/authentication/AuthenticationManagerBeanDefinitionParserTests.java +++ b/config/src/test/java/org/springframework/security/config/authentication/AuthenticationManagerBeanDefinitionParserTests.java @@ -22,7 +22,7 @@ import org.springframework.security.util.FieldUtils; */ public class AuthenticationManagerBeanDefinitionParserTests { private static final String CONTEXT = - "" + + "" + " " + " " + " " +