From 428b0e45aa481d03a194faf579e54116df250e18 Mon Sep 17 00:00:00 2001 From: Josh Cummings Date: Tue, 15 May 2018 08:11:46 -0600 Subject: [PATCH] HttpCorsConfigTests groovy->java Issue: gh-4939 --- .../config/http/HttpCorsConfigTests.groovy | 171 ------------------ .../config/http/HttpCorsConfigTests.java | 168 +++++++++++++++++ .../http/HttpCorsConfigTests-RequiresMvc.xml | 31 ++++ .../http/HttpCorsConfigTests-WithCors.xml | 45 +++++ ...onfigTests-WithCorsConfigurationSource.xml | 39 ++++ .../HttpCorsConfigTests-WithCorsFilter.xml | 43 +++++ 6 files changed, 326 insertions(+), 171 deletions(-) delete mode 100644 config/src/test/groovy/org/springframework/security/config/http/HttpCorsConfigTests.groovy create mode 100644 config/src/test/java/org/springframework/security/config/http/HttpCorsConfigTests.java create mode 100644 config/src/test/resources/org/springframework/security/config/http/HttpCorsConfigTests-RequiresMvc.xml create mode 100644 config/src/test/resources/org/springframework/security/config/http/HttpCorsConfigTests-WithCors.xml create mode 100644 config/src/test/resources/org/springframework/security/config/http/HttpCorsConfigTests-WithCorsConfigurationSource.xml create mode 100644 config/src/test/resources/org/springframework/security/config/http/HttpCorsConfigTests-WithCorsFilter.xml diff --git a/config/src/test/groovy/org/springframework/security/config/http/HttpCorsConfigTests.groovy b/config/src/test/groovy/org/springframework/security/config/http/HttpCorsConfigTests.groovy deleted file mode 100644 index f18f142173..0000000000 --- a/config/src/test/groovy/org/springframework/security/config/http/HttpCorsConfigTests.groovy +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright 2002-2016 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.http - -import org.springframework.beans.factory.BeanCreationException - -import javax.servlet.http.HttpServletResponse - -import org.springframework.http.* -import org.springframework.mock.web.* -import org.springframework.security.web.authentication.Http403ForbiddenEntryPoint -import org.springframework.web.bind.annotation.* -import org.springframework.web.filter.CorsFilter -import org.springframework.web.cors.CorsConfiguration -import org.springframework.web.cors.UrlBasedCorsConfigurationSource - -/** - * - * @author Rob Winch - * @author Tim Ysewyn - */ -class HttpCorsConfigTests extends AbstractHttpConfigTests { - MockHttpServletRequest request - MockHttpServletResponse response - MockFilterChain chain - - def setup() { - request = new MockHttpServletRequest(method:"GET") - response = new MockHttpServletResponse() - chain = new MockFilterChain() - } - - def "No MVC throws meaningful error"() { - when: - xml.http('entry-point-ref' : 'ep') { - 'cors'() - 'intercept-url'(pattern:'/**', access: 'authenticated') - } - bean('ep', Http403ForbiddenEntryPoint) - createAppContext() - then: - BeanCreationException success = thrown() - success.message.contains("Please ensure Spring Security & Spring MVC are configured in a shared ApplicationContext") - } - - def "HandlerMappingIntrospector explicit"() { - setup: - xml.http('entry-point-ref' : 'ep') { - 'cors'() - 'intercept-url'(pattern:'/**', access: 'authenticated') - } - bean('ep', Http403ForbiddenEntryPoint) - bean('controller', CorsController) - xml.'mvc:annotation-driven'() - createAppContext() - when: - addCors() - springSecurityFilterChain.doFilter(request,response,chain) - then: 'Ensure we a CORS response w/ Spring Security headers too' - responseHeaders['Access-Control-Allow-Origin'] - responseHeaders['X-Content-Type-Options'] - when: - setup() - addCors(true) - springSecurityFilterChain.doFilter(request,response,chain) - then: 'Ensure we a CORS response w/ Spring Security headers too' - responseHeaders['Access-Control-Allow-Origin'] - responseHeaders['X-Content-Type-Options'] - response.status == HttpServletResponse.SC_OK - } - - def "CorsConfigurationSource"() { - setup: - xml.http('entry-point-ref' : 'ep') { - 'cors'('configuration-source-ref':'ccs') - 'intercept-url'(pattern:'/**', access: 'authenticated') - } - bean('ep', Http403ForbiddenEntryPoint) - bean('ccs', MyCorsConfigurationSource) - createAppContext() - when: - addCors() - springSecurityFilterChain.doFilter(request,response,chain) - then: 'Ensure we a CORS response w/ Spring Security headers too' - responseHeaders['Access-Control-Allow-Origin'] - responseHeaders['X-Content-Type-Options'] - when: - setup() - addCors(true) - springSecurityFilterChain.doFilter(request,response,chain) - then: 'Ensure we a CORS response w/ Spring Security headers too' - responseHeaders['Access-Control-Allow-Origin'] - responseHeaders['X-Content-Type-Options'] - response.status == HttpServletResponse.SC_OK - } - - def "CorsFilter"() { - setup: - xml.http('entry-point-ref' : 'ep') { - 'cors'('ref' : 'cf') - 'intercept-url'(pattern:'/**', access: 'authenticated') - } - xml.'b:bean'(id: 'cf', 'class': CorsFilter.name) { - 'b:constructor-arg'(ref: 'ccs') - } - bean('ep', Http403ForbiddenEntryPoint) - bean('ccs', MyCorsConfigurationSource) - createAppContext() - when: - addCors() - springSecurityFilterChain.doFilter(request,response,chain) - then: 'Ensure we a CORS response w/ Spring Security headers too' - responseHeaders['Access-Control-Allow-Origin'] - responseHeaders['X-Content-Type-Options'] - when: - setup() - addCors(true) - springSecurityFilterChain.doFilter(request,response,chain) - then: 'Ensure we a CORS response w/ Spring Security headers too' - responseHeaders['Access-Control-Allow-Origin'] - responseHeaders['X-Content-Type-Options'] - response.status == HttpServletResponse.SC_OK - } - - def addCors(boolean isPreflight=false) { - request.addHeader(HttpHeaders.ORIGIN,"https://example.com") - if(!isPreflight) { - return - } - request.method = HttpMethod.OPTIONS.name() - request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, HttpMethod.POST.name()) - } - - def getResponseHeaders() { - def headers = [:] - response.headerNames.each { name -> - headers.put(name, response.getHeaderValues(name).join(',')) - } - return headers - } - - @RestController - @CrossOrigin(methods = [ - RequestMethod.GET, RequestMethod.POST - ]) - static class CorsController { - @RequestMapping("/") - String hello() { - "Hello" - } - } - - static class MyCorsConfigurationSource extends UrlBasedCorsConfigurationSource { - MyCorsConfigurationSource() { - registerCorsConfiguration('/**', new CorsConfiguration(allowedOrigins : ['*'], allowedMethods : [ - RequestMethod.GET.name(), - RequestMethod.POST.name() - ])) - } - } -} diff --git a/config/src/test/java/org/springframework/security/config/http/HttpCorsConfigTests.java b/config/src/test/java/org/springframework/security/config/http/HttpCorsConfigTests.java new file mode 100644 index 0000000000..52c3245a12 --- /dev/null +++ b/config/src/test/java/org/springframework/security/config/http/HttpCorsConfigTests.java @@ -0,0 +1,168 @@ +/* + * 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.http; + +import org.junit.Rule; +import org.junit.Test; +import org.springframework.beans.factory.BeanCreationException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.security.config.test.SpringTestRule; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultMatcher; +import org.springframework.test.web.servlet.request.RequestPostProcessor; +import org.springframework.web.bind.annotation.CrossOrigin; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; + +import java.util.Arrays; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.options; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * + * @author Rob Winch + * @author Tim Ysewyn + * @author Josh Cummings + */ +public class HttpCorsConfigTests { + + private static final String CONFIG_LOCATION_PREFIX = + "classpath:org/springframework/security/config/http/HttpCorsConfigTests"; + + @Rule + public final SpringTestRule spring = new SpringTestRule(); + + @Autowired + MockMvc mvc; + + @Test + public void autowireWhenMissingMvcThenGivesInformativeError() { + assertThatThrownBy(() -> + this.spring.configLocations(this.xml("RequiresMvc")).autowire()) + .isInstanceOf(BeanCreationException.class) + .hasMessageContaining("Please ensure Spring Security & Spring MVC are configured in a shared ApplicationContext"); + } + + @Test + public void getWhenUsingCorsThenDoesSpringSecurityCorsHandshake() + throws Exception { + + this.spring.configLocations(this.xml("WithCors")).autowire(); + + this.mvc.perform(get("/").with(this.approved())) + .andExpect(corsResponseHeaders()) + .andExpect((status().isIAmATeapot())); + + this.mvc.perform(options("/").with(this.preflight())) + .andExpect(corsResponseHeaders()) + .andExpect(status().isOk()); + } + + @Test + public void getWhenUsingCustomCorsConfigurationSourceThenDoesSpringSecurityCorsHandshake() + throws Exception { + + this.spring.configLocations(this.xml("WithCorsConfigurationSource")).autowire(); + + this.mvc.perform(get("/").with(this.approved())) + .andExpect(corsResponseHeaders()) + .andExpect((status().isIAmATeapot())); + + this.mvc.perform(options("/").with(this.preflight())) + .andExpect(corsResponseHeaders()) + .andExpect(status().isOk()); + } + + @Test + public void getWhenUsingCustomCorsFilterThenDoesSPringSecurityCorsHandshake() + throws Exception { + + this.spring.configLocations(this.xml("WithCorsFilter")).autowire(); + + this.mvc.perform(get("/").with(this.approved())) + .andExpect(corsResponseHeaders()) + .andExpect((status().isIAmATeapot())); + + this.mvc.perform(options("/").with(this.preflight())) + .andExpect(corsResponseHeaders()) + .andExpect(status().isOk()); + } + + @RestController + @CrossOrigin(methods = { + RequestMethod.GET, RequestMethod.POST + }) + static class CorsController { + @RequestMapping("/") + String hello() { + return "Hello"; + } + } + + static class MyCorsConfigurationSource extends UrlBasedCorsConfigurationSource { + MyCorsConfigurationSource() { + CorsConfiguration configuration = new CorsConfiguration(); + configuration.setAllowedOrigins(Arrays.asList("*")); + configuration.setAllowedMethods(Arrays.asList(RequestMethod.GET.name(), RequestMethod.POST.name())); + + super.registerCorsConfiguration( + "/**", + configuration); + } + } + + private String xml(String configName) { + return CONFIG_LOCATION_PREFIX + "-" + configName + ".xml"; + } + + private RequestPostProcessor preflight() { + return cors(true); + } + + private RequestPostProcessor approved() { + return cors(false); + } + + private RequestPostProcessor cors(boolean preflight) { + return (request) -> { + request.addHeader(HttpHeaders.ORIGIN, "https://example.com"); + + if ( preflight ) { + request.setMethod(HttpMethod.OPTIONS.name()); + request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, HttpMethod.POST.name()); + } + + return request; + }; + } + + private ResultMatcher corsResponseHeaders() { + return result -> { + header().exists("Access-Control-Allow-Origin").match(result); + header().exists("X-Content-Type-Options").match(result); + }; + } + +} diff --git a/config/src/test/resources/org/springframework/security/config/http/HttpCorsConfigTests-RequiresMvc.xml b/config/src/test/resources/org/springframework/security/config/http/HttpCorsConfigTests-RequiresMvc.xml new file mode 100644 index 0000000000..9a66ab3a5b --- /dev/null +++ b/config/src/test/resources/org/springframework/security/config/http/HttpCorsConfigTests-RequiresMvc.xml @@ -0,0 +1,31 @@ + + + + + + + + + + diff --git a/config/src/test/resources/org/springframework/security/config/http/HttpCorsConfigTests-WithCors.xml b/config/src/test/resources/org/springframework/security/config/http/HttpCorsConfigTests-WithCors.xml new file mode 100644 index 0000000000..ac57e95b45 --- /dev/null +++ b/config/src/test/resources/org/springframework/security/config/http/HttpCorsConfigTests-WithCors.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/config/src/test/resources/org/springframework/security/config/http/HttpCorsConfigTests-WithCorsConfigurationSource.xml b/config/src/test/resources/org/springframework/security/config/http/HttpCorsConfigTests-WithCorsConfigurationSource.xml new file mode 100644 index 0000000000..b73f227634 --- /dev/null +++ b/config/src/test/resources/org/springframework/security/config/http/HttpCorsConfigTests-WithCorsConfigurationSource.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + diff --git a/config/src/test/resources/org/springframework/security/config/http/HttpCorsConfigTests-WithCorsFilter.xml b/config/src/test/resources/org/springframework/security/config/http/HttpCorsConfigTests-WithCorsFilter.xml new file mode 100644 index 0000000000..28daed34b1 --- /dev/null +++ b/config/src/test/resources/org/springframework/security/config/http/HttpCorsConfigTests-WithCorsFilter.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + +