RequestCacheConfigurerTests groovy->java

Issue: gh-4939
This commit is contained in:
Josh Cummings 2018-11-16 15:40:12 -07:00
parent 686393ed5c
commit f30fcdda6b
No known key found for this signature in database
GPG Key ID: 49EF60DD7FF83443
2 changed files with 273 additions and 209 deletions

View File

@ -1,209 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configurers
import org.springframework.security.core.userdetails.PasswordEncodedUser
import javax.servlet.http.HttpServletResponse
import org.springframework.context.annotation.Configuration
import org.springframework.http.MediaType
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.web.configuration.EnableWebSecurity
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
import org.springframework.security.web.savedrequest.RequestCache
import org.springframework.security.web.savedrequest.RequestCacheAwareFilter
import spock.lang.Unroll;
/**
*
* @author Rob Winch
*/
class RequestCacheConfigurerTests extends BaseSpringSpec {
def "requestCache ObjectPostProcessor"() {
setup:
AnyObjectPostProcessor opp = Mock()
HttpSecurity http = new HttpSecurity(opp, authenticationBldr, [:])
when:
http
.requestCache()
.and()
.build()
then: "RequestCacheAwareFilter is registered with LifecycleManager"
1 * opp.postProcess(_ as RequestCacheAwareFilter) >> {RequestCacheAwareFilter o -> o}
}
def "invoke requestCache twice does not reset"() {
setup:
RequestCache RC = Mock()
AnyObjectPostProcessor opp = Mock()
HttpSecurity http = new HttpSecurity(opp, authenticationBldr, [:])
when:
http
.requestCache()
.requestCache(RC)
.and()
.requestCache()
then:
http.getSharedObject(RequestCache) == RC
}
def "RequestCache disables faviocon.ico"() {
setup:
loadConfig(RequestCacheDefautlsConfig)
request.servletPath = "/favicon.ico"
request.requestURI = "/favicon.ico"
request.method = "GET"
when: "request favicon.ico"
springSecurityFilterChain.doFilter(request,response,chain)
then: "sent to the login page"
response.status == HttpServletResponse.SC_MOVED_TEMPORARILY
response.redirectedUrl == "http://localhost/login"
when: "authenticate successfully"
super.setupWeb(request.session)
request.servletPath = "/login"
request.setParameter("username","user")
request.setParameter("password","password")
request.method = "POST"
springSecurityFilterChain.doFilter(request,response,chain)
then: "sent to default URL since it was favicon.ico"
response.status == HttpServletResponse.SC_MOVED_TEMPORARILY
response.redirectedUrl == "/"
}
def "RequestCache disables faviocon.png"() {
setup:
loadConfig(RequestCacheDefautlsConfig)
request.servletPath = "/favicon.png"
request.requestURI = "/favicon.png"
request.method = "GET"
when: "request favicon.ico"
springSecurityFilterChain.doFilter(request,response,chain)
then: "sent to the login page"
response.status == HttpServletResponse.SC_MOVED_TEMPORARILY
response.redirectedUrl == "http://localhost/login"
when: "authenticate successfully"
super.setupWeb(request.session)
request.servletPath = "/login"
request.setParameter("username","user")
request.setParameter("password","password")
request.method = "POST"
springSecurityFilterChain.doFilter(request,response,chain)
then: "sent to default URL since it was favicon.ico"
response.status == HttpServletResponse.SC_MOVED_TEMPORARILY
response.redirectedUrl == "/"
}
def "SEC-2321: RequestCache disables application/json"() {
setup:
loadConfig(RequestCacheDefautlsConfig)
request.addHeader("Accept", MediaType.APPLICATION_JSON_VALUE)
request.method = "GET"
request.servletPath = "/messages"
request.requestURI = "/messages"
when: "request application/json"
springSecurityFilterChain.doFilter(request,response,chain)
then: "sent to the login page"
response.status == HttpServletResponse.SC_MOVED_TEMPORARILY
response.redirectedUrl == "http://localhost/login"
when: "authenticate successfully"
super.setupWeb(request.session)
request.servletPath = "/login"
request.setParameter("username","user")
request.setParameter("password","password")
request.method = "POST"
springSecurityFilterChain.doFilter(request,response,chain)
then: "sent to default URL since it was application/json. This is desirable since JSON requests are typically not invoked directly from the browser and we don't want the browser to replay them"
response.status == HttpServletResponse.SC_MOVED_TEMPORARILY
response.redirectedUrl == "/"
}
def "SEC-2321: RequestCache disables X-Requested-With"() {
setup:
loadConfig(RequestCacheDefautlsConfig)
request.addHeader("X-Requested-With", "XMLHttpRequest")
request.method = "GET"
request.servletPath = "/messages"
request.requestURI = "/messages"
when: "request X-Requested-With"
springSecurityFilterChain.doFilter(request,response,chain)
then: "sent to the login page"
response.status == HttpServletResponse.SC_MOVED_TEMPORARILY
response.redirectedUrl == "http://localhost/login"
when: "authenticate successfully"
super.setupWeb(request.session)
request.servletPath = "/login"
request.setParameter("username","user")
request.setParameter("password","password")
request.method = "POST"
springSecurityFilterChain.doFilter(request,response,chain)
then: "sent to default URL since it was X-Requested-With"
response.status == HttpServletResponse.SC_MOVED_TEMPORARILY
response.redirectedUrl == "/"
}
@Unroll
def "RequestCache saves #headerName: #headerValue"() {
setup:
loadConfig(RequestCacheDefautlsConfig)
request.addHeader(headerName, headerValue)
request.method = "GET"
request.servletPath = "/messages"
request.requestURI = "/messages"
when: "request content type"
springSecurityFilterChain.doFilter(request,response,chain)
super.setupWeb(request.session)
request.servletPath = "/login"
request.setParameter("username","user")
request.setParameter("password","password")
request.method = "POST"
springSecurityFilterChain.doFilter(request,response,chain)
then: "sent to saved URL"
response.status == HttpServletResponse.SC_MOVED_TEMPORARILY
response.redirectedUrl == "http://localhost/messages"
where:
headerName << ["Accept", "Accept", "Accept", "X-Requested-With"]
headerValue << [MediaType.ALL_VALUE, MediaType.TEXT_HTML, "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8","com.android"]
}
@EnableWebSecurity
static class RequestCacheDefautlsConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser(PasswordEncodedUser.user());
}
}
}

View File

@ -0,0 +1,273 @@
/*
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configurers;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.junit.Rule;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.security.config.annotation.ObjectPostProcessor;
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.config.test.SpringTestRule;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.savedrequest.RequestCache;
import org.springframework.security.web.savedrequest.RequestCacheAwareFilter;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.RequestBuilder;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
/**
* Tests for {@link RequestCacheConfigurer}
*
* @author Rob Winch
* @author Josh Cummings
*/
public class RequestCacheConfigurerTests {
@Rule
public final SpringTestRule spring = new SpringTestRule();
@Autowired
MockMvc mvc;
@Test
public void configureWhenRegisteringObjectPostProcessorThenInvokedOnExceptionTranslationFilter() {
this.spring.register(ObjectPostProcessorConfig.class, DefaultSecurityConfig.class).autowire();
verify(ObjectPostProcessorConfig.objectPostProcessor)
.postProcess(any(RequestCacheAwareFilter.class));
}
@EnableWebSecurity
static class ObjectPostProcessorConfig extends WebSecurityConfigurerAdapter {
static ObjectPostProcessor<Object> objectPostProcessor = spy(ReflectingObjectPostProcessor.class);
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.requestCache();
// @formatter:on
}
@Bean
static ObjectPostProcessor<Object> objectPostProcessor() {
return objectPostProcessor;
}
}
static class ReflectingObjectPostProcessor implements ObjectPostProcessor<Object> {
@Override
public <O> O postProcess(O object) {
return object;
}
}
@Test
public void getWhenInvokingExceptionHandlingTwiceThenOriginalEntryPointUsed() throws Exception {
this.spring.register(InvokeTwiceDoesNotOverrideConfig.class).autowire();
this.mvc.perform(get("/"));
verify(InvokeTwiceDoesNotOverrideConfig.requestCache)
.getMatchingRequest(any(HttpServletRequest.class), any(HttpServletResponse.class));
}
@EnableWebSecurity
static class InvokeTwiceDoesNotOverrideConfig extends WebSecurityConfigurerAdapter {
static RequestCache requestCache = mock(RequestCache.class);
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.requestCache()
.requestCache(requestCache)
.and()
.requestCache();
// @formatter:on
}
}
@Test
public void getWhenBookmarkedUrlIsFaviconIcoThenPostAuthenticationRedirectsToRoot() throws Exception {
this.spring.register(RequestCacheDefaultsConfig.class, DefaultSecurityConfig.class).autowire();
MockHttpSession session = (MockHttpSession)
this.mvc.perform(get("/favicon.ico"))
.andExpect(redirectedUrl("http://localhost/login"))
.andReturn().getRequest().getSession();
this.mvc.perform(formLogin(session))
.andExpect(redirectedUrl("/")); // ignores favicon.ico
}
@Test
public void getWhenBookmarkedUrlIsFaviconPngThenPostAuthenticationRedirectsToRoot() throws Exception {
this.spring.register(RequestCacheDefaultsConfig.class, DefaultSecurityConfig.class).autowire();
MockHttpSession session = (MockHttpSession)
this.mvc.perform(get("/favicon.png"))
.andExpect(redirectedUrl("http://localhost/login"))
.andReturn().getRequest().getSession();
this.mvc.perform(formLogin(session))
.andExpect(redirectedUrl("/")); // ignores favicon.png
}
// SEC-2321
@Test
public void getWhenBookmarkedRequestIsApplicationJsonThenPostAuthenticationRedirectsToRoot() throws Exception {
this.spring.register(RequestCacheDefaultsConfig.class, DefaultSecurityConfig.class).autowire();
MockHttpSession session = (MockHttpSession)
this.mvc.perform(get("/messages")
.header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON))
.andExpect(redirectedUrl("http://localhost/login"))
.andReturn().getRequest().getSession();
this.mvc.perform(formLogin(session))
.andExpect(redirectedUrl("/")); // ignores application/json
// This is desirable since JSON requests are typically not invoked directly from the browser and we don't want the browser to replay them
}
// SEC-2321
@Test
public void getWhenBookmarkedRequestIsXRequestedWithThenPostAuthenticationRedirectsToRoot() throws Exception {
this.spring.register(RequestCacheDefaultsConfig.class, DefaultSecurityConfig.class).autowire();
MockHttpSession session = (MockHttpSession)
this.mvc.perform(get("/messages")
.header("X-Requested-With", "XMLHttpRequest"))
.andExpect(redirectedUrl("http://localhost/login"))
.andReturn().getRequest().getSession();
this.mvc.perform(formLogin(session))
.andExpect(redirectedUrl("/"));
// This is desirable since XHR requests are typically not invoked directly from the browser and we don't want the browser to replay them
}
@Test
public void getWhenBookmarkedRequestIsAllMediaTypeThenPostAuthenticationRemembers() throws Exception {
this.spring.register(RequestCacheDefaultsConfig.class, DefaultSecurityConfig.class).autowire();
MockHttpSession session = (MockHttpSession)
this.mvc.perform(get("/messages")
.header(HttpHeaders.ACCEPT, MediaType.ALL))
.andExpect(redirectedUrl("http://localhost/login"))
.andReturn().getRequest().getSession();
this.mvc.perform(formLogin(session))
.andExpect(redirectedUrl("http://localhost/messages"));
}
@Test
public void getWhenBookmarkedRequestIsTextHtmlThenPostAuthenticationRemembers() throws Exception {
this.spring.register(RequestCacheDefaultsConfig.class, DefaultSecurityConfig.class).autowire();
MockHttpSession session = (MockHttpSession)
this.mvc.perform(get("/messages")
.header(HttpHeaders.ACCEPT, MediaType.TEXT_HTML))
.andExpect(redirectedUrl("http://localhost/login"))
.andReturn().getRequest().getSession();
this.mvc.perform(formLogin(session))
.andExpect(redirectedUrl("http://localhost/messages"));
}
@Test
public void getWhenBookmarkedRequestIsChromeThenPostAuthenticationRemembers() throws Exception {
this.spring.register(RequestCacheDefaultsConfig.class, DefaultSecurityConfig.class).autowire();
MockHttpSession session = (MockHttpSession)
this.mvc.perform(get("/messages")
.header(HttpHeaders.ACCEPT, "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"))
.andExpect(redirectedUrl("http://localhost/login"))
.andReturn().getRequest().getSession();
this.mvc.perform(formLogin(session))
.andExpect(redirectedUrl("http://localhost/messages"));
}
@Test
public void getWhenBookmarkedRequestIsRequestedWithAndroidThenPostAuthenticationRemembers() throws Exception {
this.spring.register(RequestCacheDefaultsConfig.class, DefaultSecurityConfig.class).autowire();
MockHttpSession session = (MockHttpSession)
this.mvc.perform(get("/messages")
.header("X-Requested-With", "com.android"))
.andExpect(redirectedUrl("http://localhost/login"))
.andReturn().getRequest().getSession();
this.mvc.perform(formLogin(session))
.andExpect(redirectedUrl("http://localhost/messages"));
}
@EnableWebSecurity
static class RequestCacheDefaultsConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin();
}
}
@EnableWebSecurity
static class DefaultSecurityConfig {
@Bean
public InMemoryUserDetailsManager userDetailsManager() {
return new InMemoryUserDetailsManager(User.withDefaultPasswordEncoder()
.username("user")
.password("password")
.roles("USER")
.build()
);
}
}
private static RequestBuilder formLogin(MockHttpSession session) {
return post("/login")
.param("username", "user")
.param("password", "password")
.session(session)
.with(csrf());
}
}