2
0
Эх сурвалжийг харах

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.

Luke Taylor 15 жил өмнө
parent
commit
4bd41cbf72

+ 1 - 1
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'),

+ 38 - 18
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;
-
-        if (basicAuthElt != null || autoConfig) {
-            BeanDefinitionBuilder filterBuilder = BeanDefinitionBuilder.rootBeanDefinition(BasicAuthenticationFilter.class);
+        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);
+
+        }
+
+        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);
         }
 
-        basicFilter = filter;
+        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);

+ 10 - 2
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);
         }

+ 2 - 2
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, " +

+ 9 - 0
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.

+ 12 - 1
config/src/main/resources/org/springframework/security/config/spring-security-3.1.xsd

@@ -926,6 +926,11 @@
         <xs:documentation>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</xs:documentation>
       </xs:annotation>
     </xs:attribute>
+    <xs:attribute name="authentication-details-source-ref" type="xs:token">
+      <xs:annotation>
+        <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication filter</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
   </xs:attributeGroup>
   
   <xs:element name="attribute-exchange"><xs:annotation>
@@ -1059,6 +1064,11 @@
         <xs:documentation>Sets the AuthenticationEntryPoint which is used by the BasicAuthenticationFilter.</xs:documentation>
       </xs:annotation>
     </xs:attribute>
+    <xs:attribute name="authentication-details-source-ref" type="xs:token">
+      <xs:annotation>
+        <xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication filter</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
   </xs:attributeGroup>
   
   <xs:attributeGroup name="session-management.attlist">
@@ -1167,7 +1177,7 @@
   <xs:attributeGroup name="remember-me-services-ref">
     <xs:attribute name="services-ref" type="xs:token">
       <xs:annotation>
-        <xs:documentation>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.</xs:documentation>
+        <xs:documentation>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.</xs:documentation>
       </xs:annotation>
     </xs:attribute>
   </xs:attributeGroup>
@@ -1217,6 +1227,7 @@
         <xs:documentation>A reference to a user-service (or UserDetailsService bean) Id</xs:documentation>
       </xs:annotation>
     </xs:attribute>
+    <xs:attribute name="authentication-details-source-ref" type="xs:token"/>
   </xs:attributeGroup>
   <xs:element name="jee"><xs:annotation>
       <xs:documentation>Adds a J2eePreAuthenticatedProcessingFilter to the filter chain to provide integration with container authentication.</xs:documentation>

+ 1 - 8
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
 

+ 19 - 1
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 {

+ 4 - 0
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<HttpServletRequest, ?> getAuthenticationDetailsSource() {
+        return authenticationDetailsSource;
+    }
+
     /**
      * @param authenticationManager
      *            The AuthenticationManager to use