SEC-2199: Support multiple AuthenticationEntryPoint defaults

This commit is contained in:
Rob Winch
2013-07-19 16:27:06 -05:00
parent 87c9a14bff
commit 90bd241ce2
9 changed files with 296 additions and 25 deletions

View File

@@ -40,6 +40,8 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur
import org.springframework.security.core.Authentication
import org.springframework.security.core.authority.AuthorityUtils
import org.springframework.security.ldap.DefaultSpringSecurityContextSource
import org.springframework.web.accept.ContentNegotiationStrategy;
import org.springframework.web.accept.HeaderContentNegotiationStrategy;
/**
* @author Rob Winch
@@ -99,4 +101,35 @@ class WebSecurityConfigurerAdapterTests extends BaseSpringSpec {
EVENTS.add(e)
}
}
def "Override ContentNegotiationStrategy with @Bean"() {
setup:
OverrideContentNegotiationStrategySharedObjectConfig.CNS = Mock(ContentNegotiationStrategy)
when:
loadConfig(OverrideContentNegotiationStrategySharedObjectConfig)
then:
context.getBean(OverrideContentNegotiationStrategySharedObjectConfig).http.getSharedObject(ContentNegotiationStrategy) == OverrideContentNegotiationStrategySharedObjectConfig.CNS
}
@EnableWebSecurity
@Configuration
static class OverrideContentNegotiationStrategySharedObjectConfig extends WebSecurityConfigurerAdapter {
static ContentNegotiationStrategy CNS
@Bean
public ContentNegotiationStrategy cns() {
return CNS
}
}
def "ContentNegotiationStrategy shareObject defaults to Header with no @Bean"() {
when:
loadConfig(ContentNegotiationStrategyDefaultSharedObjectConfig)
then:
context.getBean(ContentNegotiationStrategyDefaultSharedObjectConfig).http.getSharedObject(ContentNegotiationStrategy).class == HeaderContentNegotiationStrategy
}
@EnableWebSecurity
@Configuration
static class ContentNegotiationStrategyDefaultSharedObjectConfig extends WebSecurityConfigurerAdapter {}
}

View File

@@ -29,6 +29,7 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.BaseWebConfig;
import org.springframework.security.config.annotation.web.configurers.DefaultLoginPageConfigurer;
import org.springframework.security.web.FilterChainProxy
import org.springframework.security.web.access.ExceptionTranslationFilter;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler
@@ -331,6 +332,8 @@ public class DefaultLoginPageConfigurerTests extends BaseSpringSpec {
http
// must set builder manually due to groovy not selecting correct method
.apply(defaultLoginConfig).and()
.exceptionHandling()
.and()
.formLogin()
.and()
.build()
@@ -339,5 +342,6 @@ public class DefaultLoginPageConfigurerTests extends BaseSpringSpec {
1 * objectPostProcessor.postProcess(_ as DefaultLoginPageViewFilter) >> {DefaultLoginPageViewFilter o -> o}
1 * objectPostProcessor.postProcess(_ as UsernamePasswordAuthenticationFilter) >> {UsernamePasswordAuthenticationFilter o -> o}
1 * objectPostProcessor.postProcess(_ as LoginUrlAuthenticationEntryPoint) >> {LoginUrlAuthenticationEntryPoint o -> o}
1 * objectPostProcessor.postProcess(_ as ExceptionTranslationFilter) >> {ExceptionTranslationFilter o -> o}
}
}

View File

@@ -15,11 +15,29 @@
*/
package org.springframework.security.config.annotation.web.configurers
import javax.servlet.http.HttpServletResponse
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration
import org.springframework.http.MediaType
import org.springframework.mock.web.MockFilterChain
import org.springframework.mock.web.MockHttpServletRequest
import org.springframework.mock.web.MockHttpServletResponse
import org.springframework.security.config.annotation.AnyObjectPostProcessor
import org.springframework.security.config.annotation.BaseSpringSpec
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.ExceptionTranslationFilter
import org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint;
import org.springframework.web.accept.ContentNegotiationStrategy;
import org.springframework.web.accept.HeaderContentNegotiationStrategy;
import spock.lang.Unroll
/**
*
@@ -40,4 +58,124 @@ class ExceptionHandlingConfigurerTests extends BaseSpringSpec {
then: "ExceptionTranslationFilter is registered with LifecycleManager"
1 * opp.postProcess(_ as ExceptionTranslationFilter) >> {ExceptionTranslationFilter o -> o}
}
@Unroll
def "SEC-2199: defaultEntryPoint for httpBasic and formLogin"(String acceptHeader, int httpStatus) {
setup:
loadConfig(HttpBasicAndFormLoginEntryPointsConfig)
when:
request.addHeader("Accept", acceptHeader)
springSecurityFilterChain.doFilter(request,response,chain)
then:
response.status == httpStatus
where:
acceptHeader | httpStatus
MediaType.ALL_VALUE | HttpServletResponse.SC_MOVED_TEMPORARILY
MediaType.APPLICATION_XHTML_XML_VALUE | HttpServletResponse.SC_MOVED_TEMPORARILY
MediaType.IMAGE_GIF_VALUE | HttpServletResponse.SC_MOVED_TEMPORARILY
MediaType.IMAGE_JPEG_VALUE | HttpServletResponse.SC_MOVED_TEMPORARILY
MediaType.IMAGE_PNG_VALUE | HttpServletResponse.SC_MOVED_TEMPORARILY
MediaType.TEXT_HTML_VALUE | HttpServletResponse.SC_MOVED_TEMPORARILY
MediaType.TEXT_PLAIN_VALUE | HttpServletResponse.SC_MOVED_TEMPORARILY
MediaType.APPLICATION_ATOM_XML_VALUE | HttpServletResponse.SC_UNAUTHORIZED
MediaType.APPLICATION_FORM_URLENCODED_VALUE | HttpServletResponse.SC_UNAUTHORIZED
MediaType.APPLICATION_JSON_VALUE | HttpServletResponse.SC_UNAUTHORIZED
MediaType.APPLICATION_OCTET_STREAM_VALUE | HttpServletResponse.SC_UNAUTHORIZED
MediaType.APPLICATION_XML_VALUE | HttpServletResponse.SC_UNAUTHORIZED
MediaType.MULTIPART_FORM_DATA_VALUE | HttpServletResponse.SC_UNAUTHORIZED
MediaType.TEXT_XML_VALUE | HttpServletResponse.SC_UNAUTHORIZED
}
def "ContentNegotiationStrategy defaults to HeaderContentNegotiationStrategy"() {
when:
loadConfig(HttpBasicAndFormLoginEntryPointsConfig)
DelegatingAuthenticationEntryPoint delegateEntryPoint = findFilter(ExceptionTranslationFilter).authenticationEntryPoint
then:
delegateEntryPoint.entryPoints.keySet().collect {it.contentNegotiationStrategy.class} == [HeaderContentNegotiationStrategy,HeaderContentNegotiationStrategy]
}
@EnableWebSecurity
@Configuration
static class HttpBasicAndFormLoginEntryPointsConfig extends WebSecurityConfigurerAdapter {
@Override
protected void registerAuthentication(AuthenticationManagerBuilder auth)
throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER")
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeUrls()
.anyRequest().authenticated()
.and()
.httpBasic()
.and()
.formLogin()
}
}
def "ContentNegotiationStrategy overrides with @Bean"() {
setup:
OverrideContentNegotiationStrategySharedObjectConfig.CNS = Mock(ContentNegotiationStrategy)
when:
loadConfig(OverrideContentNegotiationStrategySharedObjectConfig)
DelegatingAuthenticationEntryPoint delegateEntryPoint = findFilter(ExceptionTranslationFilter).authenticationEntryPoint
then:
delegateEntryPoint.entryPoints.keySet().collect {it.contentNegotiationStrategy} == [OverrideContentNegotiationStrategySharedObjectConfig.CNS,OverrideContentNegotiationStrategySharedObjectConfig.CNS]
}
def "Override ContentNegotiationStrategy with @Bean"() {
setup:
OverrideContentNegotiationStrategySharedObjectConfig.CNS = Mock(ContentNegotiationStrategy)
when:
loadConfig(OverrideContentNegotiationStrategySharedObjectConfig)
then:
context.getBean(OverrideContentNegotiationStrategySharedObjectConfig).http.getSharedObject(ContentNegotiationStrategy) == OverrideContentNegotiationStrategySharedObjectConfig.CNS
}
@EnableWebSecurity
@Configuration
static class OverrideContentNegotiationStrategySharedObjectConfig extends WebSecurityConfigurerAdapter {
static ContentNegotiationStrategy CNS
@Bean
public ContentNegotiationStrategy cns() {
return CNS
}
}
def "delegatingAuthenticationEntryPoint.defaultEntryPoint is LoginUrlAuthenticationEntryPoint when using DefaultHttpConf"() {
when:
loadConfig(DefaultHttpConf)
then:
findFilter(ExceptionTranslationFilter).authenticationEntryPoint.defaultEntryPoint.class == LoginUrlAuthenticationEntryPoint
}
@EnableWebSecurity
@Configuration
static class DefaultHttpConf extends WebSecurityConfigurerAdapter {
}
def "delegatingAuthenticationEntryPoint.defaultEntryPoint is BasicAuthenticationEntryPoint when httpBasic before formLogin"() {
when:
loadConfig(BasicAuthenticationEntryPointBeforeFormLoginConf)
then:
findFilter(ExceptionTranslationFilter).authenticationEntryPoint.defaultEntryPoint.class == BasicAuthenticationEntryPoint
}
@EnableWebSecurity
@Configuration
static class BasicAuthenticationEntryPointBeforeFormLoginConf extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic()
.and()
.formLogin()
}
}
}

View File

@@ -202,13 +202,17 @@ class FormLoginConfigurerTests extends BaseSpringSpec {
HttpSecurity http = new HttpSecurity(opp, authenticationBldr, [:])
when:
http
.exceptionHandling()
.and()
.formLogin()
.and()
.build()
then: "UsernamePasswordAuthenticationFilter is registered with LifecycleManager"
then: "UsernamePasswordAuthenticationFilter is registered with ObjectPostProcessor"
1 * opp.postProcess(_ as UsernamePasswordAuthenticationFilter) >> {UsernamePasswordAuthenticationFilter o -> o}
and: "LoginUrlAuthenticationEntryPoint is registered with LifecycleManager"
and: "LoginUrlAuthenticationEntryPoint is registered with ObjectPostProcessor"
1 * opp.postProcess(_ as LoginUrlAuthenticationEntryPoint) >> {LoginUrlAuthenticationEntryPoint o -> o}
and: "ExceptionTranslationFilter is registered with ObjectPostProcessor"
1 * opp.postProcess(_ as ExceptionTranslationFilter) >> {ExceptionTranslationFilter o -> o}
}
}