Improve testing on CorsInterceptor
This commit is contained in:
parent
ca602bdc1e
commit
69871bb8c2
|
@ -19,14 +19,50 @@ public class CorsInterceptor extends InterceptorAdapter {
|
||||||
private CorsConfiguration myConfig;
|
private CorsConfiguration myConfig;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor which creates an interceptor with default CORS configuration for use in
|
||||||
|
* a FHIR server. This includes:
|
||||||
|
* <ul>
|
||||||
|
* <li>Allowed Origin: *</li>
|
||||||
|
* <li>Allowed Header: Origin</li>
|
||||||
|
* <li>Allowed Header: Accept</li>
|
||||||
|
* <li>Allowed Header: X-Requested-With</li>
|
||||||
|
* <li>Allowed Header: Content-Type</li>
|
||||||
|
* <li>Allowed Header: Access-Control-Request-Method</li>
|
||||||
|
* <li>Allowed Header: Access-Control-Request-Headers</li>
|
||||||
|
* <li>Exposed Header: Location</li>
|
||||||
|
* <li>Exposed Header: Content-Location</li>
|
||||||
|
* </ul>
|
||||||
|
* Note that this configuration is useful for quickly getting CORS working, but
|
||||||
|
* in a real production system you probably want to consider whether it is
|
||||||
|
* appropriate for your situation. In particular, using "Allowed Origin: *"
|
||||||
|
* isn't always the right thing to do.
|
||||||
*/
|
*/
|
||||||
public CorsInterceptor() {
|
public CorsInterceptor() {
|
||||||
this(new CorsConfiguration());
|
this(createDefaultCorsConfig());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static CorsConfiguration createDefaultCorsConfig() {
|
||||||
|
CorsConfiguration retVal = new CorsConfiguration();
|
||||||
|
|
||||||
|
// *********************************************************
|
||||||
|
// Update constructor documentation if you change these:
|
||||||
|
// *********************************************************
|
||||||
|
|
||||||
|
retVal.addAllowedHeader("Origin");
|
||||||
|
retVal.addAllowedHeader("Accept");
|
||||||
|
retVal.addAllowedHeader("X-Requested-With");
|
||||||
|
retVal.addAllowedHeader("Content-Type");
|
||||||
|
retVal.addAllowedHeader("Access-Control-Request-Method");
|
||||||
|
retVal.addAllowedHeader("Access-Control-Request-Headers");
|
||||||
|
retVal.addAllowedOrigin("*");
|
||||||
|
retVal.addExposedHeader("Location");
|
||||||
|
retVal.addExposedHeader("Content-Location");
|
||||||
|
|
||||||
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor which accepts the given configuration
|
||||||
*
|
*
|
||||||
* @param theConfiguration
|
* @param theConfiguration
|
||||||
* The CORS configuration
|
* The CORS configuration
|
||||||
|
|
|
@ -1,15 +1,17 @@
|
||||||
package ca.uhn.fhir.rest.server;
|
package ca.uhn.fhir.rest.server.interceptor;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.hamcrest.Matchers.contains;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.EnumSet;
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import javax.servlet.DispatcherType;
|
|
||||||
|
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.apache.http.Header;
|
import org.apache.http.Header;
|
||||||
import org.apache.http.HttpResponse;
|
import org.apache.http.HttpResponse;
|
||||||
|
@ -23,60 +25,41 @@ import org.apache.http.impl.client.HttpClientBuilder;
|
||||||
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
||||||
import org.eclipse.jetty.server.Server;
|
import org.eclipse.jetty.server.Server;
|
||||||
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
|
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
|
||||||
import org.eclipse.jetty.servlet.FilterHolder;
|
|
||||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||||
import org.eclipse.jetty.servlet.ServletHolder;
|
import org.eclipse.jetty.servlet.ServletHolder;
|
||||||
|
import org.hl7.fhir.dstu3.model.Bundle;
|
||||||
|
import org.hl7.fhir.dstu3.model.Enumerations.AdministrativeGender;
|
||||||
|
import org.hl7.fhir.dstu3.model.HumanName;
|
||||||
|
import org.hl7.fhir.dstu3.model.IdType;
|
||||||
|
import org.hl7.fhir.dstu3.model.Identifier;
|
||||||
|
import org.hl7.fhir.dstu3.model.Identifier.IdentifierUse;
|
||||||
|
import org.hl7.fhir.dstu3.model.Patient;
|
||||||
import org.junit.AfterClass;
|
import org.junit.AfterClass;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.springframework.web.cors.CorsConfiguration;
|
import org.springframework.web.cors.CorsConfiguration;
|
||||||
import org.springframework.web.filter.CorsFilter;
|
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.model.api.Bundle;
|
import ca.uhn.fhir.rest.annotation.Create;
|
||||||
import ca.uhn.fhir.model.dstu.resource.Patient;
|
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||||
import ca.uhn.fhir.rest.server.RestfulServerSelfReferenceTest.DummyPatientResourceProvider;
|
import ca.uhn.fhir.rest.annotation.Read;
|
||||||
import ca.uhn.fhir.rest.server.interceptor.CorsInterceptor;
|
import ca.uhn.fhir.rest.annotation.RequiredParam;
|
||||||
|
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Search;
|
||||||
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
|
import ca.uhn.fhir.rest.param.TokenParam;
|
||||||
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
|
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||||
|
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||||
import ca.uhn.fhir.util.PortUtil;
|
import ca.uhn.fhir.util.PortUtil;
|
||||||
import ca.uhn.fhir.util.TestUtil;
|
import ca.uhn.fhir.util.TestUtil;
|
||||||
|
|
||||||
public class CorsTest {
|
public class CorsInterceptorDstu3Test {
|
||||||
private static CloseableHttpClient ourClient;
|
|
||||||
private static Server ourServer;
|
|
||||||
private static String ourBaseUri;
|
private static String ourBaseUri;
|
||||||
private static FhirContext ourCtx = FhirContext.forDstu1();
|
private static CloseableHttpClient ourClient;
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CorsTest.class);
|
private static FhirContext ourCtx = FhirContext.forDstu3();
|
||||||
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CorsInterceptorDstu3Test.class);
|
||||||
@Test
|
private static Server ourServer;
|
||||||
public void testRequestWithNullOrigin() throws ClientProtocolException, IOException {
|
|
||||||
{
|
|
||||||
HttpOptions httpOpt = new HttpOptions(ourBaseUri + "/Organization/b27ed191-f62d-4128-d99d-40b5e84f2bf2");
|
|
||||||
httpOpt.addHeader("Access-Control-Request-Method", "GET");
|
|
||||||
httpOpt.addHeader("Origin", "null");
|
|
||||||
httpOpt.addHeader("Access-Control-Request-Headers", "accept, x-fhir-starter, content-type");
|
|
||||||
HttpResponse status = ourClient.execute(httpOpt);
|
|
||||||
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
|
|
||||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
|
||||||
ourLog.info("Response was:\n{}", responseContent);
|
|
||||||
assertEquals("GET,POST,PUT,DELETE,OPTIONS", status.getFirstHeader(Constants.HEADER_CORS_ALLOW_METHODS).getValue());
|
|
||||||
assertEquals("null", status.getFirstHeader(Constants.HEADER_CORS_ALLOW_ORIGIN).getValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testRequestWithInvalidOrigin() throws ClientProtocolException, IOException {
|
|
||||||
{
|
|
||||||
HttpOptions httpOpt = new HttpOptions(ourBaseUri + "/Organization/b27ed191-f62d-4128-d99d-40b5e84f2bf2");
|
|
||||||
httpOpt.addHeader("Access-Control-Request-Method", "GET");
|
|
||||||
httpOpt.addHeader("Origin", "http://yahoo.com");
|
|
||||||
httpOpt.addHeader("Access-Control-Request-Headers", "accept, x-fhir-starter, content-type");
|
|
||||||
HttpResponse status = ourClient.execute(httpOpt);
|
|
||||||
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
|
|
||||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
|
||||||
ourLog.info("Response was:\n{}", responseContent);
|
|
||||||
assertEquals(403, status.getStatusLine().getStatusCode());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testContextWithSpace() throws Exception {
|
public void testContextWithSpace() throws Exception {
|
||||||
|
@ -107,9 +90,9 @@ public class CorsTest {
|
||||||
ourLog.info("Response was:\n{}", responseContent);
|
ourLog.info("Response was:\n{}", responseContent);
|
||||||
|
|
||||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||||
Bundle bundle = ourCtx.newXmlParser().parseBundle(responseContent);
|
Bundle bundle = ourCtx.newXmlParser().parseResource(Bundle.class, responseContent);
|
||||||
|
|
||||||
assertEquals(1, bundle.getEntries().size());
|
assertEquals(1, bundle.getEntry().size());
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
HttpPost httpOpt = new HttpPost(ourBaseUri + "/Patient");
|
HttpPost httpOpt = new HttpPost(ourBaseUri + "/Patient");
|
||||||
|
@ -126,11 +109,6 @@ public class CorsTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void afterClass() throws Exception {
|
|
||||||
ourServer.stop();
|
|
||||||
ourClient.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCorsConfigMethods() {
|
public void testCorsConfigMethods() {
|
||||||
CorsInterceptor corsInterceptor = new CorsInterceptor();
|
CorsInterceptor corsInterceptor = new CorsInterceptor();
|
||||||
|
@ -138,6 +116,54 @@ public class CorsTest {
|
||||||
corsInterceptor.setConfig(new CorsConfiguration());
|
corsInterceptor.setConfig(new CorsConfiguration());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDefaultConfig() {
|
||||||
|
CorsInterceptor def = new CorsInterceptor();
|
||||||
|
assertThat(def.getConfig().getAllowedOrigins(), contains("*"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRequestWithInvalidOrigin() throws ClientProtocolException, IOException {
|
||||||
|
{
|
||||||
|
HttpOptions httpOpt = new HttpOptions(ourBaseUri + "/Organization/b27ed191-f62d-4128-d99d-40b5e84f2bf2");
|
||||||
|
httpOpt.addHeader("Access-Control-Request-Method", "GET");
|
||||||
|
httpOpt.addHeader("Origin", "http://yahoo.com");
|
||||||
|
httpOpt.addHeader("Access-Control-Request-Headers", "accept, x-fhir-starter, content-type");
|
||||||
|
HttpResponse status = ourClient.execute(httpOpt);
|
||||||
|
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
ourLog.info("Response was:\n{}", responseContent);
|
||||||
|
assertEquals(403, status.getStatusLine().getStatusCode());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRequestWithNullOrigin() throws ClientProtocolException, IOException {
|
||||||
|
{
|
||||||
|
HttpOptions httpOpt = new HttpOptions(ourBaseUri + "/Organization/b27ed191-f62d-4128-d99d-40b5e84f2bf2");
|
||||||
|
httpOpt.addHeader("Access-Control-Request-Method", "GET");
|
||||||
|
httpOpt.addHeader("Origin", "null");
|
||||||
|
httpOpt.addHeader("Access-Control-Request-Headers", "accept, x-fhir-starter, content-type");
|
||||||
|
HttpResponse status = ourClient.execute(httpOpt);
|
||||||
|
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
ourLog.info("Response was:\n{}", responseContent);
|
||||||
|
assertEquals("GET,POST,PUT,DELETE,OPTIONS", status.getFirstHeader(Constants.HEADER_CORS_ALLOW_METHODS).getValue());
|
||||||
|
assertEquals("null", status.getFirstHeader(Constants.HEADER_CORS_ALLOW_ORIGIN).getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void afterClass() throws Exception {
|
||||||
|
ourServer.stop();
|
||||||
|
ourClient.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void afterClassClearContext() {
|
||||||
|
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@BeforeClass
|
@BeforeClass
|
||||||
public static void beforeClass() throws Exception {
|
public static void beforeClass() throws Exception {
|
||||||
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
|
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
|
||||||
|
@ -151,7 +177,6 @@ public class CorsTest {
|
||||||
RestfulServer restServer = new RestfulServer(ourCtx);
|
RestfulServer restServer = new RestfulServer(ourCtx);
|
||||||
restServer.setResourceProviders(new DummyPatientResourceProvider());
|
restServer.setResourceProviders(new DummyPatientResourceProvider());
|
||||||
|
|
||||||
// ServletHandler proxyHandler = new ServletHandler();
|
|
||||||
ServletHolder servletHolder = new ServletHolder(restServer);
|
ServletHolder servletHolder = new ServletHolder(restServer);
|
||||||
|
|
||||||
CorsConfiguration config = new CorsConfiguration();
|
CorsConfiguration config = new CorsConfiguration();
|
||||||
|
@ -184,10 +209,77 @@ public class CorsTest {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by dsotnikov on 2/25/2014.
|
||||||
|
*/
|
||||||
|
public static class DummyPatientResourceProvider implements IResourceProvider {
|
||||||
|
|
||||||
|
@Create
|
||||||
|
public MethodOutcome create(@ResourceParam Patient thePatient) {
|
||||||
|
return new MethodOutcome(thePatient.getIdElement());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, Patient> getIdToPatient() {
|
||||||
|
Map<String, Patient> idToPatient = new HashMap<String, Patient>();
|
||||||
|
{
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.setId("1");
|
||||||
|
patient.addIdentifier();
|
||||||
|
patient.getIdentifier().get(0).setUse(IdentifierUse.OFFICIAL);
|
||||||
|
patient.getIdentifier().get(0).setSystem(("urn:hapitest:mrns"));
|
||||||
|
patient.getIdentifier().get(0).setValue("00001");
|
||||||
|
patient.addName();
|
||||||
|
patient.getName().get(0).addFamily("Test");
|
||||||
|
patient.getName().get(0).addGiven("PatientOne");
|
||||||
|
patient.setGender(AdministrativeGender.MALE);
|
||||||
|
idToPatient.put("1", patient);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.setId("2");
|
||||||
|
patient.getIdentifier().add(new Identifier());
|
||||||
|
patient.getIdentifier().get(0).setUse(IdentifierUse.OFFICIAL);
|
||||||
|
patient.getIdentifier().get(0).setSystem(("urn:hapitest:mrns"));
|
||||||
|
patient.getIdentifier().get(0).setValue("00002");
|
||||||
|
patient.getName().add(new HumanName());
|
||||||
|
patient.getName().get(0).addFamily("Test");
|
||||||
|
patient.getName().get(0).addGiven("PatientTwo");
|
||||||
|
patient.setGender(AdministrativeGender.FEMALE);
|
||||||
|
idToPatient.put("2", patient);
|
||||||
|
}
|
||||||
|
return idToPatient;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Search()
|
||||||
|
public Patient getPatient(@RequiredParam(name = Patient.SP_IDENTIFIER) TokenParam theIdentifier) {
|
||||||
|
for (Patient next : getIdToPatient().values()) {
|
||||||
|
for (Identifier nextId : next.getIdentifier()) {
|
||||||
|
if (nextId.getSystem().equals(theIdentifier.getSystem())&& nextId.getValue().equals(theIdentifier.getValue())) {
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the resource by its identifier
|
||||||
|
*
|
||||||
|
* @param theId
|
||||||
|
* The resource identity
|
||||||
|
* @return The resource
|
||||||
|
*/
|
||||||
|
@Read()
|
||||||
|
public Patient getResourceById(@IdParam IdType theId) {
|
||||||
|
return getIdToPatient().get(theId.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<Patient> getResourceType() {
|
||||||
|
return Patient.class;
|
||||||
|
}
|
||||||
|
|
||||||
@AfterClass
|
|
||||||
public static void afterClassClearContext() {
|
|
||||||
TestUtil.clearAllStaticFieldsForUnitTest();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
Loading…
Reference in New Issue