SEC-2199: Support multiple AuthenticationEntryPoint defaults
This commit is contained in:
parent
87c9a14bff
commit
90bd241ce2
|
@ -37,6 +37,8 @@ import org.springframework.security.core.Authentication;
|
|||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
|
||||
import org.springframework.web.accept.ContentNegotiationStrategy;
|
||||
import org.springframework.web.accept.HeaderContentNegotiationStrategy;
|
||||
|
||||
/**
|
||||
* Provides a convenient base class for creating a {@link WebSecurityConfigurer}
|
||||
|
@ -51,6 +53,8 @@ public abstract class WebSecurityConfigurerAdapter implements SecurityConfigurer
|
|||
|
||||
private ApplicationContext context;
|
||||
|
||||
private ContentNegotiationStrategy contentNegotiationStrategy = new HeaderContentNegotiationStrategy();
|
||||
|
||||
private ObjectPostProcessor<Object> objectPostProcessor = new ObjectPostProcessor<Object>() {
|
||||
@Override
|
||||
public <T> T postProcess(T object) {
|
||||
|
@ -145,6 +149,7 @@ public abstract class WebSecurityConfigurerAdapter implements SecurityConfigurer
|
|||
authenticationBuilder.parentAuthenticationManager(authenticationManager);
|
||||
http = new HttpSecurity(objectPostProcessor,authenticationBuilder, parentAuthenticationBuilder.getSharedObjects());
|
||||
http.setSharedObject(UserDetailsService.class, userDetailsService());
|
||||
http.setSharedObject(ContentNegotiationStrategy.class, contentNegotiationStrategy);
|
||||
if(!disableDefaults) {
|
||||
http
|
||||
.exceptionHandling().and()
|
||||
|
@ -304,6 +309,11 @@ public abstract class WebSecurityConfigurerAdapter implements SecurityConfigurer
|
|||
this.context = context;
|
||||
}
|
||||
|
||||
@Autowired(required=false)
|
||||
public void setContentNegotationStrategy(ContentNegotiationStrategy contentNegotiationStrategy) {
|
||||
this.contentNegotiationStrategy = contentNegotiationStrategy;
|
||||
}
|
||||
|
||||
@Autowired(required=false)
|
||||
public void setObjectPostProcessor(ObjectPostProcessor<Object> objectPostProcessor) {
|
||||
this.objectPostProcessor = objectPostProcessor;
|
||||
|
|
|
@ -17,11 +17,11 @@ package org.springframework.security.config.annotation.web.configurers;
|
|||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.security.authentication.AuthenticationDetailsSource;
|
||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.annotation.web.configurers.openid.OpenIDLoginConfigurer;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.security.web.PortMapper;
|
||||
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
|
||||
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
|
||||
|
@ -33,6 +33,10 @@ import org.springframework.security.web.authentication.SimpleUrlAuthenticationFa
|
|||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
|
||||
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
|
||||
import org.springframework.security.web.util.MediaTypeRequestMatcher;
|
||||
import org.springframework.security.web.util.RequestMatcher;
|
||||
import org.springframework.web.accept.ContentNegotiationStrategy;
|
||||
import org.springframework.web.accept.HeaderContentNegotiationStrategy;
|
||||
|
||||
/**
|
||||
* Base class for confuring {@link AbstractAuthenticationFilterConfigurer}. This is intended for internal use only.
|
||||
|
@ -221,7 +225,21 @@ public abstract class AbstractAuthenticationFilterConfigurer<B extends HttpSecu
|
|||
if(permitAll) {
|
||||
PermitAllSupport.permitAll(http, loginPage, loginProcessingUrl, failureUrl);
|
||||
}
|
||||
http.setSharedObject(AuthenticationEntryPoint.class, postProcess(authenticationEntryPoint));
|
||||
registerDefaultAuthenticationEntryPoint(http);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void registerDefaultAuthenticationEntryPoint(B http) {
|
||||
ExceptionHandlingConfigurer<B> exceptionHandling = http.getConfigurer(ExceptionHandlingConfigurer.class);
|
||||
if(exceptionHandling == null) {
|
||||
return;
|
||||
}
|
||||
ContentNegotiationStrategy contentNegotiationStrategy = http.getSharedObject(ContentNegotiationStrategy.class);
|
||||
if(contentNegotiationStrategy == null) {
|
||||
contentNegotiationStrategy = new HeaderContentNegotiationStrategy();
|
||||
}
|
||||
RequestMatcher preferredMatcher = new MediaTypeRequestMatcher(contentNegotiationStrategy, MediaType.APPLICATION_XHTML_XML, new MediaType("image","*"), MediaType.TEXT_HTML, MediaType.TEXT_PLAIN);
|
||||
exceptionHandling.defaultAuthenticationEntryPointFor(postProcess(authenticationEntryPoint), preferredMatcher);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -15,15 +15,19 @@
|
|||
*/
|
||||
package org.springframework.security.config.annotation.web.configurers;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.security.web.access.AccessDeniedHandler;
|
||||
import org.springframework.security.web.access.AccessDeniedHandlerImpl;
|
||||
import org.springframework.security.web.access.ExceptionTranslationFilter;
|
||||
import org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint;
|
||||
import org.springframework.security.web.authentication.Http403ForbiddenEntryPoint;
|
||||
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
|
||||
import org.springframework.security.web.savedrequest.RequestCache;
|
||||
import org.springframework.security.web.util.RequestMatcher;
|
||||
|
||||
/**
|
||||
* Adds exception handling for Spring Security related exceptions to an application. All properties have reasonable
|
||||
|
@ -47,8 +51,6 @@ import org.springframework.security.web.savedrequest.RequestCache;
|
|||
* The following shared objects are used:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link HttpSecurity#authenticationEntryPoint()} is used to process requests that require
|
||||
* authentication</li>
|
||||
* <li>If no explicit {@link RequestCache}, is provided a {@link RequestCache} shared object is used to replay
|
||||
* the request after authentication is successful</li>
|
||||
* <li>{@link AuthenticationEntryPoint} - see {@link #authenticationEntryPoint(AuthenticationEntryPoint)} </li>
|
||||
|
@ -63,6 +65,8 @@ public final class ExceptionHandlingConfigurer<H extends HttpSecurityBuilder<H>>
|
|||
|
||||
private AccessDeniedHandler accessDeniedHandler;
|
||||
|
||||
private LinkedHashMap<RequestMatcher,AuthenticationEntryPoint> defaultEntryPointMappings = new LinkedHashMap<RequestMatcher, AuthenticationEntryPoint>();
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
* @see HttpSecurity#exceptionHandling()
|
||||
|
@ -96,18 +100,51 @@ public final class ExceptionHandlingConfigurer<H extends HttpSecurityBuilder<H>>
|
|||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link AuthenticationEntryPoint} to be used. Defaults to the
|
||||
* {@link HttpSecurity#getSharedObject(Class)} value. If that is not
|
||||
* provided defaults to {@link Http403ForbiddenEntryPoint}.
|
||||
* Sets the {@link AuthenticationEntryPoint} to be used.
|
||||
*
|
||||
* @param authenticationEntryPoint the {@link AuthenticationEntryPoint} to use
|
||||
* @return the {@link ExceptionHandlingConfigurer} for further customizations
|
||||
* <p>
|
||||
* If no {@link #authenticationEntryPoint(AuthenticationEntryPoint)} is
|
||||
* specified, then
|
||||
* {@link #defaultAuthenticationEntryPointFor(AuthenticationEntryPoint, RequestMatcher)}
|
||||
* will be used. The first {@link AuthenticationEntryPoint} will be used as
|
||||
* the default is no matches were found.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If that is not provided defaults to {@link Http403ForbiddenEntryPoint}.
|
||||
* </p>
|
||||
*
|
||||
* @param authenticationEntryPoint
|
||||
* the {@link AuthenticationEntryPoint} to use
|
||||
* @return the {@link ExceptionHandlingConfigurer} for further
|
||||
* customizations
|
||||
*/
|
||||
public ExceptionHandlingConfigurer<H> authenticationEntryPoint(AuthenticationEntryPoint authenticationEntryPoint) {
|
||||
this.authenticationEntryPoint = authenticationEntryPoint;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a default {@link AuthenticationEntryPoint} to be used which prefers
|
||||
* being invoked for the provided {@link RequestMatcher}. If only a single
|
||||
* default {@link AuthenticationEntryPoint} is specified, it will be what is
|
||||
* used for the default {@link AuthenticationEntryPoint}. If multiple
|
||||
* default {@link AuthenticationEntryPoint} instances are configured, then a
|
||||
* {@link DelegatingAuthenticationEntryPoint} will be used.
|
||||
*
|
||||
* @param entryPoint
|
||||
* the {@link AuthenticationEntryPoint} to use
|
||||
* @param preferredMatcher
|
||||
* the {@link RequestMatcher} for this default
|
||||
* {@link AuthenticationEntryPoint}
|
||||
* @return the {@link ExceptionHandlingConfigurer} for further
|
||||
* customizations
|
||||
*/
|
||||
public ExceptionHandlingConfigurer<H> defaultAuthenticationEntryPointFor(AuthenticationEntryPoint entryPoint, RequestMatcher preferredMatcher) {
|
||||
this.defaultEntryPointMappings.put(preferredMatcher, entryPoint);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets any explicitly configured {@link AuthenticationEntryPoint}
|
||||
* @return
|
||||
|
@ -118,7 +155,7 @@ public final class ExceptionHandlingConfigurer<H extends HttpSecurityBuilder<H>>
|
|||
|
||||
@Override
|
||||
public void configure(H http) throws Exception {
|
||||
AuthenticationEntryPoint entryPoint = getEntryPoint(http);
|
||||
AuthenticationEntryPoint entryPoint = getAuthenticationEntryPoint(http);
|
||||
ExceptionTranslationFilter exceptionTranslationFilter = new ExceptionTranslationFilter(entryPoint, getRequestCache(http));
|
||||
if(accessDeniedHandler != null) {
|
||||
exceptionTranslationFilter.setAccessDeniedHandler(accessDeniedHandler);
|
||||
|
@ -126,25 +163,31 @@ public final class ExceptionHandlingConfigurer<H extends HttpSecurityBuilder<H>>
|
|||
exceptionTranslationFilter = postProcess(exceptionTranslationFilter);
|
||||
http.addFilter(exceptionTranslationFilter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link AuthenticationEntryPoint} according to the rules specified by {@link #authenticationEntryPoint(AuthenticationEntryPoint)}
|
||||
* @param http the {@link HttpSecurity} used to look up shared {@link AuthenticationEntryPoint}
|
||||
* @return the {@link AuthenticationEntryPoint} to use
|
||||
*/
|
||||
AuthenticationEntryPoint getEntryPoint(H http) {
|
||||
AuthenticationEntryPoint getAuthenticationEntryPoint(H http) {
|
||||
AuthenticationEntryPoint entryPoint = this.authenticationEntryPoint;
|
||||
if(entryPoint == null) {
|
||||
AuthenticationEntryPoint sharedEntryPoint = http.getSharedObject(AuthenticationEntryPoint.class);
|
||||
if(sharedEntryPoint != null) {
|
||||
entryPoint = sharedEntryPoint;
|
||||
} else {
|
||||
entryPoint = new Http403ForbiddenEntryPoint();
|
||||
}
|
||||
entryPoint = createDefaultEntryPoint(http);
|
||||
}
|
||||
return entryPoint;
|
||||
}
|
||||
|
||||
private AuthenticationEntryPoint createDefaultEntryPoint(H http) {
|
||||
if(defaultEntryPointMappings.isEmpty()) {
|
||||
return new Http403ForbiddenEntryPoint();
|
||||
}
|
||||
if(defaultEntryPointMappings.size() == 1) {
|
||||
return defaultEntryPointMappings.values().iterator().next();
|
||||
}
|
||||
DelegatingAuthenticationEntryPoint entryPoint = new DelegatingAuthenticationEntryPoint(defaultEntryPointMappings);
|
||||
entryPoint.setDefaultEntryPoint(defaultEntryPointMappings.values().iterator().next());
|
||||
return entryPoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link RequestCache} to use. If one is defined using
|
||||
* {@link #requestCache(org.springframework.security.web.savedrequest.RequestCache)}, then it is used. Otherwise, an
|
||||
|
|
|
@ -15,8 +15,11 @@
|
|||
*/
|
||||
package org.springframework.security.config.annotation.web.configurers;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.security.authentication.AuthenticationDetailsSource;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||
|
@ -25,6 +28,10 @@ import org.springframework.security.web.AuthenticationEntryPoint;
|
|||
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
|
||||
import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint;
|
||||
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
|
||||
import org.springframework.security.web.util.MediaTypeRequestMatcher;
|
||||
import org.springframework.security.web.util.RequestMatcher;
|
||||
import org.springframework.web.accept.ContentNegotiationStrategy;
|
||||
import org.springframework.web.accept.HeaderContentNegotiationStrategy;
|
||||
|
||||
/**
|
||||
* Adds HTTP basic based authentication. All attributes have reasonable defaults
|
||||
|
@ -118,8 +125,22 @@ public final class HttpBasicConfigurer<B extends HttpSecurityBuilder<B>> extends
|
|||
}
|
||||
|
||||
public void init(B http) throws Exception {
|
||||
http
|
||||
.setSharedObject(AuthenticationEntryPoint.class, authenticationEntryPoint);
|
||||
registerDefaultAuthenticationEntryPoint(http);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void registerDefaultAuthenticationEntryPoint(B http) {
|
||||
ExceptionHandlingConfigurer<B> exceptionHandling = http.getConfigurer(ExceptionHandlingConfigurer.class);
|
||||
if(exceptionHandling == null) {
|
||||
return;
|
||||
}
|
||||
ContentNegotiationStrategy contentNegotiationStrategy = http.getSharedObject(ContentNegotiationStrategy.class);
|
||||
if(contentNegotiationStrategy == null) {
|
||||
contentNegotiationStrategy = new HeaderContentNegotiationStrategy();
|
||||
}
|
||||
MediaTypeRequestMatcher preferredMatcher = new MediaTypeRequestMatcher(contentNegotiationStrategy, MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_FORM_URLENCODED, MediaType.APPLICATION_JSON, MediaType.APPLICATION_OCTET_STREAM, MediaType.APPLICATION_XML, MediaType.MULTIPART_FORM_DATA, MediaType.TEXT_XML);
|
||||
preferredMatcher.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL));
|
||||
exceptionHandling.defaultAuthenticationEntryPointFor(postProcess(authenticationEntryPoint), preferredMatcher);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -75,7 +75,7 @@ public final class ServletApiConfigurer<H extends HttpSecurityBuilder<H>> extend
|
|||
public void configure(H http) throws Exception {
|
||||
securityContextRequestFilter.setAuthenticationManager(http.getAuthenticationManager());
|
||||
ExceptionHandlingConfigurer<H> exceptionConf = http.getConfigurer(ExceptionHandlingConfigurer.class);
|
||||
AuthenticationEntryPoint authenticationEntryPoint = exceptionConf == null ? null : exceptionConf.getEntryPoint(http);
|
||||
AuthenticationEntryPoint authenticationEntryPoint = exceptionConf == null ? null : exceptionConf.getAuthenticationEntryPoint(http);
|
||||
securityContextRequestFilter.setAuthenticationEntryPoint(authenticationEntryPoint);
|
||||
LogoutConfigurer<H> logoutConf = http.getConfigurer(LogoutConfigurer.class);
|
||||
List<LogoutHandler> logoutHandlers = logoutConf == null ? null : logoutConf.getLogoutHandlers();
|
||||
|
|
|
@ -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 {}
|
||||
}
|
||||
|
|
|
@ -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}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue