mirror of
				https://github.com/spring-projects/spring-security.git
				synced 2025-10-30 22:28:46 +00:00 
			
		
		
		
	Refactoring HTTP config tests to use spock and groovy MarkupBuilder
This commit is contained in:
		
							parent
							
								
									080430150a
								
							
						
					
					
						commit
						b0758dd8de
					
				| @ -14,6 +14,7 @@ allprojects { | |||||||
|         mavenRepo name: 'SpringSource Maven Snapshot Repo', urls: 'http://maven.springframework.org/snapshot/' |         mavenRepo name: 'SpringSource Maven Snapshot Repo', urls: 'http://maven.springframework.org/snapshot/' | ||||||
|         mavenRepo name: 'SpringSource Enterprise Release', urls: 'http://repository.springsource.com/maven/bundles/release' |         mavenRepo name: 'SpringSource Enterprise Release', urls: 'http://repository.springsource.com/maven/bundles/release' | ||||||
|         mavenRepo name: 'SpringSource Enterprise External', urls: 'http://repository.springsource.com/maven/bundles/external' |         mavenRepo name: 'SpringSource Enterprise External', urls: 'http://repository.springsource.com/maven/bundles/external' | ||||||
|  |         mavenRepo(name: 'Spock Snapshots', urls: 'http://m2repo.spockframework.org/snapshots') | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,5 +1,7 @@ | |||||||
| // Config Module build file | // Config Module build file | ||||||
| 
 | 
 | ||||||
|  | apply plugin: 'groovy' | ||||||
|  | 
 | ||||||
| compileTestJava.dependsOn(':spring-security-core:compileTestJava') | compileTestJava.dependsOn(':spring-security-core:compileTestJava') | ||||||
| 
 | 
 | ||||||
| dependencies { | dependencies { | ||||||
| @ -14,13 +16,17 @@ dependencies { | |||||||
| 
 | 
 | ||||||
|     provided "javax.servlet:servlet-api:2.5" |     provided "javax.servlet:servlet-api:2.5" | ||||||
| 
 | 
 | ||||||
|  |     groovy group: 'org.codehaus.groovy', name: 'groovy', version: '1.7.1' | ||||||
|  | 
 | ||||||
|     testCompile project(':spring-security-ldap'), |     testCompile project(':spring-security-ldap'), | ||||||
|                 project(':spring-security-openid'), |                 project(':spring-security-openid'), | ||||||
|  |                 'org.openid4java:openid4java-nodeps:0.9.5', | ||||||
|                 files(this.project(':spring-security-core').sourceSets.test.classesDir), |                 files(this.project(':spring-security-core').sourceSets.test.classesDir), | ||||||
|                 'javax.annotation:jsr250-api:1.0', |                 'javax.annotation:jsr250-api:1.0', | ||||||
|                 "org.springframework.ldap:spring-ldap-core:$springLdapVersion", |                 "org.springframework.ldap:spring-ldap-core:$springLdapVersion", | ||||||
|                 "org.springframework:spring-jdbc:$springVersion", |                 "org.springframework:spring-jdbc:$springVersion", | ||||||
|                 "org.springframework:spring-tx:$springVersion" |                 "org.springframework:spring-tx:$springVersion", | ||||||
|  |                 'org.spockframework:spock-core:0.4-groovy-1.7' | ||||||
| 
 | 
 | ||||||
|     testRuntime "hsqldb:hsqldb:$hsqlVersion", |     testRuntime "hsqldb:hsqldb:$hsqlVersion", | ||||||
|                 "cglib:cglib-nodep:2.2" |                 "cglib:cglib-nodep:2.2" | ||||||
|  | |||||||
| @ -0,0 +1,71 @@ | |||||||
|  | package org.springframework.security.config | ||||||
|  | 
 | ||||||
|  | import static org.springframework.security.config.ConfigTestUtils.AUTH_PROVIDER_XML; | ||||||
|  | 
 | ||||||
|  | import groovy.xml.MarkupBuilder | ||||||
|  | import java.util.List; | ||||||
|  | import java.util.Map; | ||||||
|  | 
 | ||||||
|  | import org.springframework.context.support.AbstractXmlApplicationContext; | ||||||
|  | import org.springframework.security.config.util.InMemoryXmlApplicationContext | ||||||
|  | import org.springframework.security.core.context.SecurityContextHolder | ||||||
|  | 
 | ||||||
|  | import spock.lang.Specification | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * | ||||||
|  |  * @author Luke Taylor | ||||||
|  |  */ | ||||||
|  | abstract class AbstractXmlConfigTests extends Specification { | ||||||
|  |     AbstractXmlApplicationContext appContext; | ||||||
|  |     Writer writer; | ||||||
|  |     MarkupBuilder xml; | ||||||
|  | 
 | ||||||
|  |     def setup() { | ||||||
|  |         writer = new StringWriter() | ||||||
|  |         xml = new MarkupBuilder(writer) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def cleanup() { | ||||||
|  |         if (appContext != null) { | ||||||
|  |             appContext.close(); | ||||||
|  |             appContext = null; | ||||||
|  |         } | ||||||
|  |         SecurityContextHolder.clearContext(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def bean(String name, Class clazz) { | ||||||
|  |         xml.'b:bean'(id: name, 'class': clazz.name) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def bean(String name, String clazz) { | ||||||
|  |         xml.'b:bean'(id: name, 'class': clazz) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def bean(String name, String clazz, List constructorArgs) { | ||||||
|  |         xml.'b:bean'(id: name, 'class': clazz) { | ||||||
|  |             constructorArgs.each { val -> | ||||||
|  |                 'b:constructor-arg'(value: val) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def bean(String name, String clazz, Map properties, Map refs) { | ||||||
|  |         xml.'b:bean'(id: name, 'class': clazz) { | ||||||
|  |             properties.each {key, val -> | ||||||
|  |                 'b:property'(name: key, value: val) | ||||||
|  |             } | ||||||
|  |             refs.each {key, val -> | ||||||
|  |                 'b:property'(name: key, ref: val) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def createAppContext() { | ||||||
|  |         createAppContext(AUTH_PROVIDER_XML) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def createAppContext(String extraXml) { | ||||||
|  |         appContext = new InMemoryXmlApplicationContext(writer.toString() + extraXml); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,68 @@ | |||||||
|  | package org.springframework.security.config.http | ||||||
|  | 
 | ||||||
|  | import groovy.lang.Closure; | ||||||
|  | import groovy.xml.MarkupBuilder | ||||||
|  | import java.util.List; | ||||||
|  | 
 | ||||||
|  | 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 | ||||||
|  | 
 | ||||||
|  | abstract class AbstractHttpConfigTests extends AbstractXmlConfigTests { | ||||||
|  |     final int AUTO_CONFIG_FILTERS = 11; | ||||||
|  | 
 | ||||||
|  |     def httpAutoConfig(Closure c) { | ||||||
|  |         xml.http('auto-config': 'true', c) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def httpAutoConfig(String matcher, Closure c) { | ||||||
|  |         xml.http(['auto-config': 'true', 'request-matcher': matcher], c) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def interceptUrl(String path, String authz) { | ||||||
|  |         xml.'intercept-url'(pattern: path, access: authz) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def interceptUrl(String path, String httpMethod, String authz) { | ||||||
|  |         xml.'intercept-url'(pattern: path, method: httpMethod, access: authz) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     def interceptUrlNoFilters(String path) { | ||||||
|  |         xml.'intercept-url'(pattern: path, filters: 'none') | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     Filter getFilter(Class type) { | ||||||
|  |         List filters = getFilters("/any"); | ||||||
|  | 
 | ||||||
|  |         for (f in filters) { | ||||||
|  |             if (f.class.isAssignableFrom(type)) { | ||||||
|  |                 return f; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return null; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     List getFilters(String url) { | ||||||
|  |         FilterChainProxy fcp = appContext.getBean(BeanIds.FILTER_CHAIN_PROXY); | ||||||
|  |         return fcp.getFilters(url) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     FilterInvocation createFilterinvocation(String path, String method) { | ||||||
|  |         MockHttpServletRequest request = new MockHttpServletRequest(); | ||||||
|  |         request.setMethod(method); | ||||||
|  |         request.setRequestURI(null); | ||||||
|  |         request.setServletPath(path); | ||||||
|  | 
 | ||||||
|  |         return new FilterInvocation(request, new MockHttpServletResponse(), new MockFilterChain()); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,71 @@ | |||||||
|  | package org.springframework.security.config.http | ||||||
|  | 
 | ||||||
|  | import org.springframework.beans.factory.BeanCreationException | ||||||
|  | import org.springframework.beans.factory.parsing.BeanDefinitionParsingException | ||||||
|  | import org.springframework.security.util.FieldUtils | ||||||
|  | import org.springframework.security.web.access.AccessDeniedHandlerImpl | ||||||
|  | import org.springframework.security.web.access.ExceptionTranslationFilter | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * | ||||||
|  |  * @author Luke Taylor | ||||||
|  |  */ | ||||||
|  | class AccessDeniedConfigTests extends AbstractHttpConfigTests { | ||||||
|  |     private static final String ACCESS_DENIED_PAGE = 'access-denied-page'; | ||||||
|  | 
 | ||||||
|  |     def accessDeniedPageAttributeIsSupported() { | ||||||
|  |         httpAccessDeniedPage ('/accessDenied') { } | ||||||
|  |         createAppContext(); | ||||||
|  | 
 | ||||||
|  |         expect: | ||||||
|  |         getFilter(ExceptionTranslationFilter.class).accessDeniedHandler.errorPage == '/accessDenied' | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def invalidAccessDeniedUrlIsDetected() { | ||||||
|  |         when: | ||||||
|  |         httpAccessDeniedPage ('noLeadingSlash') { } | ||||||
|  |         createAppContext(); | ||||||
|  |         then: | ||||||
|  |         BeanCreationException e = thrown() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def accessDeniedHandlerIsSetCorectly() { | ||||||
|  |         httpAutoConfig() { | ||||||
|  |             'access-denied-handler'(ref: 'adh') | ||||||
|  |         } | ||||||
|  |         bean('adh', AccessDeniedHandlerImpl) | ||||||
|  |         createAppContext(); | ||||||
|  | 
 | ||||||
|  |         def filter = getFilter(ExceptionTranslationFilter.class); | ||||||
|  |         def adh = appContext.getBean("adh"); | ||||||
|  | 
 | ||||||
|  |         expect: | ||||||
|  |         filter.accessDeniedHandler == adh | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def void accessDeniedPageAndAccessDeniedHandlerAreMutuallyExclusive() { | ||||||
|  |         when: | ||||||
|  |         httpAccessDeniedPage ('/accessDenied') { | ||||||
|  |             'access-denied-handler'('error-page': '/go-away') | ||||||
|  |         } | ||||||
|  |         createAppContext(); | ||||||
|  |         then: | ||||||
|  |         BeanDefinitionParsingException e = thrown() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def void accessDeniedHandlerPageAndRefAreMutuallyExclusive() { | ||||||
|  |         when: | ||||||
|  |         httpAutoConfig { | ||||||
|  |             'access-denied-handler'('error-page': '/go-away', ref: 'adh') | ||||||
|  |         } | ||||||
|  |         createAppContext(); | ||||||
|  |         bean('adh', AccessDeniedHandlerImpl) | ||||||
|  |         then: | ||||||
|  |         BeanDefinitionParsingException e = thrown() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def httpAccessDeniedPage(String page, Closure c) { | ||||||
|  |         xml.http(['auto-config': 'true', 'access-denied-page': page], c) | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,71 @@ | |||||||
|  | package org.springframework.security.config.http | ||||||
|  | 
 | ||||||
|  | import org.springframework.beans.factory.BeanCreationException | ||||||
|  | import org.springframework.security.util.FieldUtils | ||||||
|  | import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler; | ||||||
|  | import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; | ||||||
|  | import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * | ||||||
|  |  * @author Luke Taylor | ||||||
|  |  */ | ||||||
|  | class FormLoginConfigTests extends AbstractHttpConfigTests { | ||||||
|  | 
 | ||||||
|  |     def formLoginWithNoLoginPageAddsDefaultLoginPageFilter() { | ||||||
|  |         httpAutoConfig('ant') { | ||||||
|  |             form-login() | ||||||
|  |         } | ||||||
|  |         createAppContext() | ||||||
|  |         filtersMatchExpectedAutoConfigList(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def 'Form login alwaysUseDefaultTarget sets correct property'() { | ||||||
|  |         xml.http { | ||||||
|  |             'form-login'('default-target-url':'/default', 'always-use-default-target': 'true') | ||||||
|  |         } | ||||||
|  |         createAppContext() | ||||||
|  |         def filter = getFilter(UsernamePasswordAuthenticationFilter.class); | ||||||
|  | 
 | ||||||
|  |         expect: | ||||||
|  |         FieldUtils.getFieldValue(filter, 'successHandler.defaultTargetUrl') == '/default'; | ||||||
|  |         FieldUtils.getFieldValue(filter, 'successHandler.alwaysUseDefaultTargetUrl') == true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def invalidLoginPageIsDetected() { | ||||||
|  |         when: | ||||||
|  |         xml.http { | ||||||
|  |             'form-login'('login-page': 'noLeadingSlash') | ||||||
|  |         } | ||||||
|  |         createAppContext() | ||||||
|  | 
 | ||||||
|  |         then: | ||||||
|  |         BeanCreationException e = thrown(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def invalidDefaultTargetUrlIsDetected() { | ||||||
|  |         when: | ||||||
|  |         xml.http { | ||||||
|  |             'form-login'('default-target-url': 'noLeadingSlash') | ||||||
|  |         } | ||||||
|  |         createAppContext() | ||||||
|  | 
 | ||||||
|  |         then: | ||||||
|  |         BeanCreationException e = thrown(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def customSuccessAndFailureHandlersCanBeSetThroughTheNamespace() { | ||||||
|  |         xml.http { | ||||||
|  |             'form-login'('authentication-success-handler-ref': 'sh', 'authentication-failure-handler-ref':'fh') | ||||||
|  |         } | ||||||
|  |         bean('sh', SavedRequestAwareAuthenticationSuccessHandler.class.name) | ||||||
|  |         bean('fh', SimpleUrlAuthenticationFailureHandler.class.name) | ||||||
|  |         createAppContext() | ||||||
|  | 
 | ||||||
|  |         def apf = getFilter(UsernamePasswordAuthenticationFilter.class); | ||||||
|  | 
 | ||||||
|  |         expect: | ||||||
|  |         FieldUtils.getFieldValue(apf, "successHandler") == appContext.getBean("sh"); | ||||||
|  |         FieldUtils.getFieldValue(apf, "failureHandler") == appContext.getBean("fh") | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,149 @@ | |||||||
|  | package org.springframework.security.config.http | ||||||
|  | 
 | ||||||
|  | import javax.servlet.http.HttpServletRequest | ||||||
|  | import org.springframework.beans.factory.parsing.BeanDefinitionParsingException | ||||||
|  | import org.springframework.mock.web.MockFilterChain | ||||||
|  | import org.springframework.mock.web.MockHttpServletRequest | ||||||
|  | import org.springframework.mock.web.MockHttpServletResponse | ||||||
|  | import org.springframework.security.config.BeanIds | ||||||
|  | import org.springframework.security.openid.OpenIDAuthenticationFilter | ||||||
|  | import org.springframework.security.openid.OpenIDAuthenticationToken | ||||||
|  | import org.springframework.security.openid.OpenIDConsumer | ||||||
|  | import org.springframework.security.openid.OpenIDConsumerException | ||||||
|  | import org.springframework.security.web.FilterChainProxy | ||||||
|  | import org.springframework.security.web.access.ExceptionTranslationFilter | ||||||
|  | import org.springframework.security.web.authentication.rememberme.AbstractRememberMeServices | ||||||
|  | import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * | ||||||
|  |  * @author Luke Taylor | ||||||
|  |  */ | ||||||
|  | class OpenIDConfigTests extends AbstractHttpConfigTests { | ||||||
|  |     def openIDAndFormLoginWorkTogether() { | ||||||
|  |         xml.http() { | ||||||
|  |             'openid-login'() | ||||||
|  |             'form-login'() | ||||||
|  |         } | ||||||
|  |         createAppContext() | ||||||
|  | 
 | ||||||
|  |         def etf = getFilter(ExceptionTranslationFilter) | ||||||
|  |         def ap = etf.getAuthenticationEntryPoint(); | ||||||
|  | 
 | ||||||
|  |         expect: | ||||||
|  |         ap.loginFormUrl == "/spring_security_login" | ||||||
|  |         // Default login filter should be present since we haven't specified any login URLs | ||||||
|  |         getFilter(DefaultLoginPageGeneratingFilter) != null | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def formLoginEntryPointTakesPrecedenceIfLoginUrlIsSet() { | ||||||
|  |         xml.http() { | ||||||
|  |             'openid-login'() | ||||||
|  |             'form-login'('login-page': '/form-page') | ||||||
|  |         } | ||||||
|  |         createAppContext() | ||||||
|  | 
 | ||||||
|  |         expect: | ||||||
|  |         getFilter(ExceptionTranslationFilter).authenticationEntryPoint.loginFormUrl == '/form-page' | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def openIDEntryPointTakesPrecedenceIfLoginUrlIsSet() { | ||||||
|  |         xml.http() { | ||||||
|  |             'openid-login'('login-page': '/openid-page') | ||||||
|  |             'form-login'() | ||||||
|  |         } | ||||||
|  |         createAppContext() | ||||||
|  | 
 | ||||||
|  |         expect: | ||||||
|  |         getFilter(ExceptionTranslationFilter).authenticationEntryPoint.loginFormUrl == '/openid-page' | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def multipleLoginPagesCausesError() { | ||||||
|  |         when: | ||||||
|  |         xml.http() { | ||||||
|  |             'openid-login'('login-page': '/openid-page') | ||||||
|  |             'form-login'('login-page': '/form-page') | ||||||
|  |         } | ||||||
|  |         createAppContext() | ||||||
|  |         then: | ||||||
|  |         thrown(BeanDefinitionParsingException) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def openIDAndRememberMeWorkTogether() { | ||||||
|  |         xml.http() { | ||||||
|  |             interceptUrl('/**', 'ROLE_NOBODY') | ||||||
|  |             'openid-login'() | ||||||
|  |             'remember-me'() | ||||||
|  |         } | ||||||
|  |         createAppContext() | ||||||
|  | 
 | ||||||
|  |         // Default login filter should be present since we haven't specified any login URLs | ||||||
|  |         def loginFilter = getFilter(DefaultLoginPageGeneratingFilter) | ||||||
|  |         def openIDFilter = getFilter(OpenIDAuthenticationFilter) | ||||||
|  |         openIDFilter.setConsumer(new OpenIDConsumer() { | ||||||
|  |             public String beginConsumption(HttpServletRequest req, String claimedIdentity, String returnToUrl, String realm) | ||||||
|  |                     throws OpenIDConsumerException { | ||||||
|  |                 return "http://testopenid.com?openid.return_to=" + returnToUrl; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             public OpenIDAuthenticationToken endConsumption(HttpServletRequest req) throws OpenIDConsumerException { | ||||||
|  |                 throw new UnsupportedOperationException(); | ||||||
|  |             } | ||||||
|  |         }) | ||||||
|  |         Set<String> returnToUrlParameters = new HashSet<String>() | ||||||
|  |         returnToUrlParameters.add(AbstractRememberMeServices.DEFAULT_PARAMETER) | ||||||
|  |         openIDFilter.setReturnToUrlParameters(returnToUrlParameters) | ||||||
|  |         assert loginFilter.openIDrememberMeParameter != null | ||||||
|  | 
 | ||||||
|  |         MockHttpServletRequest request = new MockHttpServletRequest(); | ||||||
|  |         MockHttpServletResponse response = new MockHttpServletResponse(); | ||||||
|  | 
 | ||||||
|  |         when: "Initial request is made" | ||||||
|  |         FilterChainProxy fcp = appContext.getBean(BeanIds.FILTER_CHAIN_PROXY) | ||||||
|  |         request.setServletPath("/something.html") | ||||||
|  |         fcp.doFilter(request, response, new MockFilterChain()) | ||||||
|  |         then: "Redirected to login" | ||||||
|  |         response.getRedirectedUrl().endsWith("/spring_security_login") | ||||||
|  |         when: "Login page is requested" | ||||||
|  |         request.setServletPath("/spring_security_login") | ||||||
|  |         request.setRequestURI("/spring_security_login") | ||||||
|  |         response = new MockHttpServletResponse() | ||||||
|  |         fcp.doFilter(request, response, new MockFilterChain()) | ||||||
|  |         then: "Remember-me choice is added to page" | ||||||
|  |         response.getContentAsString().contains(AbstractRememberMeServices.DEFAULT_PARAMETER) | ||||||
|  |         when: "Login is submitted with remember-me selected" | ||||||
|  |         request.setRequestURI("/j_spring_openid_security_check") | ||||||
|  |         request.setParameter(OpenIDAuthenticationFilter.DEFAULT_CLAIMED_IDENTITY_FIELD, "http://hey.openid.com/") | ||||||
|  |         request.setParameter(AbstractRememberMeServices.DEFAULT_PARAMETER, "on") | ||||||
|  |         response = new MockHttpServletResponse(); | ||||||
|  |         fcp.doFilter(request, response, new MockFilterChain()); | ||||||
|  |         String expectedReturnTo = request.getRequestURL().append("?") | ||||||
|  |                                         .append(AbstractRememberMeServices.DEFAULT_PARAMETER) | ||||||
|  |                                         .append("=").append("on").toString(); | ||||||
|  |         then: "return_to URL contains remember-me choice" | ||||||
|  |         response.getRedirectedUrl() == "http://testopenid.com?openid.return_to=" + expectedReturnTo | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def openIDWithAttributeExchangeConfigurationIsParsedCorrectly() { | ||||||
|  |         xml.http() { | ||||||
|  |             'openid-login'() { | ||||||
|  |                 'attribute-exchange'() { | ||||||
|  |                     'openid-attribute'(name: 'nickname', type: 'http://schema.openid.net/namePerson/friendly') | ||||||
|  |                     'openid-attribute'(name: 'email', type: 'http://schema.openid.net/contact/email', required: 'true', | ||||||
|  |                             'count': '2') | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         createAppContext() | ||||||
|  | 
 | ||||||
|  |         List attributes = getFilter(OpenIDAuthenticationFilter).consumer.attributesToFetchFactory.createAttributeList('http://someid') | ||||||
|  | 
 | ||||||
|  |         expect: | ||||||
|  |         attributes.size() == 2 | ||||||
|  |         attributes[0].name == 'nickname' | ||||||
|  |         attributes[0].type == 'http://schema.openid.net/namePerson/friendly' | ||||||
|  |         attributes[0].required == false | ||||||
|  |         attributes[1].required == true | ||||||
|  |         attributes[1].getCount() == 2 | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,497 @@ | |||||||
|  | package org.springframework.security.config.http; | ||||||
|  | 
 | ||||||
|  | import java.util.Collection; | ||||||
|  | import java.util.Map; | ||||||
|  | import java.util.Iterator; | ||||||
|  | 
 | ||||||
|  | import javax.servlet.Filter | ||||||
|  | import javax.servlet.http.HttpServletRequest; | ||||||
|  | 
 | ||||||
|  | import org.springframework.beans.BeansException | ||||||
|  | import org.springframework.beans.factory.BeanCreationException; | ||||||
|  | import org.springframework.mock.web.MockFilterChain; | ||||||
|  | import org.springframework.mock.web.MockHttpServletRequest; | ||||||
|  | import org.springframework.mock.web.MockHttpServletResponse; | ||||||
|  | import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer; | ||||||
|  | import org.springframework.beans.factory.parsing.BeanDefinitionParsingException; | ||||||
|  | import org.springframework.context.support.AbstractXmlApplicationContext | ||||||
|  | import org.springframework.security.config.BeanIds; | ||||||
|  | import org.springframework.security.config.util.InMemoryXmlApplicationContext; | ||||||
|  | import org.springframework.security.core.context.SecurityContextHolder; | ||||||
|  | import org.springframework.security.util.FieldUtils; | ||||||
|  | import org.springframework.security.access.AccessDeniedException | ||||||
|  | import org.springframework.security.access.SecurityConfig; | ||||||
|  | import org.springframework.security.authentication.TestingAuthenticationToken | ||||||
|  | import org.springframework.security.config.MockUserServiceBeanPostProcessor; | ||||||
|  | import org.springframework.security.config.PostProcessedMockUserDetailsService; | ||||||
|  | import org.springframework.security.web.*; | ||||||
|  | import org.springframework.security.web.access.channel.ChannelProcessingFilter; | ||||||
|  | import org.springframework.security.web.access.ExceptionTranslationFilter; | ||||||
|  | import org.springframework.security.web.access.intercept.FilterSecurityInterceptor; | ||||||
|  | import org.springframework.security.web.authentication.* | ||||||
|  | import org.springframework.security.web.authentication.logout.LogoutFilter | ||||||
|  | import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler | ||||||
|  | import org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter | ||||||
|  | import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter | ||||||
|  | import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint | ||||||
|  | import org.springframework.security.web.authentication.www.BasicAuthenticationFilter | ||||||
|  | import org.springframework.security.web.context.*; | ||||||
|  | import org.springframework.security.web.savedrequest.HttpSessionRequestCache | ||||||
|  | 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; | ||||||
|  | 
 | ||||||
|  | class MiscHttpConfigTests extends AbstractHttpConfigTests { | ||||||
|  |     def 'Minimal configuration parses'() { | ||||||
|  |         setup: | ||||||
|  |         xml.http { | ||||||
|  |             'http-basic'() | ||||||
|  |         } | ||||||
|  |         createAppContext() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def httpAutoConfigSetsUpCorrectFilterList() { | ||||||
|  |         when: | ||||||
|  |         xml.http('auto-config': 'true') | ||||||
|  |         createAppContext() | ||||||
|  | 
 | ||||||
|  |         then: | ||||||
|  |         filtersMatchExpectedAutoConfigList('/anyurl'); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void filtersMatchExpectedAutoConfigList(String url) { | ||||||
|  |         def filterList = getFilters(url); | ||||||
|  |         Iterator<Filter> filters = filterList.iterator(); | ||||||
|  | 
 | ||||||
|  |         assert filters.next() instanceof SecurityContextPersistenceFilter | ||||||
|  |         assert filters.next() instanceof LogoutFilter | ||||||
|  |         Object authProcFilter = filters.next(); | ||||||
|  |         assert authProcFilter instanceof UsernamePasswordAuthenticationFilter | ||||||
|  |         assert filters.next() instanceof DefaultLoginPageGeneratingFilter | ||||||
|  |         assert filters.next() instanceof BasicAuthenticationFilter | ||||||
|  |         assert filters.next() instanceof RequestCacheAwareFilter | ||||||
|  |         assert filters.next() instanceof SecurityContextHolderAwareRequestFilter | ||||||
|  |         assert filters.next() instanceof AnonymousAuthenticationFilter | ||||||
|  |         assert filters.next() instanceof SessionManagementFilter | ||||||
|  |         assert filters.next() instanceof ExceptionTranslationFilter | ||||||
|  |         Object fsiObj = filters.next(); | ||||||
|  |         assert fsiObj instanceof FilterSecurityInterceptor | ||||||
|  |         def fsi = (FilterSecurityInterceptor) fsiObj; | ||||||
|  |         assert fsi.isObserveOncePerRequest() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def duplicateElementCausesError() { | ||||||
|  |         when: "Two http blocks are defined" | ||||||
|  |         xml.http('auto-config': 'true') | ||||||
|  |         xml.http('auto-config': 'true') | ||||||
|  |         createAppContext() | ||||||
|  | 
 | ||||||
|  |         then: | ||||||
|  |         BeanDefinitionParsingException e = thrown(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def filterListShouldBeEmptyForPatternWithNoFilters() { | ||||||
|  |         httpAutoConfig() { | ||||||
|  |             interceptUrlNoFilters('/unprotected') | ||||||
|  |         } | ||||||
|  |         createAppContext() | ||||||
|  | 
 | ||||||
|  |         expect: | ||||||
|  |         getFilters("/unprotected").size() == 0 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def regexPathsWorkCorrectly() { | ||||||
|  |         httpAutoConfig('regex') { | ||||||
|  |             interceptUrlNoFilters('\\A\\/[a-z]+') | ||||||
|  |         } | ||||||
|  |         createAppContext() | ||||||
|  | 
 | ||||||
|  |         expect: | ||||||
|  |         getFilters('/imlowercase').size() == 0 | ||||||
|  |         filtersMatchExpectedAutoConfigList('/MixedCase'); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def ciRegexPathsWorkCorrectly() { | ||||||
|  |         when: | ||||||
|  |         httpAutoConfig('ciRegex') { | ||||||
|  |             interceptUrlNoFilters('\\A\\/[a-z]+') | ||||||
|  |         } | ||||||
|  |         createAppContext() | ||||||
|  | 
 | ||||||
|  |         then: | ||||||
|  |         getFilters('/imMixedCase').size() == 0 | ||||||
|  |         filtersMatchExpectedAutoConfigList('/Im_caught_by_the_Universal_Match'); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // SEC-1152 | ||||||
|  |     def anonymousFilterIsAddedByDefault() { | ||||||
|  |         xml.http { | ||||||
|  |             'form-login'() | ||||||
|  |         } | ||||||
|  |         createAppContext() | ||||||
|  | 
 | ||||||
|  |         expect: | ||||||
|  |         getFilters("/anything")[5] instanceof AnonymousAuthenticationFilter | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def anonymousFilterIsRemovedIfDisabledFlagSet() { | ||||||
|  |         xml.http { | ||||||
|  |             'form-login'() | ||||||
|  |             'anonymous'(enabled: 'false') | ||||||
|  |         } | ||||||
|  |         createAppContext() | ||||||
|  | 
 | ||||||
|  |         expect: | ||||||
|  |         !(getFilters("/anything").get(5) instanceof AnonymousAuthenticationFilter) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def anonymousCustomAttributesAreSetCorrectly() { | ||||||
|  |         xml.http { | ||||||
|  |             'form-login'() | ||||||
|  |             'anonymous'(username: 'joe', 'granted-authority':'anonymity', key: 'customKey') | ||||||
|  |         } | ||||||
|  |         createAppContext() | ||||||
|  | 
 | ||||||
|  |         AnonymousAuthenticationFilter filter = getFilter(AnonymousAuthenticationFilter); | ||||||
|  | 
 | ||||||
|  |         expect: | ||||||
|  |         'customKey' == filter.getKey() | ||||||
|  |         'joe' == filter.userAttribute.password | ||||||
|  |         'anonymity' == filter.userAttribute.authorities[0].authority | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def httpMethodMatchIsSupported() { | ||||||
|  |         httpAutoConfig { | ||||||
|  |             interceptUrl '/secure*', 'DELETE', 'ROLE_SUPERVISOR' | ||||||
|  |             interceptUrl '/secure*', 'POST', 'ROLE_A,ROLE_B' | ||||||
|  |             interceptUrl '/**', 'ROLE_C' | ||||||
|  |         } | ||||||
|  |         createAppContext() | ||||||
|  | 
 | ||||||
|  |         def fids = getFilter(FilterSecurityInterceptor).getSecurityMetadataSource(); | ||||||
|  |         def attrs = fids.getAttributes(createFilterinvocation("/secure", "POST")); | ||||||
|  | 
 | ||||||
|  |         expect: | ||||||
|  |         attrs.size() == 2 | ||||||
|  |         attrs.contains(new SecurityConfig("ROLE_A")) | ||||||
|  |         attrs.contains(new SecurityConfig("ROLE_B")) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def oncePerRequestAttributeIsSupported() { | ||||||
|  |         xml.http('once-per-request': 'false') { | ||||||
|  |             'http-basic'() | ||||||
|  |         } | ||||||
|  |         createAppContext() | ||||||
|  | 
 | ||||||
|  |         expect: | ||||||
|  |         !getFilter(FilterSecurityInterceptor).isObserveOncePerRequest() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def httpBasicSupportsSeparateEntryPoint() { | ||||||
|  |         xml.http() { | ||||||
|  |             'http-basic'('entry-point-ref': 'ep') | ||||||
|  |         } | ||||||
|  |         bean('ep', BasicAuthenticationEntryPoint.class.name, ['realmName':'whocares'],[:]) | ||||||
|  |         createAppContext(); | ||||||
|  | 
 | ||||||
|  |         def baf = getFilter(BasicAuthenticationFilter) | ||||||
|  |         def etf = getFilter(ExceptionTranslationFilter) | ||||||
|  |         def ep = appContext.getBean("ep") | ||||||
|  | 
 | ||||||
|  |         expect: | ||||||
|  |         baf.authenticationEntryPoint == ep | ||||||
|  |         // Since no other authentication system is in use, this should also end up on the ETF | ||||||
|  |         etf.authenticationEntryPoint == ep | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def interceptUrlWithRequiresChannelAddsChannelFilterToStack() { | ||||||
|  |         httpAutoConfig { | ||||||
|  |             'intercept-url'(pattern: '/**', 'requires-channel': 'https') | ||||||
|  |         } | ||||||
|  |         createAppContext(); | ||||||
|  |         List filters = getFilters("/someurl"); | ||||||
|  | 
 | ||||||
|  |         expect: | ||||||
|  |         filters.size() == AUTO_CONFIG_FILTERS + 1 | ||||||
|  |         filters[0] instanceof ChannelProcessingFilter | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def portMappingsAreParsedCorrectly() { | ||||||
|  |         httpAutoConfig { | ||||||
|  |             'port-mappings'() { | ||||||
|  |                 'port-mapping'(http: '9080', https: '9443') | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         createAppContext(); | ||||||
|  | 
 | ||||||
|  |         def pm = (appContext.getBeansOfType(PortMapperImpl).values() as List)[0]; | ||||||
|  | 
 | ||||||
|  |         expect: | ||||||
|  |         pm.getTranslatedPortMappings().size() == 1 | ||||||
|  |         pm.lookupHttpPort(9443) == 9080 | ||||||
|  |         pm.lookupHttpsPort(9080) == 9443 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def externalFiltersAreTreatedCorrectly() { | ||||||
|  |         httpAutoConfig { | ||||||
|  |             'custom-filter'(position: 'FIRST', ref: '${customFilterRef}') | ||||||
|  |             'custom-filter'(after: 'LOGOUT_FILTER', ref: 'userFilter') | ||||||
|  |             'custom-filter'(before: 'SECURITY_CONTEXT_FILTER', ref: 'userFilter1') | ||||||
|  |         } | ||||||
|  |         bean('phc', PropertyPlaceholderConfigurer) | ||||||
|  |         bean('userFilter', SecurityContextHolderAwareRequestFilter) | ||||||
|  |         bean('userFilter1', SecurityContextPersistenceFilter) | ||||||
|  | 
 | ||||||
|  |         System.setProperty('customFilterRef', 'userFilter') | ||||||
|  |         createAppContext(); | ||||||
|  | 
 | ||||||
|  |         def filters = getFilters("/someurl"); | ||||||
|  | 
 | ||||||
|  |         expect: | ||||||
|  |         AUTO_CONFIG_FILTERS + 3 == filters.size(); | ||||||
|  |         filters[0] instanceof SecurityContextHolderAwareRequestFilter | ||||||
|  |         filters[1] instanceof SecurityContextPersistenceFilter | ||||||
|  |         filters[4] instanceof SecurityContextHolderAwareRequestFilter | ||||||
|  |         filters[1] instanceof SecurityContextPersistenceFilter | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def twoFiltersWithSameOrderAreRejected() { | ||||||
|  |         when: | ||||||
|  |         httpAutoConfig { | ||||||
|  |             'custom-filter'(position: 'LOGOUT_FILTER', ref: 'userFilter') | ||||||
|  |         } | ||||||
|  |         bean('userFilter', SecurityContextHolderAwareRequestFilter) | ||||||
|  |         createAppContext(); | ||||||
|  | 
 | ||||||
|  |         then: | ||||||
|  |         thrown(BeanDefinitionParsingException) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def x509SupportAddsFilterAtExpectedPosition() { | ||||||
|  |         httpAutoConfig { | ||||||
|  |             x509() | ||||||
|  |         } | ||||||
|  |         createAppContext() | ||||||
|  | 
 | ||||||
|  |         def filters = getFilters("/someurl") | ||||||
|  | 
 | ||||||
|  |         expect: | ||||||
|  |         getFilters("/someurl")[2] instanceof X509AuthenticationFilter | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def x509SubjectPrincipalRegexCanBeSetUsingPropertyPlaceholder() { | ||||||
|  |         httpAutoConfig { | ||||||
|  |             x509('subject-principal-regex':'${subject-principal-regex}') | ||||||
|  |         } | ||||||
|  |         bean('phc', PropertyPlaceholderConfigurer.class.name) | ||||||
|  |         System.setProperty("subject-principal-regex", "uid=(.*),"); | ||||||
|  |         createAppContext() | ||||||
|  |         def filter = getFilter(X509AuthenticationFilter) | ||||||
|  | 
 | ||||||
|  |         expect: | ||||||
|  |         filter.principalExtractor.subjectDnPattern.pattern() == "uid=(.*)," | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def invalidLogoutSuccessUrlIsDetected() { | ||||||
|  |         when: | ||||||
|  |         xml.http { | ||||||
|  |             'form-login'() | ||||||
|  |             'logout'('logout-success-url': 'noLeadingSlash') | ||||||
|  |         } | ||||||
|  |         createAppContext() | ||||||
|  | 
 | ||||||
|  |         then: | ||||||
|  |         BeanCreationException e = thrown() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def invalidLogoutUrlIsDetected() { | ||||||
|  |         when: | ||||||
|  |         xml.http { | ||||||
|  |             'logout'('logout-url': 'noLeadingSlash') | ||||||
|  |             'form-login'() | ||||||
|  |         } | ||||||
|  |         createAppContext() | ||||||
|  | 
 | ||||||
|  |         then: | ||||||
|  |         BeanCreationException e = thrown(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def logoutSuccessHandlerIsSetCorrectly() { | ||||||
|  |         xml.http { | ||||||
|  |             'form-login'() | ||||||
|  |             'logout'('success-handler-ref': 'logoutHandler') | ||||||
|  |         } | ||||||
|  |         bean('logoutHandler', SimpleUrlLogoutSuccessHandler) | ||||||
|  |         createAppContext() | ||||||
|  | 
 | ||||||
|  |         LogoutFilter filter = getFilter(LogoutFilter); | ||||||
|  | 
 | ||||||
|  |         expect: | ||||||
|  |         FieldUtils.getFieldValue(filter, "logoutSuccessHandler") == appContext.getBean("logoutHandler") | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def externalRequestCacheIsConfiguredCorrectly() { | ||||||
|  |         httpAutoConfig { | ||||||
|  |             'request-cache'(ref: 'cache') | ||||||
|  |         } | ||||||
|  |         bean('cache', HttpSessionRequestCache.class.name) | ||||||
|  |         createAppContext() | ||||||
|  | 
 | ||||||
|  |         expect: | ||||||
|  |         appContext.getBean("cache") == getFilter(ExceptionTranslationFilter.class).requestCache | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def customEntryPointIsSupported() { | ||||||
|  |         xml.http('auto-config': 'true', 'entry-point-ref': 'entryPoint') {} | ||||||
|  |         bean('entryPoint', MockEntryPoint.class.name) | ||||||
|  |         createAppContext() | ||||||
|  | 
 | ||||||
|  |         expect: | ||||||
|  |         getFilter(ExceptionTranslationFilter).getAuthenticationEntryPoint() instanceof MockEntryPoint | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def disablingSessionProtectionRemovesSessionManagementFilterIfNoInvalidSessionUrlSet() { | ||||||
|  |         httpAutoConfig { | ||||||
|  |             'session-management'('session-fixation-protection': 'none') | ||||||
|  |         } | ||||||
|  |         createAppContext() | ||||||
|  | 
 | ||||||
|  |         expect: | ||||||
|  |         !(getFilters("/someurl")[8] instanceof SessionManagementFilter) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def disablingSessionProtectionRetainsSessionManagementFilterInvalidSessionUrlSet() { | ||||||
|  |         httpAutoConfig { | ||||||
|  |             'session-management'('session-fixation-protection': 'none', 'invalid-session-url': '/timeoutUrl') | ||||||
|  |         } | ||||||
|  |         createAppContext() | ||||||
|  |         def filter = getFilters("/someurl")[8] | ||||||
|  | 
 | ||||||
|  |         expect: | ||||||
|  |         filter instanceof SessionManagementFilter | ||||||
|  |         filter.invalidSessionUrl == '/timeoutUrl' | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * See SEC-750. If the http security post processor causes beans to be instantiated too eagerly, they way miss | ||||||
|  |      * additional processing. In this method we have a UserDetailsService which is referenced from the namespace | ||||||
|  |      * and also has a post processor registered which will modify it. | ||||||
|  |      */ | ||||||
|  |     def httpElementDoesntInterfereWithBeanPostProcessing() { | ||||||
|  |         httpAutoConfig {} | ||||||
|  |         xml.'authentication-manager'() { | ||||||
|  |             'authentication-provider'('user-service-ref': 'myUserService') | ||||||
|  |         } | ||||||
|  |         bean('myUserService', PostProcessedMockUserDetailsService) | ||||||
|  |         bean('beanPostProcessor', MockUserServiceBeanPostProcessor) | ||||||
|  |         createAppContext("") | ||||||
|  | 
 | ||||||
|  |         expect: | ||||||
|  |         appContext.getBean("myUserService").getPostProcessorWasHere() == "Hello from the post processor!" | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* SEC-934 */ | ||||||
|  |     def supportsTwoIdenticalInterceptUrls() { | ||||||
|  |         httpAutoConfig { | ||||||
|  |             interceptUrl ('/someUrl', 'ROLE_A') | ||||||
|  |             interceptUrl ('/someUrl', 'ROLE_B') | ||||||
|  |         } | ||||||
|  |         createAppContext() | ||||||
|  |         def fis = getFilter(FilterSecurityInterceptor) | ||||||
|  |         def fids = fis.securityMetadataSource | ||||||
|  |         Collection attrs = fids.getAttributes(createFilterinvocation("/someurl", null)); | ||||||
|  | 
 | ||||||
|  |         expect: | ||||||
|  |         attrs.size() == 1 | ||||||
|  |         attrs.contains(new SecurityConfig("ROLE_B")) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def supportsExternallyDefinedSecurityContextRepository() { | ||||||
|  |         xml.http('create-session': 'always', 'security-context-repository-ref': 'repo') { | ||||||
|  |             'http-basic'() | ||||||
|  |         } | ||||||
|  |         bean('repo', HttpSessionSecurityContextRepository) | ||||||
|  |         createAppContext() | ||||||
|  | 
 | ||||||
|  |         def filter = getFilter(SecurityContextPersistenceFilter) | ||||||
|  | 
 | ||||||
|  |         expect: | ||||||
|  |         filter.repo == appContext.getBean('repo') | ||||||
|  |         filter.forceEagerSessionCreation == true | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def expressionBasedAccessAllowsAndDeniesAccessAsExpected() { | ||||||
|  |         setup: | ||||||
|  |         xml.http('auto-config': 'true', 'use-expressions': 'true') { | ||||||
|  |             interceptUrl('/secure*', "hasAnyRole('ROLE_A','ROLE_C')") | ||||||
|  |             interceptUrl('/**', 'permitAll') | ||||||
|  |         } | ||||||
|  |         createAppContext() | ||||||
|  | 
 | ||||||
|  |         def fis = getFilter(FilterSecurityInterceptor) | ||||||
|  |         def fids = fis.getSecurityMetadataSource() | ||||||
|  |         Collection attrs = fids.getAttributes(createFilterinvocation("/secure", null)); | ||||||
|  |         assert 1 == attrs.size() | ||||||
|  | 
 | ||||||
|  |         when: "Unprotected URL" | ||||||
|  |         SecurityContextHolder.getContext().setAuthentication(new TestingAuthenticationToken("joe", "", "ROLE_A")); | ||||||
|  |         fis.invoke(createFilterinvocation("/permitallurl", null)); | ||||||
|  |         then: | ||||||
|  |         notThrown(AccessDeniedException) | ||||||
|  | 
 | ||||||
|  |         when: "Invoking secure Url as a valid user" | ||||||
|  |         fis.invoke(createFilterinvocation("/secure", null)); | ||||||
|  |         then: | ||||||
|  |         notThrown(AccessDeniedException) | ||||||
|  | 
 | ||||||
|  |         when: "User does not have the required role" | ||||||
|  |         SecurityContextHolder.getContext().setAuthentication(new TestingAuthenticationToken("joe", "", "ROLE_B")); | ||||||
|  |         fis.invoke(createFilterinvocation("/secure", null)); | ||||||
|  |         then: | ||||||
|  |         thrown(AccessDeniedException) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def disablingUrlRewritingThroughTheNamespaceSetsCorrectPropertyOnContextRepo() { | ||||||
|  |         xml.http('auto-config': 'true', 'disable-url-rewriting': 'true') | ||||||
|  |         createAppContext() | ||||||
|  | 
 | ||||||
|  |         expect: | ||||||
|  |         getFilter(SecurityContextPersistenceFilter).repo.disableUrlRewriting == true | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def userDetailsServiceInParentContextIsLocatedSuccessfully() { | ||||||
|  |         when: | ||||||
|  |         createAppContext() | ||||||
|  |         httpAutoConfig { | ||||||
|  |             'remember-me' | ||||||
|  |         } | ||||||
|  |         appContext = new InMemoryXmlApplicationContext(writer.toString(), appContext) | ||||||
|  | 
 | ||||||
|  |         then: | ||||||
|  |         notThrown(BeansException) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def httpConfigWithNoAuthProvidersWorksOk() { | ||||||
|  |         when: "Http config has no internal authentication providers" | ||||||
|  |         xml.http() { | ||||||
|  |             'form-login'() | ||||||
|  |             anonymous(enabled: 'false') | ||||||
|  |         } | ||||||
|  |         createAppContext() | ||||||
|  |         FilterChainProxy fcp = appContext.getBean(BeanIds.FILTER_CHAIN_PROXY); | ||||||
|  |         MockHttpServletRequest request = new MockHttpServletRequest("POST", "/j_spring_security_check"); | ||||||
|  |         request.setServletPath("/j_spring_security_check"); | ||||||
|  |         request.addParameter("j_username", "bob"); | ||||||
|  |         request.addParameter("j_password", "bob"); | ||||||
|  |         then: "App context creation and login request succeed" | ||||||
|  |         fcp.doFilter(request, new MockHttpServletResponse(), new MockFilterChain()); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class MockEntryPoint extends LoginUrlAuthenticationEntryPoint { | ||||||
|  |     public MockEntryPoint() { | ||||||
|  |         super.setLoginFormUrl("/notused"); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,154 @@ | |||||||
|  | package org.springframework.security.config.http | ||||||
|  | 
 | ||||||
|  | import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer | ||||||
|  | import org.springframework.mock.web.MockFilterChain | ||||||
|  | import org.springframework.mock.web.MockHttpServletRequest | ||||||
|  | import org.springframework.mock.web.MockHttpServletResponse | ||||||
|  | import org.springframework.security.access.ConfigAttribute | ||||||
|  | import org.springframework.security.access.SecurityConfig | ||||||
|  | import org.springframework.security.util.FieldUtils | ||||||
|  | import org.springframework.security.web.PortMapperImpl | ||||||
|  | import org.springframework.security.web.access.ExceptionTranslationFilter | ||||||
|  | import org.springframework.security.web.access.channel.ChannelProcessingFilter | ||||||
|  | import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource | ||||||
|  | import org.springframework.security.web.access.intercept.FilterSecurityInterceptor | ||||||
|  | import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter | ||||||
|  | 
 | ||||||
|  | class PlaceHolderAndELConfigTests extends AbstractHttpConfigTests { | ||||||
|  | 
 | ||||||
|  |     void setup() { | ||||||
|  |         // Add a PropertyPlaceholderConfigurer to the context for all the tests | ||||||
|  |         xml.'b:bean'('class': PropertyPlaceholderConfigurer.class.name) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def filtersEqualsNoneSupportsPlaceholderForPattern() { | ||||||
|  |         System.setProperty("pattern.nofilters", "/unprotected"); | ||||||
|  | 
 | ||||||
|  |         httpAutoConfig() { | ||||||
|  |             interceptUrlNoFilters('${pattern.nofilters}') | ||||||
|  |             interceptUrl('/**', 'ROLE_A') | ||||||
|  |         } | ||||||
|  |         createAppContext() | ||||||
|  | 
 | ||||||
|  |         List filters = getFilters("/unprotected"); | ||||||
|  | 
 | ||||||
|  |         expect: | ||||||
|  |         filters.size() == 0 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // SEC-1201 | ||||||
|  |     def interceptUrlsAndFormLoginSupportPropertyPlaceholders() { | ||||||
|  |         System.setProperty("secure.Url", "/Secure"); | ||||||
|  |         System.setProperty("secure.role", "ROLE_A"); | ||||||
|  |         System.setProperty("login.page", "/loginPage"); | ||||||
|  |         System.setProperty("default.target", "/defaultTarget"); | ||||||
|  |         System.setProperty("auth.failure", "/authFailure"); | ||||||
|  | 
 | ||||||
|  |         xml.http { | ||||||
|  |             interceptUrl('${secure.Url}', '${secure.role}') | ||||||
|  |             interceptUrlNoFilters('${login.page}'); | ||||||
|  |             'form-login'('login-page':'${login.page}', 'default-target-url': '${default.target}', | ||||||
|  |                 'authentication-failure-url':'${auth.failure}'); | ||||||
|  |         } | ||||||
|  |         createAppContext(); | ||||||
|  | 
 | ||||||
|  |         expect: | ||||||
|  |         propertyValuesMatchPlaceholders() | ||||||
|  |         getFilters("/loginPage").size() == 0 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // SEC-1309 | ||||||
|  |     def interceptUrlsAndFormLoginSupportEL() { | ||||||
|  |         System.setProperty("secure.url", "/Secure"); | ||||||
|  |         System.setProperty("secure.role", "ROLE_A"); | ||||||
|  |         System.setProperty("login.page", "/loginPage"); | ||||||
|  |         System.setProperty("default.target", "/defaultTarget"); | ||||||
|  |         System.setProperty("auth.failure", "/authFailure"); | ||||||
|  | 
 | ||||||
|  |         xml.http { | ||||||
|  |             interceptUrl("#{systemProperties['secure.url']}", "#{systemProperties['secure.role']}") | ||||||
|  |             'form-login'('login-page':"#{systemProperties['login.page']}", 'default-target-url': "#{systemProperties['default.target']}", | ||||||
|  |                 'authentication-failure-url':"#{systemProperties['auth.failure']}"); | ||||||
|  |         } | ||||||
|  |         createAppContext() | ||||||
|  | 
 | ||||||
|  |         expect: | ||||||
|  |         propertyValuesMatchPlaceholders() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private void propertyValuesMatchPlaceholders() { | ||||||
|  |         // Check the security attribute | ||||||
|  |         def fis = getFilter(FilterSecurityInterceptor); | ||||||
|  |         def fids = fis.getSecurityMetadataSource(); | ||||||
|  |         Collection attrs = fids.getAttributes(createFilterinvocation("/secure", null)); | ||||||
|  |         assert attrs.size() == 1 | ||||||
|  |         assert attrs.contains(new SecurityConfig("ROLE_A")) | ||||||
|  | 
 | ||||||
|  |         // Check the form login properties are set | ||||||
|  |         def apf = getFilter(UsernamePasswordAuthenticationFilter) | ||||||
|  |         assert FieldUtils.getFieldValue(apf, "successHandler.defaultTargetUrl") == '/defaultTarget' | ||||||
|  |         assert "/authFailure" == FieldUtils.getFieldValue(apf, "failureHandler.defaultFailureUrl") | ||||||
|  | 
 | ||||||
|  |         def etf = getFilter(ExceptionTranslationFilter) | ||||||
|  |         assert "/loginPage"== etf.authenticationEntryPoint.loginFormUrl | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def portMappingsWorkWithPlaceholdersAndEL() { | ||||||
|  |         System.setProperty("http", "9080"); | ||||||
|  |         System.setProperty("https", "9443"); | ||||||
|  | 
 | ||||||
|  |         httpAutoConfig { | ||||||
|  |             'port-mappings'() { | ||||||
|  |                 'port-mapping'(http: '#{systemProperties.http}', https: '${https}') | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         createAppContext(); | ||||||
|  | 
 | ||||||
|  |         def pm = (appContext.getBeansOfType(PortMapperImpl).values() as List)[0]; | ||||||
|  | 
 | ||||||
|  |         expect: | ||||||
|  |         pm.getTranslatedPortMappings().size() == 1 | ||||||
|  |         pm.lookupHttpPort(9443) == 9080 | ||||||
|  |         pm.lookupHttpsPort(9080) == 9443 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def requiresChannelSupportsPlaceholder() { | ||||||
|  |         System.setProperty("secure.url", "/secure"); | ||||||
|  |         System.setProperty("required.channel", "https"); | ||||||
|  | 
 | ||||||
|  |         httpAutoConfig { | ||||||
|  |             'intercept-url'(pattern: '${secure.url}', 'requires-channel': '${required.channel}') | ||||||
|  |         } | ||||||
|  |         createAppContext(); | ||||||
|  |         List filters = getFilters("/secure"); | ||||||
|  | 
 | ||||||
|  |         expect: | ||||||
|  |         filters.size() == AUTO_CONFIG_FILTERS + 1 | ||||||
|  |         filters[0] instanceof ChannelProcessingFilter | ||||||
|  |         MockHttpServletRequest request = new MockHttpServletRequest(); | ||||||
|  |         request.setServletPath("/secure"); | ||||||
|  |         MockHttpServletResponse response = new MockHttpServletResponse(); | ||||||
|  |         filters[0].doFilter(request, response, new MockFilterChain()); | ||||||
|  |         response.getRedirectedUrl().startsWith("https") | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def accessDeniedPageWorksWithPlaceholders() { | ||||||
|  |         System.setProperty("accessDenied", "/go-away"); | ||||||
|  |         xml.http('auto-config': 'true', 'access-denied-page': '${accessDenied}') | ||||||
|  |         createAppContext(); | ||||||
|  | 
 | ||||||
|  |         expect: | ||||||
|  |         FieldUtils.getFieldValue(getFilter(ExceptionTranslationFilter.class), "accessDeniedHandler.errorPage") == '/go-away' | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def accessDeniedHandlerPageWorksWithEL() { | ||||||
|  |         httpAutoConfig { | ||||||
|  |             'access-denied-handler'('error-page': "#{'/go' + '-away'}") | ||||||
|  |         } | ||||||
|  |         createAppContext() | ||||||
|  | 
 | ||||||
|  |         expect: | ||||||
|  |         getFilter(ExceptionTranslationFilter).accessDeniedHandler.errorPage == '/go-away' | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @ -0,0 +1,145 @@ | |||||||
|  | package org.springframework.security.config.http | ||||||
|  | 
 | ||||||
|  | import static org.springframework.security.config.ConfigTestUtils.AUTH_PROVIDER_XML; | ||||||
|  | 
 | ||||||
|  | import org.springframework.beans.factory.parsing.BeanDefinitionParsingException | ||||||
|  | import org.springframework.security.TestDataSource; | ||||||
|  | import org.springframework.security.authentication.ProviderManager | ||||||
|  | import org.springframework.security.authentication.RememberMeAuthenticationProvider | ||||||
|  | import org.springframework.security.config.BeanIds | ||||||
|  | import org.springframework.security.core.userdetails.MockUserDetailsService; | ||||||
|  | import org.springframework.security.util.FieldUtils | ||||||
|  | import org.springframework.security.web.authentication.logout.LogoutFilter | ||||||
|  | import org.springframework.security.web.authentication.logout.LogoutHandler | ||||||
|  | import org.springframework.security.web.authentication.rememberme.InMemoryTokenRepositoryImpl | ||||||
|  | import org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices | ||||||
|  | import org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter | ||||||
|  | import org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * | ||||||
|  |  * @author Luke Taylor | ||||||
|  |  */ | ||||||
|  | class RememberMeConfigTests extends AbstractHttpConfigTests { | ||||||
|  | 
 | ||||||
|  |     def rememberMeServiceWorksWithTokenRepoRef() { | ||||||
|  |         httpAutoConfig () { | ||||||
|  |             'remember-me'('token-repository-ref': 'tokenRepo') | ||||||
|  |         } | ||||||
|  |         bean('tokenRepo', InMemoryTokenRepositoryImpl.class.name) | ||||||
|  | 
 | ||||||
|  |         createAppContext(AUTH_PROVIDER_XML) | ||||||
|  | 
 | ||||||
|  |         expect: | ||||||
|  |         rememberMeServices() instanceof PersistentTokenBasedRememberMeServices | ||||||
|  |         FieldUtils.getFieldValue(rememberMeServices(), "useSecureCookie") == false | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def rememberMeServiceWorksWithDataSourceRef() { | ||||||
|  |         httpAutoConfig () { | ||||||
|  |             'remember-me'('data-source-ref': 'ds') | ||||||
|  |         } | ||||||
|  |         bean('ds', TestDataSource.class.name, ['tokendb']) | ||||||
|  | 
 | ||||||
|  |         createAppContext(AUTH_PROVIDER_XML) | ||||||
|  | 
 | ||||||
|  |         expect: | ||||||
|  |         rememberMeServices() instanceof PersistentTokenBasedRememberMeServices | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def rememberMeServiceWorksWithExternalServicesImpl() { | ||||||
|  |         httpAutoConfig () { | ||||||
|  |             'remember-me'('key': "#{'our' + 'key'}", 'services-ref': 'rms') | ||||||
|  |         } | ||||||
|  |         bean('rms', TokenBasedRememberMeServices.class.name, | ||||||
|  |                 ['key':'ourKey', 'tokenValiditySeconds':'5000'], ['userDetailsService':'us']) | ||||||
|  | 
 | ||||||
|  |         createAppContext(AUTH_PROVIDER_XML) | ||||||
|  | 
 | ||||||
|  |         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]; | ||||||
|  | 
 | ||||||
|  |         expect: | ||||||
|  |         5000 == FieldUtils.getFieldValue(rememberMeServices(), "tokenValiditySeconds") | ||||||
|  |         // SEC-909 | ||||||
|  |         logoutHandlers.size() == 2 | ||||||
|  |         logoutHandlers.get(1) == rememberMeServices() | ||||||
|  |         // SEC-1281 | ||||||
|  |         rmp.key == "ourkey" | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def rememberMeTokenValidityIsParsedCorrectly() { | ||||||
|  |         httpAutoConfig () { | ||||||
|  |             'remember-me'('key': 'ourkey', 'token-validity-seconds':'10000') | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         createAppContext(AUTH_PROVIDER_XML) | ||||||
|  |         expect: | ||||||
|  |         rememberMeServices().tokenValiditySeconds == 10000 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def 'Remember-me token validity allows negative value for non-persistent implementation'() { | ||||||
|  |         httpAutoConfig () { | ||||||
|  |             'remember-me'('key': 'ourkey', 'token-validity-seconds':'-1') | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         createAppContext(AUTH_PROVIDER_XML) | ||||||
|  |         expect: | ||||||
|  |         rememberMeServices().tokenValiditySeconds == -1 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def rememberMeSecureCookieAttributeIsSetCorrectly() { | ||||||
|  |         httpAutoConfig () { | ||||||
|  |             'remember-me'('key': 'ourkey', 'use-secure-cookie':'true') | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         createAppContext(AUTH_PROVIDER_XML) | ||||||
|  |         expect: | ||||||
|  |         FieldUtils.getFieldValue(rememberMeServices(), "useSecureCookie") == true | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def 'Negative token-validity is rejected with persistent implementation'() { | ||||||
|  |         when: | ||||||
|  |         httpAutoConfig () { | ||||||
|  |             'remember-me'('key': 'ourkey', 'token-validity-seconds':'-1', 'token-repository-ref': 'tokenRepo') | ||||||
|  |         } | ||||||
|  |         bean('tokenRepo', InMemoryTokenRepositoryImpl.class.name) | ||||||
|  |         createAppContext(AUTH_PROVIDER_XML) | ||||||
|  | 
 | ||||||
|  |         then: | ||||||
|  |         BeanDefinitionParsingException e = thrown() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def 'Custom user service is supported'() { | ||||||
|  |         when: | ||||||
|  |         httpAutoConfig () { | ||||||
|  |             'remember-me'('key': 'ourkey', 'token-validity-seconds':'-1', 'user-service-ref': 'userService') | ||||||
|  |         } | ||||||
|  |         bean('userService', MockUserDetailsService.class.name) | ||||||
|  |         createAppContext(AUTH_PROVIDER_XML) | ||||||
|  | 
 | ||||||
|  |         then: "Parses OK" | ||||||
|  |         notThrown BeanDefinitionParsingException | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // SEC-742 | ||||||
|  |     def rememberMeWorksWithoutBasicProcessingFilter() { | ||||||
|  |         when: | ||||||
|  |         xml.http () { | ||||||
|  |             'form-login'('login-page': '/login.jsp', 'default-target-url': '/messageList.html' ) | ||||||
|  |             logout('logout-success-url': '/login.jsp') | ||||||
|  |             anonymous(username: 'guest', 'granted-authority': 'guest') | ||||||
|  |             'remember-me'() | ||||||
|  |         } | ||||||
|  |         createAppContext(AUTH_PROVIDER_XML) | ||||||
|  | 
 | ||||||
|  |         then: "Parses OK" | ||||||
|  |         notThrown BeanDefinitionParsingException | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def rememberMeServices() { | ||||||
|  |         getFilter(RememberMeAuthenticationFilter.class).getRememberMeServices() | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,175 @@ | |||||||
|  | package org.springframework.security.config.http | ||||||
|  | 
 | ||||||
|  | import static org.junit.Assert.*; | ||||||
|  | 
 | ||||||
|  | import groovy.lang.Closure; | ||||||
|  | 
 | ||||||
|  | import javax.servlet.Filter; | ||||||
|  | import org.springframework.security.web.FilterChainProxy | ||||||
|  | import org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy; | ||||||
|  | 
 | ||||||
|  | import org.springframework.mock.web.MockFilterChain | ||||||
|  | import org.springframework.mock.web.MockHttpServletRequest | ||||||
|  | import org.springframework.mock.web.MockHttpServletResponse | ||||||
|  | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken | ||||||
|  | import org.springframework.security.config.BeanIds | ||||||
|  | import org.springframework.security.core.context.SecurityContext | ||||||
|  | import org.springframework.security.core.context.SecurityContextHolder | ||||||
|  | import org.springframework.security.core.session.SessionRegistryImpl | ||||||
|  | import org.springframework.security.util.FieldUtils | ||||||
|  | import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter | ||||||
|  | import org.springframework.security.web.context.NullSecurityContextRepository | ||||||
|  | import org.springframework.security.web.context.SaveContextOnUpdateOrErrorResponseWrapper | ||||||
|  | import org.springframework.security.web.context.SecurityContextPersistenceFilter | ||||||
|  | import org.springframework.security.web.savedrequest.RequestCacheAwareFilter | ||||||
|  | import org.springframework.security.web.session.ConcurrentSessionFilter | ||||||
|  | import org.springframework.security.web.session.SessionManagementFilter | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Tests session-related functionality for the <http> namespace element and <session-management> | ||||||
|  |  * | ||||||
|  |  * @author Luke Taylor | ||||||
|  |  */ | ||||||
|  | class SessionManagementConfigTests extends AbstractHttpConfigTests { | ||||||
|  | 
 | ||||||
|  |     def settingCreateSessionToAlwaysSetsFilterPropertiesCorrectly() { | ||||||
|  |         httpCreateSession('always') { } | ||||||
|  |         createAppContext(); | ||||||
|  | 
 | ||||||
|  |         def filter = getFilter(SecurityContextPersistenceFilter.class); | ||||||
|  | 
 | ||||||
|  |         expect: | ||||||
|  |         filter.forceEagerSessionCreation == true | ||||||
|  |         filter.repo.allowSessionCreation == true | ||||||
|  |         filter.repo.disableUrlRewriting == false | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def settingCreateSessionToNeverSetsFilterPropertiesCorrectly() { | ||||||
|  |         httpCreateSession('never') { } | ||||||
|  |         createAppContext(); | ||||||
|  | 
 | ||||||
|  |         def filter = getFilter(SecurityContextPersistenceFilter.class); | ||||||
|  | 
 | ||||||
|  |         expect: | ||||||
|  |         filter.forceEagerSessionCreation == false | ||||||
|  |         filter.repo.allowSessionCreation == false | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def settingCreateSessionToStatelessSetsFilterPropertiesCorrectly() { | ||||||
|  |         httpCreateSession('stateless') { } | ||||||
|  |         createAppContext(); | ||||||
|  | 
 | ||||||
|  |         def filter = getFilter(SecurityContextPersistenceFilter.class); | ||||||
|  | 
 | ||||||
|  |         expect: | ||||||
|  |         filter.forceEagerSessionCreation == false | ||||||
|  |         filter.repo instanceof NullSecurityContextRepository | ||||||
|  |         getFilter(SessionManagementFilter.class) == null | ||||||
|  |         getFilter(RequestCacheAwareFilter.class) == null | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def settingCreateSessionToIfRequiredDoesntCreateASessionForPublicInvocation() { | ||||||
|  |         httpCreateSession('ifRequired') { } | ||||||
|  |         createAppContext(); | ||||||
|  | 
 | ||||||
|  |         def filter = getFilter(SecurityContextPersistenceFilter.class); | ||||||
|  | 
 | ||||||
|  |         expect: | ||||||
|  |         filter.forceEagerSessionCreation == false | ||||||
|  |         filter.repo.allowSessionCreation == true | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def httpCreateSession(String create, Closure c) { | ||||||
|  |         xml.http(['auto-config': 'true', 'create-session': create], c) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def concurrentSessionSupportAddsFilterAndExpectedBeans() { | ||||||
|  |         httpAutoConfig { | ||||||
|  |             'session-management'() { | ||||||
|  |                 'concurrency-control'('session-registry-alias':'sr', 'expired-url': '/expired') | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         createAppContext(); | ||||||
|  |         List filters = getFilters("/someurl"); | ||||||
|  | 
 | ||||||
|  |         expect: | ||||||
|  |         filters.get(0) instanceof ConcurrentSessionFilter | ||||||
|  |         appContext.getBean("sr") != null | ||||||
|  |         getFilter(SessionManagementFilter.class) != null | ||||||
|  |         sessionRegistryIsValid(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def externalSessionStrategyIsSupported() { | ||||||
|  |         when: | ||||||
|  |         httpAutoConfig { | ||||||
|  |             'session-management'('session-authentication-strategy-ref':'ss') | ||||||
|  |         } | ||||||
|  |         bean('ss', SessionFixationProtectionStrategy.class.name) | ||||||
|  |         createAppContext(); | ||||||
|  | 
 | ||||||
|  |         then: | ||||||
|  |         notThrown(Exception.class) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def externalSessionRegistryBeanIsConfiguredCorrectly() { | ||||||
|  |         httpAutoConfig { | ||||||
|  |             'session-management'() { | ||||||
|  |                 'concurrency-control'('session-registry-ref':'sr') | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         bean('sr', SessionRegistryImpl.class.name) | ||||||
|  |         createAppContext(); | ||||||
|  | 
 | ||||||
|  |         expect: | ||||||
|  |         sessionRegistryIsValid(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def sessionRegistryIsValid() { | ||||||
|  |         Object sessionRegistry = appContext.getBean("sr"); | ||||||
|  |         Object sessionRegistryFromConcurrencyFilter = FieldUtils.getFieldValue( | ||||||
|  |                 getFilter(ConcurrentSessionFilter.class), "sessionRegistry"); | ||||||
|  |         Object sessionRegistryFromFormLoginFilter = FieldUtils.getFieldValue( | ||||||
|  |                 getFilter(UsernamePasswordAuthenticationFilter.class),"sessionStrategy.sessionRegistry"); | ||||||
|  |         Object sessionRegistryFromMgmtFilter = FieldUtils.getFieldValue( | ||||||
|  |                 getFilter(SessionManagementFilter.class),"sessionStrategy.sessionRegistry"); | ||||||
|  | 
 | ||||||
|  |         assertSame(sessionRegistry, sessionRegistryFromConcurrencyFilter); | ||||||
|  |         assertSame(sessionRegistry, sessionRegistryFromMgmtFilter); | ||||||
|  |         // SEC-1143 | ||||||
|  |         assertSame(sessionRegistry, sessionRegistryFromFormLoginFilter); | ||||||
|  |         true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def concurrentSessionMaxSessionsIsCorrectlyConfigured() { | ||||||
|  |         setup: | ||||||
|  |         httpAutoConfig { | ||||||
|  |             'session-management'('session-authentication-error-url':'/max-exceeded') { | ||||||
|  |                 'concurrency-control'('max-sessions': '2', 'error-if-maximum-exceeded':'true') | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         createAppContext(); | ||||||
|  | 
 | ||||||
|  |         def seshFilter = getFilter(SessionManagementFilter.class); | ||||||
|  |         def auth = new UsernamePasswordAuthenticationToken("bob", "pass"); | ||||||
|  |         SecurityContextHolder.getContext().setAuthentication(auth); | ||||||
|  |         MockHttpServletResponse mockResponse = new MockHttpServletResponse(); | ||||||
|  |         def response = new SaveContextOnUpdateOrErrorResponseWrapper(mockResponse, false) { | ||||||
|  |             protected void saveContext(SecurityContext context) { | ||||||
|  |             } | ||||||
|  |         }; | ||||||
|  |         when: "First session is established" | ||||||
|  |         seshFilter.doFilter(new MockHttpServletRequest(), response, new MockFilterChain()); | ||||||
|  |         then: "ok" | ||||||
|  |         mockResponse.redirectedUrl == null | ||||||
|  |         when: "Second session is established" | ||||||
|  |         seshFilter.doFilter(new MockHttpServletRequest(), response, new MockFilterChain()); | ||||||
|  |         then: "ok" | ||||||
|  |         mockResponse.redirectedUrl == null | ||||||
|  |         when: "Third session is established" | ||||||
|  |         seshFilter.doFilter(new MockHttpServletRequest(), response, new MockFilterChain()); | ||||||
|  |         then: "Rejected" | ||||||
|  |         mockResponse.redirectedUrl == "/max-exceeded"; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @ -128,11 +128,6 @@ public class HttpSecurityBeanDefinitionParserTests { | |||||||
|         checkAutoConfigFilters(filterList); |         checkAutoConfigFilters(filterList); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @Test(expected=BeanDefinitionParsingException.class) |  | ||||||
|     public void duplicateElementCausesError() throws Exception { |  | ||||||
|         setContext("<http auto-config='true' /><http auto-config='true' />" + AUTH_PROVIDER_XML); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private void checkAutoConfigFilters(List<Filter> filterList) throws Exception { |     private void checkAutoConfigFilters(List<Filter> filterList) throws Exception { | ||||||
|         Iterator<Filter> filters = filterList.iterator(); |         Iterator<Filter> filters = filterList.iterator(); | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user