mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-07-02 00:32:15 +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