From 4bd41cbf7282f10ffbe5388bd2e712f9c4aacadc Mon Sep 17 00:00:00 2001 From: Luke Taylor Date: Sat, 14 Aug 2010 15:10:03 +0100 Subject: [PATCH] SEC-1133: Support for setting of authenticationDetailsSource property for form-login, openid-login, http-basic and x509 namespace elements. These elements now support an additional 'authentication-details-source-ref' attribute. --- config/config.gradle | 2 +- .../http/AuthenticationConfigBuilder.java | 56 +++++++++++++------ .../http/FormLoginBeanDefinitionParser.java | 12 +++- .../HttpSecurityBeanDefinitionParser.java | 4 +- .../security/config/spring-security-3.1.rnc | 9 +++ .../security/config/spring-security-3.1.xsd | 13 ++++- .../http/AbstractHttpConfigTests.groovy | 9 +-- .../config/http/MiscHttpConfigTests.groovy | 20 ++++++- ...tractPreAuthenticatedProcessingFilter.java | 4 ++ 9 files changed, 96 insertions(+), 33 deletions(-) diff --git a/config/config.gradle b/config/config.gradle index 7b3abcb65a..e375a1838e 100644 --- a/config/config.gradle +++ b/config/config.gradle @@ -16,7 +16,7 @@ dependencies { provided "javax.servlet:servlet-api:2.5" - groovy group: 'org.codehaus.groovy', name: 'groovy', version: '1.7.3' + groovy group: 'org.codehaus.groovy', name: 'groovy', version: '1.7.4' testCompile project(':spring-security-ldap'), project(':spring-security-openid'), diff --git a/config/src/main/java/org/springframework/security/config/http/AuthenticationConfigBuilder.java b/config/src/main/java/org/springframework/security/config/http/AuthenticationConfigBuilder.java index 2b5657aa2b..86e3b0f818 100644 --- a/config/src/main/java/org/springframework/security/config/http/AuthenticationConfigBuilder.java +++ b/config/src/main/java/org/springframework/security/config/http/AuthenticationConfigBuilder.java @@ -64,6 +64,8 @@ final class AuthenticationConfigBuilder { private static final String OPEN_ID_ATTRIBUTE_FACTORY_CLASS = "org.springframework.security.openid.RegexBasedAxFetchListFactory"; static final String AUTHENTICATION_PROCESSING_FILTER_CLASS = "org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter"; + static final String ATT_AUTH_DETAILS_SOURCE_REF = "authentication-details-source-ref"; + private static final String ATT_AUTO_CONFIG = "auto-config"; private static final String ATT_ACCESS_DENIED_PAGE = "access-denied-page"; @@ -72,6 +74,7 @@ final class AuthenticationConfigBuilder { private static final String ATT_USER_SERVICE_REF = "user-service-ref"; + private final Element httpElt; private final ParserContext pc; @@ -148,7 +151,7 @@ final class AuthenticationConfigBuilder { key = DEF_KEY; } - rememberMeFilter = (RootBeanDefinition) new RememberMeBeanDefinitionParser(key).parse(rememberMeElt, pc); + rememberMeFilter = new RememberMeBeanDefinitionParser(key).parse(rememberMeElt, pc); rememberMeFilter.getPropertyValues().addPropertyValue("authenticationManager", authenticationManager); rememberMeServicesId = ((RuntimeBeanReference) rememberMeFilter.getPropertyValues().getPropertyValue("rememberMeServices").getValue()).getBeanName(); createRememberMeProvider(key); @@ -291,35 +294,41 @@ final class AuthenticationConfigBuilder { void createBasicFilter(BeanReference authManager) { Element basicAuthElt = DomUtils.getChildElementByTagName(httpElt, Elements.BASIC_AUTH); + if (basicAuthElt == null && !autoConfig) { + // No basic auth, do nothing + return; + } + String realm = httpElt.getAttribute(ATT_REALM); if (!StringUtils.hasText(realm)) { realm = DEF_REALM; } - RootBeanDefinition filter = null; + BeanDefinitionBuilder filterBuilder = BeanDefinitionBuilder.rootBeanDefinition(BasicAuthenticationFilter.class); - if (basicAuthElt != null || autoConfig) { - BeanDefinitionBuilder filterBuilder = BeanDefinitionBuilder.rootBeanDefinition(BasicAuthenticationFilter.class); + String entryPointId; - String entryPointId; - - if (basicAuthElt != null && StringUtils.hasText(basicAuthElt.getAttribute(ATT_ENTRY_POINT_REF))) { + if (basicAuthElt != null) { + if (StringUtils.hasText(basicAuthElt.getAttribute(ATT_ENTRY_POINT_REF))) { basicEntryPoint = new RuntimeBeanReference(basicAuthElt.getAttribute(ATT_ENTRY_POINT_REF)); - } else { - RootBeanDefinition entryPoint = new RootBeanDefinition(BasicAuthenticationEntryPoint.class); - entryPoint.setSource(pc.extractSource(httpElt)); - entryPoint.getPropertyValues().addPropertyValue("realmName", realm); - entryPointId = pc.getReaderContext().generateBeanName(entryPoint); - pc.registerBeanComponent(new BeanComponentDefinition(entryPoint, entryPointId)); - basicEntryPoint = new RuntimeBeanReference(entryPointId); } - filterBuilder.addPropertyValue("authenticationManager", authManager); - filterBuilder.addPropertyValue("authenticationEntryPoint", basicEntryPoint); - filter = (RootBeanDefinition) filterBuilder.getBeanDefinition(); + injectAuthenticationDetailsSource(basicAuthElt, filterBuilder); + } - basicFilter = filter; + if (basicEntryPoint == null) { + RootBeanDefinition entryPoint = new RootBeanDefinition(BasicAuthenticationEntryPoint.class); + entryPoint.setSource(pc.extractSource(httpElt)); + entryPoint.getPropertyValues().addPropertyValue("realmName", realm); + entryPointId = pc.getReaderContext().generateBeanName(entryPoint); + pc.registerBeanComponent(new BeanComponentDefinition(entryPoint, entryPointId)); + basicEntryPoint = new RuntimeBeanReference(entryPointId); + } + + filterBuilder.addPropertyValue("authenticationManager", authManager); + filterBuilder.addPropertyValue("authenticationEntryPoint", basicEntryPoint); + basicFilter = filterBuilder.getBeanDefinition(); } void createX509Filter(BeanReference authManager) { @@ -339,6 +348,9 @@ final class AuthenticationConfigBuilder { filterBuilder.addPropertyValue("principalExtractor", extractor.getBeanDefinition()); } + + injectAuthenticationDetailsSource(x509Elt, filterBuilder); + filter = (RootBeanDefinition) filterBuilder.getBeanDefinition(); createPrauthEntryPoint(x509Elt); @@ -348,6 +360,14 @@ final class AuthenticationConfigBuilder { x509Filter = filter; } + private void injectAuthenticationDetailsSource(Element elt, BeanDefinitionBuilder filterBuilder) { + String authDetailsSourceRef = elt.getAttribute(AuthenticationConfigBuilder.ATT_AUTH_DETAILS_SOURCE_REF); + + if (StringUtils.hasText(authDetailsSourceRef)) { + filterBuilder.addPropertyReference("authenticationDetailsSource", authDetailsSourceRef); + } + } + private void createX509Provider() { Element x509Elt = DomUtils.getChildElementByTagName(httpElt, Elements.X509); BeanDefinition provider = new RootBeanDefinition(PreAuthenticatedAuthenticationProvider.class); diff --git a/config/src/main/java/org/springframework/security/config/http/FormLoginBeanDefinitionParser.java b/config/src/main/java/org/springframework/security/config/http/FormLoginBeanDefinitionParser.java index e202253c48..ee95ef91af 100644 --- a/config/src/main/java/org/springframework/security/config/http/FormLoginBeanDefinitionParser.java +++ b/config/src/main/java/org/springframework/security/config/http/FormLoginBeanDefinitionParser.java @@ -68,6 +68,7 @@ public class FormLoginBeanDefinitionParser { // Only available with form-login String usernameParameter = null; String passwordParameter = null; + String authDetailsSourceRef = null; Object source = null; @@ -83,6 +84,8 @@ public class FormLoginBeanDefinitionParser { loginPage = elt.getAttribute(ATT_LOGIN_PAGE); successHandlerRef = elt.getAttribute(ATT_SUCCESS_HANDLER_REF); failureHandlerRef = elt.getAttribute(ATT_FAILURE_HANDLER_REF); + authDetailsSourceRef = elt.getAttribute(AuthenticationConfigBuilder.ATT_AUTH_DETAILS_SOURCE_REF); + if (!StringUtils.hasText(loginPage)) { loginPage = null; @@ -93,7 +96,7 @@ public class FormLoginBeanDefinitionParser { } filterBean = createFilterBean(loginUrl, defaultTargetUrl, alwaysUseDefault, loginPage, authenticationFailureUrl, - successHandlerRef, failureHandlerRef); + successHandlerRef, failureHandlerRef, authDetailsSourceRef); if (StringUtils.hasText(usernameParameter)) { filterBean.getPropertyValues().addPropertyValue("usernameParameter", usernameParameter); @@ -114,7 +117,8 @@ public class FormLoginBeanDefinitionParser { } private RootBeanDefinition createFilterBean(String loginUrl, String defaultTargetUrl, String alwaysUseDefault, - String loginPage, String authenticationFailureUrl, String successHandlerRef, String failureHandlerRef) { + String loginPage, String authenticationFailureUrl, String successHandlerRef, String failureHandlerRef, + String authDetailsSourceRef) { BeanDefinitionBuilder filterBuilder = BeanDefinitionBuilder.rootBeanDefinition(filterClassName); @@ -136,6 +140,10 @@ public class FormLoginBeanDefinitionParser { filterBuilder.addPropertyValue("authenticationSuccessHandler", successHandler.getBeanDefinition()); } + if (StringUtils.hasText(authDetailsSourceRef)) { + filterBuilder.addPropertyReference("authenticationDetailsSource", authDetailsSourceRef); + } + if (sessionStrategy != null) { filterBuilder.addPropertyValue("sessionAuthenticationStrategy", sessionStrategy); } 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 fad301d6b6..0c1af2800c 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 @@ -187,10 +187,10 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser { logger.info("Checking sorted filter chain: " + filters); for(int i=0; i < filters.size(); i++) { - OrderDecorator filter = (OrderDecorator)filters.get(i); + OrderDecorator filter = filters.get(i); if (i > 0) { - OrderDecorator previous = (OrderDecorator)filters.get(i-1); + OrderDecorator previous = filters.get(i-1); if (filter.getOrder() == previous.getOrder()) { pc.getReaderContext().error("Filter beans '" + filter.bean + "' and '" + previous.bean + "' have the same 'order' value. When using custom filters, " + 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 a37f10bc51..2ac422207e 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 @@ -381,6 +381,10 @@ form-login.attlist &= form-login.attlist &= ## Reference to an AuthenticationFailureHandler bean which should be used to handle a failed authentication request. Should not be used in combination with authentication-failure-url as the implementation should always deal with navigation to the subsequent destination attribute authentication-failure-handler-ref {xsd:token}? +form-login.attlist &= + ## Reference to an AuthenticationDetailsSource which will be used by the authentication filter + attribute authentication-details-source-ref {xsd:token}? + openid-login = ## Sets up form login for authentication with an Open ID identity @@ -451,6 +455,9 @@ http-basic = http-basic.attlist &= ## Sets the AuthenticationEntryPoint which is used by the BasicAuthenticationFilter. attribute entry-point-ref {xsd:token}? +http-basic.attlist &= + ## Reference to an AuthenticationDetailsSource which will be used by the authentication filter + attribute authentication-details-source-ref {xsd:token}? session-management = element session-management {session-management.attlist, concurrency-control?} @@ -565,6 +572,8 @@ x509.attlist &= x509.attlist &= ## Explicitly specifies which user-service should be used to load user data for X.509 authenticated clients. If ommitted, the default user-service will be used. user-service-ref? +x509.attlist &= + attribute authentication-details-source-ref {xsd:token}? jee = ## Adds a J2eePreAuthenticatedProcessingFilter to the filter chain to provide integration with container authentication. 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 023e450aff..1286a74689 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 @@ -926,6 +926,11 @@ Reference to an AuthenticationFailureHandler bean which should be used to handle a failed authentication request. Should not be used in combination with authentication-failure-url as the implementation should always deal with navigation to the subsequent destination + + + Reference to an AuthenticationDetailsSource which will be used by the authentication filter + + @@ -1059,6 +1064,11 @@ Sets the AuthenticationEntryPoint which is used by the BasicAuthenticationFilter. + + + Reference to an AuthenticationDetailsSource which will be used by the authentication filter + + @@ -1167,7 +1177,7 @@ - Allows a custom implementation of RememberMeServices to be used. Note that this implementation should return RememberMeAuthenticationToken instances with the same "key" value as specified in the remember-me element. Alternatively it should register its own AuthenticationProvider. + Allows a custom implementation of RememberMeServices to be used. Note that this implementation should return RememberMeAuthenticationToken instances with the same "key" value as specified in the remember-me element. Alternatively it should register its own AuthenticationProvider. It should also implement the LogoutHandler interface, which will be invoked when a user logs out. Typically the remember-me cookie would be removed on logout. @@ -1217,6 +1227,7 @@ A reference to a user-service (or UserDetailsService bean) Id + Adds a J2eePreAuthenticatedProcessingFilter to the filter chain to provide integration with container authentication. diff --git a/config/src/test/groovy/org/springframework/security/config/http/AbstractHttpConfigTests.groovy b/config/src/test/groovy/org/springframework/security/config/http/AbstractHttpConfigTests.groovy index df25edc7de..205b630fca 100644 --- a/config/src/test/groovy/org/springframework/security/config/http/AbstractHttpConfigTests.groovy +++ b/config/src/test/groovy/org/springframework/security/config/http/AbstractHttpConfigTests.groovy @@ -1,18 +1,11 @@ package org.springframework.security.config.http -import groovy.lang.Closure; -import groovy.xml.MarkupBuilder -import java.util.List; - -import javax.servlet.Filter; - +import javax.servlet.Filter import org.springframework.mock.web.MockFilterChain import org.springframework.mock.web.MockHttpServletRequest import org.springframework.mock.web.MockHttpServletResponse import org.springframework.security.config.AbstractXmlConfigTests import org.springframework.security.config.BeanIds -import org.springframework.security.config.util.InMemoryXmlApplicationContext -import org.springframework.security.core.context.SecurityContextHolder import org.springframework.security.web.FilterChainProxy import org.springframework.security.web.FilterInvocation 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 7fb9a2d215..09cc1b6da4 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 @@ -44,7 +44,8 @@ import org.springframework.security.web.savedrequest.RequestCacheAwareFilter; import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter; import org.springframework.security.web.session.SessionManagementFilter; -import groovy.lang.Closure; +import groovy.lang.Closure +import org.springframework.security.openid.OpenIDAuthenticationFilter; class MiscHttpConfigTests extends AbstractHttpConfigTests { def 'Minimal configuration parses'() { @@ -502,6 +503,23 @@ class MiscHttpConfigTests extends AbstractHttpConfigTests { roles.contains 'ROLE_user' roles.contains 'ROLE_c' } + + def authenticationDetailsSourceInjectionSucceeds() { + xml.http() { + 'form-login'('authentication-details-source-ref' : 'adsr') + 'openid-login' ('authentication-details-source-ref' : 'adsr') + 'http-basic' ('authentication-details-source-ref' : 'adsr') + 'x509' ('authentication-details-source-ref' : 'adsr') + } + bean('adsr', 'org.springframework.security.web.authentication.WebAuthenticationDetailsSource') + createAppContext() + def adsr = appContext.getBean('adsr') + expect: + getFilter(UsernamePasswordAuthenticationFilter).authenticationDetailsSource == adsr + getFilter(OpenIDAuthenticationFilter).authenticationDetailsSource == adsr + getFilter(BasicAuthenticationFilter).authenticationDetailsSource == adsr + getFilter(X509AuthenticationFilter).authenticationDetailsSource == adsr + } } class MockEntryPoint extends LoginUrlAuthenticationEntryPoint { diff --git a/web/src/main/java/org/springframework/security/web/authentication/preauth/AbstractPreAuthenticatedProcessingFilter.java b/web/src/main/java/org/springframework/security/web/authentication/preauth/AbstractPreAuthenticatedProcessingFilter.java index 7b3e2c3ac1..637ff119c0 100755 --- a/web/src/main/java/org/springframework/security/web/authentication/preauth/AbstractPreAuthenticatedProcessingFilter.java +++ b/web/src/main/java/org/springframework/security/web/authentication/preauth/AbstractPreAuthenticatedProcessingFilter.java @@ -196,6 +196,10 @@ public abstract class AbstractPreAuthenticatedProcessingFilter extends GenericFi this.authenticationDetailsSource = authenticationDetailsSource; } + protected AuthenticationDetailsSource getAuthenticationDetailsSource() { + return authenticationDetailsSource; + } + /** * @param authenticationManager * The AuthenticationManager to use