ATEST-259:

- adding tests, re-implementing a simpler CORS filter than previously
used
This commit is contained in:
Zellweger Christof Ralf
2015-10-28 12:52:32 +01:00
parent 6aa5801491
commit cd72af733a
9 changed files with 225 additions and 84 deletions

View File

@ -5,11 +5,6 @@
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.wst.common.project.facet.core.builder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
@ -25,6 +20,5 @@
<nature>org.springframework.ide.eclipse.core.springnature</nature>
<nature>org.springsource.ide.eclipse.gradle.core.nature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.wst.common.project.facet.core.nature</nature>
</natures>
</projectDescription>

View File

@ -1,5 +1,5 @@
#
#Wed Sep 16 07:23:26 CEST 2015
#Wed Oct 28 07:37:57 CET 2015
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.compliance=1.8
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve

View File

@ -30,6 +30,9 @@ dependencies {
testCompile libraries.spring_boot_starter_test
testCompile libraries.jsonassert
testCompile libraries.jsonpath
testCompile 'com.jayway.restassured:rest-assured:2.4.1'
testCompile 'com.jayway.restassured:spring-mock-mvc:2.4.1'
}
uploadArchives {

View File

@ -24,12 +24,6 @@ import org.springframework.util.StringUtils;
import org.springframework.validation.Validator;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import ch.psi.daq.cassandra.util.test.CassandraDataGen;
import ch.psi.daq.common.json.deserialize.AttributeBasedDeserializer;
import ch.psi.daq.common.statistic.StorelessStatistics;
@ -42,12 +36,18 @@ import ch.psi.daq.query.model.Query;
import ch.psi.daq.query.model.QueryField;
import ch.psi.daq.query.model.impl.AbstractQuery;
import ch.psi.daq.queryrest.controller.validator.QueryValidator;
import ch.psi.daq.queryrest.filter.SimpleCORSFilter;
import ch.psi.daq.queryrest.filter.CorsFilter;
import ch.psi.daq.queryrest.model.PropertyFilterMixin;
import ch.psi.daq.queryrest.response.JsonByteArraySerializer;
import ch.psi.daq.queryrest.response.JsonStreamSerializer;
import ch.psi.daq.queryrest.response.ResponseStreamWriter;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
@Configuration
@PropertySource(value = {"classpath:queryrest.properties"})
@PropertySource(value = {"file:${user.home}/.config/daq/queryrest.properties"}, ignoreResourceNotFound = true)
@ -181,9 +181,14 @@ public class QueryRestConfig extends WebMvcConfigurerAdapter {
return new QueryValidator();
}
// @Bean
// public Filter regexCORSFilter() {
// return new RegexCORSFilter();
// }
@Bean
public Filter simpleCORSFilter() {
return new SimpleCORSFilter();
public Filter corsFilter() {
return new CorsFilter();
}
// ==========================================================================================

View File

@ -0,0 +1,80 @@
package ch.psi.daq.queryrest.filter;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.filter.OncePerRequestFilter;
public class CorsFilter extends OncePerRequestFilter {
private static final String ALLOW_ORIGIN_HEADER = "Access-Control-Allow-Origin";
@Value("${queryrest.cors.allowedorigins}")
private String configuredOrigins;
@Value("${queryrest.cors.forceallheaders}")
private boolean forceAllHeaders;
/**
* @{inheritDoc
*/
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
Set<String> allowedOrigins = new HashSet<String>(Arrays.asList(configuredOrigins.split(","))
.stream()
.map(s -> {
return s.trim(); })
.collect(Collectors.toList()));
String originHeader = request.getHeader("Origin");
if (forceAllHeaders) {
// include headers no matter what - good for development
if (allowedOrigins.contains(originHeader)) {
response.addHeader(ALLOW_ORIGIN_HEADER, originHeader);
} else {
response.addHeader(ALLOW_ORIGIN_HEADER, "*");
}
setDefaultCorsHeaders(response);
} else if (request.getHeader("Access-Control-Request-Method") != null && "OPTIONS".equals(request.getMethod())) {
// this is for 'real' Cross-site browser requests
if (allowedOrigins.contains(originHeader)) {
response.addHeader(ALLOW_ORIGIN_HEADER, originHeader);
}
setDefaultCorsHeaders(response);
}
filterChain.doFilter(request, response);
}
private void setDefaultCorsHeaders(HttpServletResponse response) {
response.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
response.addHeader("Access-Control-Allow-Headers", "Origin, Authorization, Accept, Content-Type");
response.addHeader("Access-Control-Max-Age", "1800");
}
public void setConfiguredOrigins(String configuredOrigins) {
this.configuredOrigins = configuredOrigins;
}
public void setForceAllHeaders(boolean forceAllHeaders) {
this.forceAllHeaders = forceAllHeaders;
}
}

View File

@ -1,41 +0,0 @@
package ch.psi.daq.queryrest.filter;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class SimpleCORSFilter implements Filter {
@Value("${queryrest.enableCORS}")
private boolean enableCORS;
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException,
ServletException {
if (enableCORS) {
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers",
"Content-Type, Cache-Control, Accept, Authorization, X-Requested-With");
}
chain.doFilter(req, res);
}
public void init(FilterConfig filterConfig) {
}
public void destroy() {
}
}

View File

@ -7,3 +7,11 @@ queryrest.default.response.aggregations=min,max,sum
# enables / disables the CORS servlet filter. Adds multiple CORS headers to the response
queryrest.enableCORS=true
# includes the CORS headers no matter what request or preflight was sent. If an Origin header is set, this header will be used.
# If no Origin header is set, '*' will be used.
queryrest.cors.forceallheaders=true
# defines a specific regex to be used in the Access-Control-Allow-Origin. This property needs to be specified
# but might be null or the empty string (""), i.e. don't comment it out.
queryrest.cors.allowedorigins=http://localhost:8080, *

View File

@ -27,9 +27,9 @@ import ch.psi.daq.test.cassandra.CassandraDaqUnitDependencyInjectionTestExecutio
CassandraDaqUnitDependencyInjectionTestExecutionListener.class,
DependencyInjectionTestExecutionListener.class})
@SpringApplicationConfiguration(classes = {
QueryRestApplication.class
,QueryRestConfig.class
,DaqWebMvcConfig.class
QueryRestApplication.class,
QueryRestConfig.class,
DaqWebMvcConfig.class
})
@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
@WebAppConfiguration

View File

@ -8,6 +8,7 @@ import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import ch.psi.daq.cassandra.util.test.CassandraDataGen;
import ch.psi.daq.common.ordering.Ordering;
@ -16,13 +17,14 @@ import ch.psi.daq.query.model.impl.PulseRangeQuery;
import ch.psi.daq.query.model.impl.TimeRangeQuery;
import ch.psi.daq.query.model.impl.TimeRangeQueryDate;
import ch.psi.daq.queryrest.controller.QueryRestController;
import ch.psi.daq.queryrest.filter.CorsFilter;
import ch.psi.daq.test.cassandra.admin.CassandraTestAdmin;
import ch.psi.daq.test.queryrest.AbstractDaqRestTest;
/**
* Tests the {@link DaqController} implementation.
*/
public class DaqRestControllerTest extends AbstractDaqRestTest {
public class QueryRestControllerTest extends AbstractDaqRestTest {
@Resource
private CassandraTestAdmin cassandraTestAdmin;
@ -30,7 +32,10 @@ public class DaqRestControllerTest extends AbstractDaqRestTest {
@Resource
private CassandraDataGen dataGen;
public static final String[] TEST_CHANNEL_NAMES = new String[]{"testChannel1", "testChannel2"};
@Resource
private CorsFilter corsFilter;
public static final String[] TEST_CHANNEL_NAMES = new String[] {"testChannel1", "testChannel2"};
@After
public void tearDown() throws Exception {}
@ -68,6 +73,92 @@ public class DaqRestControllerTest extends AbstractDaqRestTest {
.andExpect(MockMvcResultMatchers.jsonPath("$[4]").doesNotExist());
}
@Test
public void testCorsFilterNoHeaders() throws Exception {
corsFilter.setForceAllHeaders(false);
this.mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).addFilters(corsFilter).build();
this.mockMvc.perform(
MockMvcRequestBuilders
.options(QueryRestController.CHANNELS)
.contentType(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultHandlers.print())
.andExpect(MockMvcResultMatchers.status().isOk())
// we didn't set the 'Origin' header so no access-control
.andExpect(MockMvcResultMatchers.header().doesNotExist("Access-Control-Allow-Origin"));
}
@Test
public void testCorsFilterIncludesHeaders() throws Exception {
// all headers are set
this.mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).addFilters(corsFilter).build();
this.mockMvc.perform(
MockMvcRequestBuilders
.options(QueryRestController.CHANNELS)
.header("Origin", "*")
.contentType(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultHandlers.print())
.andExpect(MockMvcResultMatchers.status().isOk())
// we didn't set the 'Origin' header so no access-control
.andExpect(MockMvcResultMatchers.header().string("Access-Control-Allow-Origin", "*"));
this.mockMvc.perform(
MockMvcRequestBuilders
.options(QueryRestController.CHANNELS)
.header("Origin", "http://localhost:8080")
.contentType(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultHandlers.print())
.andExpect(MockMvcResultMatchers.status().isOk())
// we didn't set the 'Origin' header so no access-control
.andExpect(MockMvcResultMatchers.header().string("Access-Control-Allow-Origin", "http://localhost:8080"));
this.mockMvc.perform(
MockMvcRequestBuilders
.options(QueryRestController.CHANNELS)
.header("Origin", "someBogusDomain.com")
.contentType(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultHandlers.print())
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.header().string("Access-Control-Allow-Origin", "*"));
}
@Test
public void testCorsFilterMismatchSpecificOrigin() throws Exception {
corsFilter.setForceAllHeaders(false);
this.mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).addFilters(corsFilter).build();
this.mockMvc
.perform(
MockMvcRequestBuilders
.options(QueryRestController.CHANNELS)
.header("Origin", "*")
.header("Access-Control-Request-Method", "GET")
.contentType(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultHandlers.print())
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.header().string("Access-Control-Allow-Origin", "*"))
.andExpect(
MockMvcResultMatchers.header().string("Access-Control-Allow-Headers",
"Origin, Authorization, Accept, Content-Type"));
this.mockMvc
.perform(
MockMvcRequestBuilders
.options(QueryRestController.CHANNELS)
.header("Origin", "someBogusDomain.com")
.header("Access-Control-Request-Method", "GET")
.contentType(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultHandlers.print())
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.header().doesNotExist("Access-Control-Allow-Origin"))
.andExpect(
MockMvcResultMatchers.header().string("Access-Control-Allow-Headers",
"Origin, Authorization, Accept, Content-Type"));
}
@Test
public void testPulseRangeQuery() throws Exception {
PulseRangeQuery request = new PulseRangeQuery(
@ -204,4 +295,5 @@ public class DaqRestControllerTest extends AbstractDaqRestTest {
.andExpect(MockMvcResultMatchers.jsonPath("$[0].data.maxima.max.event.pulseId").value(101));
}
}