From d2c3cb49482d53e3e3c633c7160a46467d16df1d Mon Sep 17 00:00:00 2001 From: Mark Lowe Date: Thu, 25 Apr 2019 14:05:47 +0100 Subject: [PATCH 1/7] Use prematching for logging filter only. Limit domain response filtering only to 3rd party server only. --- .../com/quorum/tessera/api/filter/DomainResponseFilter.java | 2 -- .../java/com/quorum/tessera/api/filter/LoggingFilter.java | 3 ++- .../main/java/com/quorum/tessera/p2p/TransactionResource.java | 2 -- .../main/java/com/quorum/tessera/q2t/TransactionResource.java | 4 ---- .../com/quorum/tessera/thridparty/RawTransactionResource.java | 2 -- 5 files changed, 2 insertions(+), 11 deletions(-) diff --git a/jaxrs-service/src/main/java/com/quorum/tessera/api/filter/DomainResponseFilter.java b/jaxrs-service/src/main/java/com/quorum/tessera/api/filter/DomainResponseFilter.java index 0cfcf3cddf..8f3a139fbd 100644 --- a/jaxrs-service/src/main/java/com/quorum/tessera/api/filter/DomainResponseFilter.java +++ b/jaxrs-service/src/main/java/com/quorum/tessera/api/filter/DomainResponseFilter.java @@ -5,11 +5,9 @@ import javax.ws.rs.container.ContainerRequestContext; import javax.ws.rs.container.ContainerResponseContext; import javax.ws.rs.container.ContainerResponseFilter; -import javax.ws.rs.container.PreMatching; import javax.ws.rs.core.MultivaluedMap; @DomainFilter -@PreMatching public class DomainResponseFilter implements ContainerResponseFilter { @Override diff --git a/jaxrs-service/src/main/java/com/quorum/tessera/api/filter/LoggingFilter.java b/jaxrs-service/src/main/java/com/quorum/tessera/api/filter/LoggingFilter.java index c9c6b28d5c..38cae69798 100644 --- a/jaxrs-service/src/main/java/com/quorum/tessera/api/filter/LoggingFilter.java +++ b/jaxrs-service/src/main/java/com/quorum/tessera/api/filter/LoggingFilter.java @@ -9,11 +9,12 @@ import javax.ws.rs.container.ContainerResponseContext; import javax.ws.rs.container.ContainerResponseFilter; import javax.ws.rs.core.UriInfo; - +import javax.ws.rs.container.PreMatching; /* https://docs.oracle.com/javaee/7/api/javax/ws/rs/NameBinding.html */ @Logged +@PreMatching public class LoggingFilter implements ContainerRequestFilter, ContainerResponseFilter { private static final Logger LOGGER = LoggerFactory.getLogger(LoggingFilter.class); diff --git a/jaxrs-service/src/main/java/com/quorum/tessera/p2p/TransactionResource.java b/jaxrs-service/src/main/java/com/quorum/tessera/p2p/TransactionResource.java index d7e29c2ed4..128e1829ab 100644 --- a/jaxrs-service/src/main/java/com/quorum/tessera/p2p/TransactionResource.java +++ b/jaxrs-service/src/main/java/com/quorum/tessera/p2p/TransactionResource.java @@ -1,6 +1,5 @@ package com.quorum.tessera.p2p; -import com.quorum.tessera.api.filter.Logged; import com.quorum.tessera.api.model.ResendRequest; import com.quorum.tessera.api.model.ResendResponse; import com.quorum.tessera.enclave.model.MessageHash; @@ -30,7 +29,6 @@ * - creating new transactions and distributing them - deleting transactions - * fetching transactions - resending old transactions */ -@Logged @Path("/") public class TransactionResource { diff --git a/jaxrs-service/src/main/java/com/quorum/tessera/q2t/TransactionResource.java b/jaxrs-service/src/main/java/com/quorum/tessera/q2t/TransactionResource.java index d60a08354e..b3786d7da4 100644 --- a/jaxrs-service/src/main/java/com/quorum/tessera/q2t/TransactionResource.java +++ b/jaxrs-service/src/main/java/com/quorum/tessera/q2t/TransactionResource.java @@ -1,7 +1,5 @@ package com.quorum.tessera.q2t; -import com.quorum.tessera.api.filter.DomainFilter; -import com.quorum.tessera.api.filter.Logged; import com.quorum.tessera.api.model.*; import com.quorum.tessera.transaction.TransactionManager; import io.swagger.annotations.ApiOperation; @@ -33,8 +31,6 @@ * - creating new transactions and distributing them - deleting transactions - * fetching transactions - resending old transactions */ -@DomainFilter -@Logged @Path("/") public class TransactionResource { diff --git a/jaxrs-service/src/main/java/com/quorum/tessera/thridparty/RawTransactionResource.java b/jaxrs-service/src/main/java/com/quorum/tessera/thridparty/RawTransactionResource.java index 8359a8e419..b5a32d27e7 100644 --- a/jaxrs-service/src/main/java/com/quorum/tessera/thridparty/RawTransactionResource.java +++ b/jaxrs-service/src/main/java/com/quorum/tessera/thridparty/RawTransactionResource.java @@ -1,7 +1,6 @@ package com.quorum.tessera.thridparty; import com.quorum.tessera.api.filter.DomainFilter; -import com.quorum.tessera.api.filter.Logged; import com.quorum.tessera.api.model.*; import com.quorum.tessera.config.apps.ThirdPartyApp; import com.quorum.tessera.transaction.TransactionManager; @@ -25,7 +24,6 @@ * Provides endpoints for dealing with raw transactions */ @DomainFilter -@Logged @Path("/") public class RawTransactionResource implements ThirdPartyApp { From 2da85958524c1699909b1b8ab5db8679a4b3cebf Mon Sep 17 00:00:00 2001 From: Mark Lowe Date: Thu, 25 Apr 2019 17:13:00 +0100 Subject: [PATCH 2/7] Move commonly used filters into server module and create when setting up jersey. Add CORS filter but still pending confirmation on matching logic. --- .../tessera/config/cli/OverrideUtilTest.java | 1 + .../quorum/tessera/config/ServerConfig.java | 12 +++++++++ .../tessera/api/filter/DomainFilter.java | 14 ---------- .../thridparty/RawTransactionResource.java | 2 -- .../main/resources/tessera-jaxrs-spring.xml | 3 --- .../quorum/tessera/server/JerseyServer.java | 26 ++++++++++++------- .../jaxrs/CorsDomainResponseFilter.java | 25 +++++++++++++----- .../tessera/server/jaxrs}/LoggingFilter.java | 4 +-- .../jaxrs/CorsDomainResponseFilterTest.java | 9 ++++--- .../tessera/server/jaxrs/JerseyServerIT.java | 2 ++ .../server/jaxrs}/LoggingFilterTest.java | 3 +-- 11 files changed, 56 insertions(+), 45 deletions(-) delete mode 100644 jaxrs-service/src/main/java/com/quorum/tessera/api/filter/DomainFilter.java rename jaxrs-service/src/main/java/com/quorum/tessera/api/filter/DomainResponseFilter.java => server/jersey-server/src/main/java/com/quorum/tessera/server/jaxrs/CorsDomainResponseFilter.java (65%) rename {jaxrs-service/src/main/java/com/quorum/tessera/api/filter => server/jersey-server/src/main/java/com/quorum/tessera/server/jaxrs}/LoggingFilter.java (93%) rename jaxrs-service/src/test/java/com/quorum/tessera/api/filter/DomainResponseFilterTest.java => server/jersey-server/src/test/java/com/quorum/tessera/server/jaxrs/CorsDomainResponseFilterTest.java (93%) rename {jaxrs-service/src/test/java/com/quorum/tessera/api/filter => server/jersey-server/src/test/java/com/quorum/tessera/server/jaxrs}/LoggingFilterTest.java (93%) diff --git a/config-cli/src/test/java/com/quorum/tessera/config/cli/OverrideUtilTest.java b/config-cli/src/test/java/com/quorum/tessera/config/cli/OverrideUtilTest.java index 616fb3b255..b67a351475 100644 --- a/config-cli/src/test/java/com/quorum/tessera/config/cli/OverrideUtilTest.java +++ b/config-cli/src/test/java/com/quorum/tessera/config/cli/OverrideUtilTest.java @@ -54,6 +54,7 @@ public void buildOptions() { "useWhiteList", "disablePeerDiscovery", "serverConfigs.serverAddress", + "serverConfigs.corsDomains", "serverConfigs.sslConfig.serverTrustStore", "serverConfigs.influxConfig.dbName", "serverConfigs.sslConfig.knownClientsFile", diff --git a/config/src/main/java/com/quorum/tessera/config/ServerConfig.java b/config/src/main/java/com/quorum/tessera/config/ServerConfig.java index 92cfa268fb..a883a5470a 100644 --- a/config/src/main/java/com/quorum/tessera/config/ServerConfig.java +++ b/config/src/main/java/com/quorum/tessera/config/ServerConfig.java @@ -10,6 +10,7 @@ import javax.xml.bind.annotation.XmlElement; import java.net.URI; import java.net.URISyntaxException; +import java.util.List; import java.util.Objects; @XmlAccessorType(XmlAccessType.FIELD) @@ -48,6 +49,9 @@ public class ServerConfig extends ConfigItem { @XmlElement private String serverAddress; + @XmlElement + private List corsDomains; + public ServerConfig(final AppType app, final boolean enabled, final String serverAddress, @@ -148,4 +152,12 @@ public boolean isUnixSocket() { return Objects.equals(getServerUri().getScheme(), "unix"); } + public List getCorsDomains() { + return corsDomains; + } + + public void setCorsDomains(List corsDomains) { + this.corsDomains = corsDomains; + } + } diff --git a/jaxrs-service/src/main/java/com/quorum/tessera/api/filter/DomainFilter.java b/jaxrs-service/src/main/java/com/quorum/tessera/api/filter/DomainFilter.java deleted file mode 100644 index a32755f38e..0000000000 --- a/jaxrs-service/src/main/java/com/quorum/tessera/api/filter/DomainFilter.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.quorum.tessera.api.filter; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import javax.ws.rs.NameBinding; - -@Target({ElementType.TYPE, ElementType.METHOD}) -@Retention(value = RetentionPolicy.RUNTIME) -@NameBinding -public @interface DomainFilter { - -} diff --git a/jaxrs-service/src/main/java/com/quorum/tessera/thridparty/RawTransactionResource.java b/jaxrs-service/src/main/java/com/quorum/tessera/thridparty/RawTransactionResource.java index b5a32d27e7..5512a367de 100644 --- a/jaxrs-service/src/main/java/com/quorum/tessera/thridparty/RawTransactionResource.java +++ b/jaxrs-service/src/main/java/com/quorum/tessera/thridparty/RawTransactionResource.java @@ -1,6 +1,5 @@ package com.quorum.tessera.thridparty; -import com.quorum.tessera.api.filter.DomainFilter; import com.quorum.tessera.api.model.*; import com.quorum.tessera.config.apps.ThirdPartyApp; import com.quorum.tessera.transaction.TransactionManager; @@ -23,7 +22,6 @@ /** * Provides endpoints for dealing with raw transactions */ -@DomainFilter @Path("/") public class RawTransactionResource implements ThirdPartyApp { diff --git a/jaxrs-service/src/main/resources/tessera-jaxrs-spring.xml b/jaxrs-service/src/main/resources/tessera-jaxrs-spring.xml index c5d4a9cf65..26aa39dc31 100644 --- a/jaxrs-service/src/main/resources/tessera-jaxrs-spring.xml +++ b/jaxrs-service/src/main/resources/tessera-jaxrs-spring.xml @@ -77,14 +77,11 @@ - - - diff --git a/server/jersey-server/src/main/java/com/quorum/tessera/server/JerseyServer.java b/server/jersey-server/src/main/java/com/quorum/tessera/server/JerseyServer.java index df8bf328ca..d9daeb3bc5 100644 --- a/server/jersey-server/src/main/java/com/quorum/tessera/server/JerseyServer.java +++ b/server/jersey-server/src/main/java/com/quorum/tessera/server/JerseyServer.java @@ -3,25 +3,26 @@ import com.jpmorgan.quorum.server.utils.ServerUtils; import com.quorum.tessera.config.InfluxConfig; import com.quorum.tessera.config.ServerConfig; +import com.quorum.tessera.server.jaxrs.CorsDomainResponseFilter; +import com.quorum.tessera.server.jaxrs.LoggingFilter; import com.quorum.tessera.server.monitoring.InfluxDbClient; import com.quorum.tessera.server.monitoring.InfluxDbPublisher; import com.quorum.tessera.server.monitoring.MetricsResource; -import org.glassfish.jersey.server.ResourceConfig; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.slf4j.bridge.SLF4JBridgeHandler; -import javax.ws.rs.core.Application; import java.net.URI; import java.util.HashMap; import java.util.Map; import java.util.Objects; +import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; - -import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor; +import javax.ws.rs.core.Application; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; +import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.servlet.ServletContainer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.bridge.SLF4JBridgeHandler; /** * Implementation of a RestServer using Jersey and Jetty. @@ -76,7 +77,12 @@ public void start() throws Exception { final ResourceConfig config = ResourceConfig.forApplication(application); config.addProperties(initParams) - .register(MetricsResource.class); + .register(MetricsResource.class) + .register(LoggingFilter.class); + + if (serverConfig.getCorsDomains() != null) { + config.register(new CorsDomainResponseFilter(serverConfig.getCorsDomains())); + } this.server = ServerUtils.buildWebServer(serverConfig); @@ -104,7 +110,7 @@ private void startInfluxMonitoring() { Runnable publisher = new InfluxDbPublisher(influxDbClient); final Runnable exceptionSafePublisher = () -> { - try{ + try { publisher.run(); } catch (final Throwable ex) { LOGGER.error("Error when executing action {}", publisher.getClass().getSimpleName()); @@ -125,7 +131,7 @@ public void stop() { } if (Objects.nonNull(this.server)) { - try{ + try { this.server.stop(); } catch (Exception ex) { LOGGER.warn(null, ex); diff --git a/jaxrs-service/src/main/java/com/quorum/tessera/api/filter/DomainResponseFilter.java b/server/jersey-server/src/main/java/com/quorum/tessera/server/jaxrs/CorsDomainResponseFilter.java similarity index 65% rename from jaxrs-service/src/main/java/com/quorum/tessera/api/filter/DomainResponseFilter.java rename to server/jersey-server/src/main/java/com/quorum/tessera/server/jaxrs/CorsDomainResponseFilter.java index 3746ffcba1..e859362618 100644 --- a/jaxrs-service/src/main/java/com/quorum/tessera/api/filter/DomainResponseFilter.java +++ b/server/jersey-server/src/main/java/com/quorum/tessera/server/jaxrs/CorsDomainResponseFilter.java @@ -1,17 +1,28 @@ -package com.quorum.tessera.api.filter; +package com.quorum.tessera.server.jaxrs; import java.io.IOException; +import java.util.List; import java.util.Objects; import javax.ws.rs.container.ContainerRequestContext; import javax.ws.rs.container.ContainerResponseContext; import javax.ws.rs.container.ContainerResponseFilter; import javax.ws.rs.core.MultivaluedMap; -/* -https://docs.oracle.com/javaee/7/api/javax/ws/rs/NameBinding.html - */ -@DomainFilter -public class DomainResponseFilter implements ContainerResponseFilter { +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class CorsDomainResponseFilter implements ContainerResponseFilter { + + private static final Logger LOGGER = LoggerFactory.getLogger(CorsDomainResponseFilter.class); + + private final List tokens; + + public CorsDomainResponseFilter(List tokens) { + this.tokens = Objects.requireNonNull(tokens); + LOGGER.info("Create filter with tokens {}",String.join(",", tokens)); + } + + @Override public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException { @@ -28,7 +39,7 @@ public void filter(ContainerRequestContext requestContext, ContainerResponseCont headers.add("Access-Control-Allow-Headers", requestContext.getHeaderString("Access-Control-Request-Headers")); } - } + } diff --git a/jaxrs-service/src/main/java/com/quorum/tessera/api/filter/LoggingFilter.java b/server/jersey-server/src/main/java/com/quorum/tessera/server/jaxrs/LoggingFilter.java similarity index 93% rename from jaxrs-service/src/main/java/com/quorum/tessera/api/filter/LoggingFilter.java rename to server/jersey-server/src/main/java/com/quorum/tessera/server/jaxrs/LoggingFilter.java index 6bedb9d1e5..5e8c04fd60 100644 --- a/jaxrs-service/src/main/java/com/quorum/tessera/api/filter/LoggingFilter.java +++ b/server/jersey-server/src/main/java/com/quorum/tessera/server/jaxrs/LoggingFilter.java @@ -1,4 +1,4 @@ -package com.quorum.tessera.api.filter; +package com.quorum.tessera.server.jaxrs; import java.util.Optional; import org.slf4j.Logger; @@ -9,9 +9,7 @@ import javax.ws.rs.container.ContainerResponseContext; import javax.ws.rs.container.ContainerResponseFilter; import javax.ws.rs.core.UriInfo; -import javax.ws.rs.container.PreMatching; -@PreMatching public class LoggingFilter implements ContainerRequestFilter, ContainerResponseFilter { private static final Logger LOGGER = LoggerFactory.getLogger(LoggingFilter.class); diff --git a/jaxrs-service/src/test/java/com/quorum/tessera/api/filter/DomainResponseFilterTest.java b/server/jersey-server/src/test/java/com/quorum/tessera/server/jaxrs/CorsDomainResponseFilterTest.java similarity index 93% rename from jaxrs-service/src/test/java/com/quorum/tessera/api/filter/DomainResponseFilterTest.java rename to server/jersey-server/src/test/java/com/quorum/tessera/server/jaxrs/CorsDomainResponseFilterTest.java index db19e0e261..a3bb86036f 100644 --- a/jaxrs-service/src/test/java/com/quorum/tessera/api/filter/DomainResponseFilterTest.java +++ b/server/jersey-server/src/test/java/com/quorum/tessera/server/jaxrs/CorsDomainResponseFilterTest.java @@ -1,6 +1,7 @@ -package com.quorum.tessera.api.filter; +package com.quorum.tessera.server.jaxrs; import java.net.URI; +import java.util.Collections; import javax.ws.rs.container.ContainerRequestContext; import javax.ws.rs.container.ContainerResponseContext; import javax.ws.rs.core.MultivaluedHashMap; @@ -15,9 +16,9 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; -public class DomainResponseFilterTest { +public class CorsDomainResponseFilterTest { - private DomainResponseFilter domainResponseFilter; + private CorsDomainResponseFilter domainResponseFilter; private ContainerRequestContext requestContext; @@ -27,7 +28,7 @@ public class DomainResponseFilterTest { @Before public void setUp() { - domainResponseFilter = new DomainResponseFilter(); + domainResponseFilter = new CorsDomainResponseFilter(Collections.EMPTY_LIST); requestContext = mock(ContainerRequestContext.class); responseContext = mock(ContainerResponseContext.class); uriInfo = mock(UriInfo.class); diff --git a/server/jersey-server/src/test/java/com/quorum/tessera/server/jaxrs/JerseyServerIT.java b/server/jersey-server/src/test/java/com/quorum/tessera/server/jaxrs/JerseyServerIT.java index 5a3cfe03b8..c5964cf777 100644 --- a/server/jersey-server/src/test/java/com/quorum/tessera/server/jaxrs/JerseyServerIT.java +++ b/server/jersey-server/src/test/java/com/quorum/tessera/server/jaxrs/JerseyServerIT.java @@ -5,6 +5,7 @@ import com.quorum.tessera.config.ServerConfig; import com.quorum.tessera.server.JerseyServer; import java.net.URI; +import java.util.Arrays; import javax.ws.rs.client.ClientBuilder; import javax.ws.rs.client.Entity; import javax.ws.rs.core.Application; @@ -26,6 +27,7 @@ public void onSetUp() throws Exception { ServerConfig serverConfig = new ServerConfig(); serverConfig.setCommunicationType(CommunicationType.REST); serverConfig.setServerAddress("http://localhost:8080"); + serverConfig.setCorsDomains(Arrays.asList("*.acme.com")); Application sample = new SampleApplication(); server = new JerseyServer(serverConfig, sample); diff --git a/jaxrs-service/src/test/java/com/quorum/tessera/api/filter/LoggingFilterTest.java b/server/jersey-server/src/test/java/com/quorum/tessera/server/jaxrs/LoggingFilterTest.java similarity index 93% rename from jaxrs-service/src/test/java/com/quorum/tessera/api/filter/LoggingFilterTest.java rename to server/jersey-server/src/test/java/com/quorum/tessera/server/jaxrs/LoggingFilterTest.java index 6b8c1e38c9..610a2076a5 100644 --- a/jaxrs-service/src/test/java/com/quorum/tessera/api/filter/LoggingFilterTest.java +++ b/server/jersey-server/src/test/java/com/quorum/tessera/server/jaxrs/LoggingFilterTest.java @@ -1,6 +1,5 @@ -package com.quorum.tessera.api.filter; +package com.quorum.tessera.server.jaxrs; -import com.quorum.tessera.api.filter.LoggingFilter; import org.junit.After; import org.junit.Before; import org.junit.Test; From 5341f42b90c16f702fb9464625623f43f07049c2 Mon Sep 17 00:00:00 2001 From: Mark Lowe Date: Thu, 25 Apr 2019 18:55:32 +0100 Subject: [PATCH 3/7] Add some headers in tests in lieu of some confirmation with requirements --- .../jaxrs/CorsDomainResponseFilter.java | 17 +++--- .../jaxrs/CorsDomainResponseFilterTest.java | 7 +-- .../tessera/server/jaxrs/JerseyServerIT.java | 52 +++++++++++++------ 3 files changed, 50 insertions(+), 26 deletions(-) diff --git a/server/jersey-server/src/main/java/com/quorum/tessera/server/jaxrs/CorsDomainResponseFilter.java b/server/jersey-server/src/main/java/com/quorum/tessera/server/jaxrs/CorsDomainResponseFilter.java index e859362618..278b21a36d 100644 --- a/server/jersey-server/src/main/java/com/quorum/tessera/server/jaxrs/CorsDomainResponseFilter.java +++ b/server/jersey-server/src/main/java/com/quorum/tessera/server/jaxrs/CorsDomainResponseFilter.java @@ -10,19 +10,17 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; - public class CorsDomainResponseFilter implements ContainerResponseFilter { - + private static final Logger LOGGER = LoggerFactory.getLogger(CorsDomainResponseFilter.class); - + private final List tokens; public CorsDomainResponseFilter(List tokens) { this.tokens = Objects.requireNonNull(tokens); - LOGGER.info("Create filter with tokens {}",String.join(",", tokens)); + LOGGER.info("Create filter with tokens {}", String.join(",", tokens)); } - @Override public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException { @@ -32,14 +30,17 @@ public void filter(ContainerRequestContext requestContext, ContainerResponseCont final String origin = requestContext.getHeaderString("Origin"); + LOGGER.info("Origin header value {}", origin); + if (Objects.nonNull(origin) && !Objects.equals(origin, "")) { + MultivaluedMap headers = responseContext.getHeaders(); + headers.add("Access-Control-Allow-Origin", origin); headers.add("Access-Control-Allow-Credentials", "true"); - headers.add("Access-Control-Allow-Headers", - requestContext.getHeaderString("Access-Control-Request-Headers")); + headers.add("Access-Control-Request-Headers", requestContext.getHeaderString("Access-Control-Request-Headers")); + } } - } diff --git a/server/jersey-server/src/test/java/com/quorum/tessera/server/jaxrs/CorsDomainResponseFilterTest.java b/server/jersey-server/src/test/java/com/quorum/tessera/server/jaxrs/CorsDomainResponseFilterTest.java index a3bb86036f..1ac1e453c8 100644 --- a/server/jersey-server/src/test/java/com/quorum/tessera/server/jaxrs/CorsDomainResponseFilterTest.java +++ b/server/jersey-server/src/test/java/com/quorum/tessera/server/jaxrs/CorsDomainResponseFilterTest.java @@ -69,18 +69,19 @@ public void filter() throws Exception { when(requestContext.getHeaderString("Origin")).thenReturn("bogus.com"); when(requestContext.getHeaderString("Access-Control-Request-Headers")) - .thenReturn("Some Headers"); + .thenReturn("SomeHeaders"); domainResponseFilter.filter(requestContext, responseContext); assertThat(headers) .containsKeys( "Access-Control-Allow-Origin", - "Access-Control-Allow-Credentials", "Access-Control-Allow-Headers"); + "Access-Control-Allow-Credentials", + "Access-Control-Request-Headers"); assertThat(headers.get("Access-Control-Allow-Origin")).containsExactly("bogus.com"); assertThat(headers.get("Access-Control-Allow-Credentials")).containsExactly("true"); - assertThat(headers.get("Access-Control-Allow-Headers")).containsExactly("Some Headers"); + assertThat(headers.get("Access-Control-Request-Headers")).containsExactly("SomeHeaders"); verify(requestContext).getUriInfo(); verify(requestContext).getHeaderString("Origin"); diff --git a/server/jersey-server/src/test/java/com/quorum/tessera/server/jaxrs/JerseyServerIT.java b/server/jersey-server/src/test/java/com/quorum/tessera/server/jaxrs/JerseyServerIT.java index c5964cf777..dc4275c8bd 100644 --- a/server/jersey-server/src/test/java/com/quorum/tessera/server/jaxrs/JerseyServerIT.java +++ b/server/jersey-server/src/test/java/com/quorum/tessera/server/jaxrs/JerseyServerIT.java @@ -3,6 +3,7 @@ import com.quorum.tessera.config.CommunicationType; import com.quorum.tessera.config.ServerConfig; +import com.quorum.tessera.config.util.JaxbUtil; import com.quorum.tessera.server.JerseyServer; import java.net.URI; import java.util.Arrays; @@ -10,6 +11,8 @@ import javax.ws.rs.client.Entity; import javax.ws.rs.core.Application; import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedHashMap; +import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; import static org.assertj.core.api.Assertions.assertThat; import org.junit.After; @@ -24,10 +27,15 @@ public class JerseyServerIT { @Before public void onSetUp() throws Exception { + System.setProperty("sun.net.http.allowRestrictedHeaders", "true"); + ServerConfig serverConfig = new ServerConfig(); serverConfig.setCommunicationType(CommunicationType.REST); serverConfig.setServerAddress("http://localhost:8080"); - serverConfig.setCorsDomains(Arrays.asList("*.acme.com")); + serverConfig.setCorsDomains(Arrays.asList("*.acme.com", "*.other.com")); + + JaxbUtil.marshalWithNoValidation(serverConfig, System.out); + Application sample = new SampleApplication(); server = new JerseyServer(serverConfig, sample); @@ -41,12 +49,26 @@ public void onTearDown() throws Exception { @Test public void ping() { - + + MultivaluedMap headers = new MultivaluedHashMap<>(); + headers.add("Origin", "*.acme.com"); + headers.add("Access-Control-Request-Headers","FOO,BAR"); + Response result = ClientBuilder.newClient() - .target(serverUri) - .path("ping") - .request() - .get(); + .target(serverUri) + .path("ping") + .request() + .headers(headers) + .get(); + + assertThat(result.getHeaderString("Access-Control-Allow-Origin")) + .isEqualTo("*.acme.com"); + + assertThat(result.getHeaderString("Access-Control-Allow-Credentials")) + .isEqualTo("true"); + + assertThat(result.getHeaderString("Access-Control-Request-Headers")) + .isEqualTo("FOO,BAR"); assertThat(result.getStatus()).isEqualTo(200); assertThat(result.readEntity(String.class)).isEqualTo("HEllow"); @@ -59,26 +81,26 @@ public void create() { payload.setValue("Hellow"); Response result = ClientBuilder.newClient() - .target(serverUri) - .path("create") - .request() - .post(Entity.entity(payload, MediaType.APPLICATION_JSON)); + .target(serverUri) + .path("create") + .request() + .post(Entity.entity(payload, MediaType.APPLICATION_JSON)); assertThat(result.getStatus()).isEqualTo(201); assertThat(result.getLocation()).isNotNull(); Response result2 = ClientBuilder.newClient() - .target(result.getLocation()) - .request(MediaType.APPLICATION_JSON) - .get(); + .target(result.getLocation()) + .request(MediaType.APPLICATION_JSON) + .get(); SamplePayload p = result2.readEntity(SamplePayload.class); assertThat(p).isNotNull(); assertThat(p.getValue()).isEqualTo("Hellow"); Response result3 = ClientBuilder.newClient() - .target(serverUri) - .path(p.getId()).request().delete(); + .target(serverUri) + .path(p.getId()).request().delete(); assertThat(result3.getStatus()).isEqualTo(200); SamplePayload deleted = result3.readEntity(SamplePayload.class); From 8f008c314301963d419bf414489ee820b91f42c7 Mon Sep 17 00:00:00 2001 From: Mark Lowe Date: Thu, 25 Apr 2019 19:11:25 +0100 Subject: [PATCH 4/7] Assume that access control request isn't required. --- .../tessera/server/jaxrs/CorsDomainResponseFilter.java | 5 ++--- .../tessera/server/jaxrs/CorsDomainResponseFilterTest.java | 7 ++----- .../com/quorum/tessera/server/jaxrs/JerseyServerIT.java | 6 +----- 3 files changed, 5 insertions(+), 13 deletions(-) diff --git a/server/jersey-server/src/main/java/com/quorum/tessera/server/jaxrs/CorsDomainResponseFilter.java b/server/jersey-server/src/main/java/com/quorum/tessera/server/jaxrs/CorsDomainResponseFilter.java index 278b21a36d..5b92094234 100644 --- a/server/jersey-server/src/main/java/com/quorum/tessera/server/jaxrs/CorsDomainResponseFilter.java +++ b/server/jersey-server/src/main/java/com/quorum/tessera/server/jaxrs/CorsDomainResponseFilter.java @@ -30,15 +30,14 @@ public void filter(ContainerRequestContext requestContext, ContainerResponseCont final String origin = requestContext.getHeaderString("Origin"); - LOGGER.info("Origin header value {}", origin); - if (Objects.nonNull(origin) && !Objects.equals(origin, "")) { MultivaluedMap headers = responseContext.getHeaders(); headers.add("Access-Control-Allow-Origin", origin); headers.add("Access-Control-Allow-Credentials", "true"); - headers.add("Access-Control-Request-Headers", requestContext.getHeaderString("Access-Control-Request-Headers")); + headers.add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD"); + headers.add("Access-Control-Allow-Headers", "origin, content-type, accept, authorization"); } } diff --git a/server/jersey-server/src/test/java/com/quorum/tessera/server/jaxrs/CorsDomainResponseFilterTest.java b/server/jersey-server/src/test/java/com/quorum/tessera/server/jaxrs/CorsDomainResponseFilterTest.java index 1ac1e453c8..9e74f31600 100644 --- a/server/jersey-server/src/test/java/com/quorum/tessera/server/jaxrs/CorsDomainResponseFilterTest.java +++ b/server/jersey-server/src/test/java/com/quorum/tessera/server/jaxrs/CorsDomainResponseFilterTest.java @@ -68,8 +68,6 @@ public void filter() throws Exception { when(requestContext.getHeaderString("Origin")).thenReturn("bogus.com"); - when(requestContext.getHeaderString("Access-Control-Request-Headers")) - .thenReturn("SomeHeaders"); domainResponseFilter.filter(requestContext, responseContext); @@ -77,15 +75,14 @@ public void filter() throws Exception { .containsKeys( "Access-Control-Allow-Origin", "Access-Control-Allow-Credentials", - "Access-Control-Request-Headers"); + "Access-Control-Allow-Methods", + "Access-Control-Allow-Headers"); assertThat(headers.get("Access-Control-Allow-Origin")).containsExactly("bogus.com"); assertThat(headers.get("Access-Control-Allow-Credentials")).containsExactly("true"); - assertThat(headers.get("Access-Control-Request-Headers")).containsExactly("SomeHeaders"); verify(requestContext).getUriInfo(); verify(requestContext).getHeaderString("Origin"); - verify(requestContext).getHeaderString("Access-Control-Request-Headers"); verify(responseContext).getHeaders(); } diff --git a/server/jersey-server/src/test/java/com/quorum/tessera/server/jaxrs/JerseyServerIT.java b/server/jersey-server/src/test/java/com/quorum/tessera/server/jaxrs/JerseyServerIT.java index dc4275c8bd..c2b2aa5d16 100644 --- a/server/jersey-server/src/test/java/com/quorum/tessera/server/jaxrs/JerseyServerIT.java +++ b/server/jersey-server/src/test/java/com/quorum/tessera/server/jaxrs/JerseyServerIT.java @@ -52,8 +52,7 @@ public void ping() { MultivaluedMap headers = new MultivaluedHashMap<>(); headers.add("Origin", "*.acme.com"); - headers.add("Access-Control-Request-Headers","FOO,BAR"); - + Response result = ClientBuilder.newClient() .target(serverUri) .path("ping") @@ -67,9 +66,6 @@ public void ping() { assertThat(result.getHeaderString("Access-Control-Allow-Credentials")) .isEqualTo("true"); - assertThat(result.getHeaderString("Access-Control-Request-Headers")) - .isEqualTo("FOO,BAR"); - assertThat(result.getStatus()).isEqualTo(200); assertThat(result.readEntity(String.class)).isEqualTo("HEllow"); } From 778a4982845109a5d890c1840ecb3a0974c36e9a Mon Sep 17 00:00:00 2001 From: Mark Lowe Date: Fri, 26 Apr 2019 13:44:58 +0100 Subject: [PATCH 5/7] Added matching logic in origin match. - match wildcards - match exact matches. Add defaults for additional cors configs and pass through to headers. --- .../tessera/config/CrossDomainConfig.java | 56 +++++++++++++ .../quorum/tessera/config/ServerConfig.java | 15 ++-- .../quorum/tessera/server/JerseyServer.java | 4 +- .../jaxrs/CorsDomainResponseFilter.java | 31 +++++--- .../tessera/server/jaxrs/OriginMatchUtil.java | 33 ++++++++ .../jaxrs/CorsDomainResponseFilterTest.java | 20 +++-- .../tessera/server/jaxrs/JerseyServerIT.java | 6 +- .../server/jaxrs/OriginMatchUtilTest.java | 78 +++++++++++++++++++ .../tessera/service/ServiceContainer.java | 5 +- 9 files changed, 220 insertions(+), 28 deletions(-) create mode 100644 config/src/main/java/com/quorum/tessera/config/CrossDomainConfig.java create mode 100644 server/jersey-server/src/main/java/com/quorum/tessera/server/jaxrs/OriginMatchUtil.java create mode 100644 server/jersey-server/src/test/java/com/quorum/tessera/server/jaxrs/OriginMatchUtilTest.java diff --git a/config/src/main/java/com/quorum/tessera/config/CrossDomainConfig.java b/config/src/main/java/com/quorum/tessera/config/CrossDomainConfig.java new file mode 100644 index 0000000000..f2b14edb30 --- /dev/null +++ b/config/src/main/java/com/quorum/tessera/config/CrossDomainConfig.java @@ -0,0 +1,56 @@ +package com.quorum.tessera.config; + +import java.util.Arrays; +import java.util.List; +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; + +@XmlAccessorType(XmlAccessType.FIELD) +public class CrossDomainConfig extends ConfigItem { + + @XmlElement + private List allowedMethods = Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS", "HEAD"); + + @XmlElement + private List allowedOrigins; + + @XmlElement + private List allowedHeaders; + + @XmlElement + private Boolean allowCredentials = Boolean.TRUE; + + public List getAllowedOrigins() { + return allowedOrigins; + } + + public void setAllowedOrigins(List allowedOrigins) { + this.allowedOrigins = allowedOrigins; + } + + public List getAllowedMethods() { + return allowedMethods; + } + + public void setAllowedMethods(List allowedMethods) { + this.allowedMethods = allowedMethods; + } + + public List getAllowedHeaders() { + return allowedHeaders; + } + + public void setAllowedHeaders(List allowedHeaders) { + this.allowedHeaders = allowedHeaders; + } + + public Boolean getAllowCredentials() { + return allowCredentials; + } + + public void setAllowCredentials(Boolean allowCredentials) { + this.allowCredentials = allowCredentials; + } + +} diff --git a/config/src/main/java/com/quorum/tessera/config/ServerConfig.java b/config/src/main/java/com/quorum/tessera/config/ServerConfig.java index a883a5470a..687ed78a4d 100644 --- a/config/src/main/java/com/quorum/tessera/config/ServerConfig.java +++ b/config/src/main/java/com/quorum/tessera/config/ServerConfig.java @@ -10,7 +10,6 @@ import javax.xml.bind.annotation.XmlElement; import java.net.URI; import java.net.URISyntaxException; -import java.util.List; import java.util.Objects; @XmlAccessorType(XmlAccessType.FIELD) @@ -49,8 +48,8 @@ public class ServerConfig extends ConfigItem { @XmlElement private String serverAddress; - @XmlElement - private List corsDomains; + @XmlElement(name = "cors") + private CrossDomainConfig crossDomainConfig; public ServerConfig(final AppType app, final boolean enabled, @@ -152,12 +151,14 @@ public boolean isUnixSocket() { return Objects.equals(getServerUri().getScheme(), "unix"); } - public List getCorsDomains() { - return corsDomains; + public CrossDomainConfig getCrossDomainConfig() { + return crossDomainConfig; } - public void setCorsDomains(List corsDomains) { - this.corsDomains = corsDomains; + public void setCrossDomainConfig(CrossDomainConfig crossDomainConfig) { + this.crossDomainConfig = crossDomainConfig; } + + } diff --git a/server/jersey-server/src/main/java/com/quorum/tessera/server/JerseyServer.java b/server/jersey-server/src/main/java/com/quorum/tessera/server/JerseyServer.java index d9daeb3bc5..26bb497e33 100644 --- a/server/jersey-server/src/main/java/com/quorum/tessera/server/JerseyServer.java +++ b/server/jersey-server/src/main/java/com/quorum/tessera/server/JerseyServer.java @@ -80,8 +80,8 @@ public void start() throws Exception { .register(MetricsResource.class) .register(LoggingFilter.class); - if (serverConfig.getCorsDomains() != null) { - config.register(new CorsDomainResponseFilter(serverConfig.getCorsDomains())); + if (serverConfig.getCrossDomainConfig() != null) { + config.register(new CorsDomainResponseFilter(serverConfig.getCrossDomainConfig())); } this.server = ServerUtils.buildWebServer(serverConfig); diff --git a/server/jersey-server/src/main/java/com/quorum/tessera/server/jaxrs/CorsDomainResponseFilter.java b/server/jersey-server/src/main/java/com/quorum/tessera/server/jaxrs/CorsDomainResponseFilter.java index 5b92094234..7d54f7e8c6 100644 --- a/server/jersey-server/src/main/java/com/quorum/tessera/server/jaxrs/CorsDomainResponseFilter.java +++ b/server/jersey-server/src/main/java/com/quorum/tessera/server/jaxrs/CorsDomainResponseFilter.java @@ -1,8 +1,7 @@ package com.quorum.tessera.server.jaxrs; +import com.quorum.tessera.config.CrossDomainConfig; import java.io.IOException; -import java.util.List; -import java.util.Objects; import javax.ws.rs.container.ContainerRequestContext; import javax.ws.rs.container.ContainerResponseContext; import javax.ws.rs.container.ContainerResponseFilter; @@ -14,11 +13,13 @@ public class CorsDomainResponseFilter implements ContainerResponseFilter { private static final Logger LOGGER = LoggerFactory.getLogger(CorsDomainResponseFilter.class); - private final List tokens; + private final OriginMatchUtil originMatchUtil; + + private CrossDomainConfig corsConfig; - public CorsDomainResponseFilter(List tokens) { - this.tokens = Objects.requireNonNull(tokens); - LOGGER.info("Create filter with tokens {}", String.join(",", tokens)); + public CorsDomainResponseFilter(CrossDomainConfig corsConfig) { + this.corsConfig = corsConfig; + this.originMatchUtil = new OriginMatchUtil(corsConfig.getAllowedOrigins()); } @Override @@ -29,15 +30,23 @@ public void filter(ContainerRequestContext requestContext, ContainerResponseCont } final String origin = requestContext.getHeaderString("Origin"); - - if (Objects.nonNull(origin) && !Objects.equals(origin, "")) { + + if (originMatchUtil.matches(origin)) { MultivaluedMap headers = responseContext.getHeaders(); headers.add("Access-Control-Allow-Origin", origin); - headers.add("Access-Control-Allow-Credentials", "true"); - headers.add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD"); - headers.add("Access-Control-Allow-Headers", "origin, content-type, accept, authorization"); + headers.add("Access-Control-Allow-Credentials", corsConfig.getAllowCredentials().toString()); + headers.add("Access-Control-Allow-Methods", String.join(",", corsConfig.getAllowedMethods())); + + final String allowedHeaders; + if(corsConfig.getAllowedHeaders() != null) { + allowedHeaders = String.join(",",corsConfig.getAllowedHeaders()); + } else { + allowedHeaders = requestContext.getHeaderString("Access-Control-Request-Headers"); + } + + headers.add("Access-Control-Allow-Headers", allowedHeaders); } } diff --git a/server/jersey-server/src/main/java/com/quorum/tessera/server/jaxrs/OriginMatchUtil.java b/server/jersey-server/src/main/java/com/quorum/tessera/server/jaxrs/OriginMatchUtil.java new file mode 100644 index 0000000000..daf5cfbfad --- /dev/null +++ b/server/jersey-server/src/main/java/com/quorum/tessera/server/jaxrs/OriginMatchUtil.java @@ -0,0 +1,33 @@ +package com.quorum.tessera.server.jaxrs; + +import java.util.List; +import java.util.Objects; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +public class OriginMatchUtil { + + private final List tokens; + + private final Predicate wildcardMatch = s -> s.equals("*"); + + public OriginMatchUtil(List tokens) { + this.tokens = tokens.stream() + .map(s -> ("\\Q" + s + "\\E")) + .map(s -> s.replace("*", "\\E.*\\Q")) + .collect(Collectors.toList()); + } + + public boolean matches(String origin) { + + if(Objects.isNull(origin) || Objects.equals("", origin)) { + return false; + } + + Predicate subdomainMatch = s -> origin.matches(s); + Predicate matchingCritera = wildcardMatch.or(subdomainMatch); + + return tokens.stream().anyMatch(matchingCritera); + } + +} diff --git a/server/jersey-server/src/test/java/com/quorum/tessera/server/jaxrs/CorsDomainResponseFilterTest.java b/server/jersey-server/src/test/java/com/quorum/tessera/server/jaxrs/CorsDomainResponseFilterTest.java index 9e74f31600..ed6c910bc3 100644 --- a/server/jersey-server/src/test/java/com/quorum/tessera/server/jaxrs/CorsDomainResponseFilterTest.java +++ b/server/jersey-server/src/test/java/com/quorum/tessera/server/jaxrs/CorsDomainResponseFilterTest.java @@ -1,7 +1,8 @@ package com.quorum.tessera.server.jaxrs; +import com.quorum.tessera.config.CrossDomainConfig; import java.net.URI; -import java.util.Collections; +import java.util.Arrays; import javax.ws.rs.container.ContainerRequestContext; import javax.ws.rs.container.ContainerResponseContext; import javax.ws.rs.core.MultivaluedHashMap; @@ -18,6 +19,8 @@ public class CorsDomainResponseFilterTest { + private static final String SOME_ORIGIN = "http://bogus.com"; + private CorsDomainResponseFilter domainResponseFilter; private ContainerRequestContext requestContext; @@ -28,7 +31,11 @@ public class CorsDomainResponseFilterTest { @Before public void setUp() { - domainResponseFilter = new CorsDomainResponseFilter(Collections.EMPTY_LIST); + + CrossDomainConfig crossDomainConfig = new CrossDomainConfig(); + crossDomainConfig.setAllowedOrigins(Arrays.asList(SOME_ORIGIN)); + + domainResponseFilter = new CorsDomainResponseFilter(crossDomainConfig); requestContext = mock(ContainerRequestContext.class); responseContext = mock(ContainerResponseContext.class); uriInfo = mock(UriInfo.class); @@ -62,11 +69,11 @@ public void filter() throws Exception { when(responseContext.getHeaders()).thenReturn(headers); - when(uriInfo.getBaseUri()).thenReturn(new URI("http://bogus.com/")); + when(uriInfo.getBaseUri()).thenReturn(new URI(SOME_ORIGIN)); when(requestContext.getUriInfo()).thenReturn(uriInfo); - when(requestContext.getHeaderString("Origin")).thenReturn("bogus.com"); + when(requestContext.getHeaderString("Origin")).thenReturn(SOME_ORIGIN); domainResponseFilter.filter(requestContext, responseContext); @@ -78,11 +85,12 @@ public void filter() throws Exception { "Access-Control-Allow-Methods", "Access-Control-Allow-Headers"); - assertThat(headers.get("Access-Control-Allow-Origin")).containsExactly("bogus.com"); + assertThat(headers.get("Access-Control-Allow-Origin")).containsExactly(SOME_ORIGIN); assertThat(headers.get("Access-Control-Allow-Credentials")).containsExactly("true"); verify(requestContext).getUriInfo(); verify(requestContext).getHeaderString("Origin"); + verify(requestContext).getHeaderString("Access-Control-Request-Headers"); verify(responseContext).getHeaders(); } @@ -104,7 +112,7 @@ public void ignoreNoOrigin() throws Exception { @Test public void ignoreEmptyOrigin() throws Exception { - when(uriInfo.getBaseUri()).thenReturn(new URI("bogus.com")); + when(uriInfo.getBaseUri()).thenReturn(new URI(SOME_ORIGIN)); when(requestContext.getUriInfo()).thenReturn(uriInfo); when(requestContext.getHeaderString("Origin")).thenReturn(""); diff --git a/server/jersey-server/src/test/java/com/quorum/tessera/server/jaxrs/JerseyServerIT.java b/server/jersey-server/src/test/java/com/quorum/tessera/server/jaxrs/JerseyServerIT.java index c2b2aa5d16..8a54b1725c 100644 --- a/server/jersey-server/src/test/java/com/quorum/tessera/server/jaxrs/JerseyServerIT.java +++ b/server/jersey-server/src/test/java/com/quorum/tessera/server/jaxrs/JerseyServerIT.java @@ -1,6 +1,7 @@ package com.quorum.tessera.server.jaxrs; import com.quorum.tessera.config.CommunicationType; +import com.quorum.tessera.config.CrossDomainConfig; import com.quorum.tessera.config.ServerConfig; import com.quorum.tessera.config.util.JaxbUtil; @@ -32,7 +33,10 @@ public void onSetUp() throws Exception { ServerConfig serverConfig = new ServerConfig(); serverConfig.setCommunicationType(CommunicationType.REST); serverConfig.setServerAddress("http://localhost:8080"); - serverConfig.setCorsDomains(Arrays.asList("*.acme.com", "*.other.com")); + + CrossDomainConfig crossDomainConfig = new CrossDomainConfig(); + crossDomainConfig.setAllowedOrigins(Arrays.asList("*.acme.com", "*.other.com")); + serverConfig.setCrossDomainConfig(crossDomainConfig); JaxbUtil.marshalWithNoValidation(serverConfig, System.out); diff --git a/server/jersey-server/src/test/java/com/quorum/tessera/server/jaxrs/OriginMatchUtilTest.java b/server/jersey-server/src/test/java/com/quorum/tessera/server/jaxrs/OriginMatchUtilTest.java new file mode 100644 index 0000000000..31eb2a0216 --- /dev/null +++ b/server/jersey-server/src/test/java/com/quorum/tessera/server/jaxrs/OriginMatchUtilTest.java @@ -0,0 +1,78 @@ +package com.quorum.tessera.server.jaxrs; + +import java.util.Arrays; +import java.util.List; +import static org.assertj.core.api.Assertions.assertThat; +import org.junit.Test; + +public class OriginMatchUtilTest { + + @Test + public void matchWildCard() { + + List tokens = Arrays.asList("*"); + String origin = "http://bogus.com"; + OriginMatchUtil matcher = new OriginMatchUtil(tokens); + + assertThat(matcher.matches(origin)).isTrue(); + } + + @Test + public void exactMatch() { + List tokens = Arrays.asList("http://bogus.com"); + String origin = "http://bogus.com"; + OriginMatchUtil matcher = new OriginMatchUtil(tokens); + + assertThat(matcher.matches(origin)).isTrue(); + } + + @Test + public void withSubDomain() { + List tokens = Arrays.asList("http://*.bogus.com"); + String origin = "http://myhost.bogus.com"; + + OriginMatchUtil matcher = new OriginMatchUtil(tokens); + assertThat(matcher.matches(origin)).isTrue(); + } + + @Test + public void withBadSubDomain() { + List tokens = Arrays.asList("http://*.bogus.com"); + String origin = "http://myhost.other.com"; + + OriginMatchUtil matcher = new OriginMatchUtil(tokens); + assertThat(matcher.matches(origin)).isFalse(); + } + + @Test + public void withNullOrigin() { + List tokens = Arrays.asList("http://*.bogus.com"); + OriginMatchUtil matcher = new OriginMatchUtil(tokens); + assertThat(matcher.matches(null)).isFalse(); + } + + @Test + public void withEmptyOrigin() { + List tokens = Arrays.asList("http://*.bogus.com"); + OriginMatchUtil matcher = new OriginMatchUtil(tokens); + assertThat(matcher.matches("")).isFalse(); + } + + @Test + public void withSubDomainWithPort() { + List tokens = Arrays.asList("http://*.bogus.com"); + String origin = "http://myhost.bogus.com:989"; + + OriginMatchUtil matcher = new OriginMatchUtil(tokens); + assertThat(matcher.matches(origin)).isFalse(); + } + + @Test + public void withDifferentScheme() { + List tokens = Arrays.asList("https://bogus.com"); + String origin = "http://bogus.com"; + + OriginMatchUtil matcher = new OriginMatchUtil(tokens); + assertThat(matcher.matches(origin)).isFalse(); + } +} diff --git a/shared/src/main/java/com/quorum/tessera/service/ServiceContainer.java b/shared/src/main/java/com/quorum/tessera/service/ServiceContainer.java index e8679a31ac..7f098b2610 100644 --- a/shared/src/main/java/com/quorum/tessera/service/ServiceContainer.java +++ b/shared/src/main/java/com/quorum/tessera/service/ServiceContainer.java @@ -62,7 +62,10 @@ public void run() { LOGGER.debug("Started service {}", service); } catch (Throwable ex) { LOGGER.trace(null, ex); - LOGGER.warn("Exception thrown : {} While starting service {}", Optional.ofNullable(ex.getCause()).orElse(ex).getMessage(), service); + LOGGER.warn("Exception thrown : {} While starting service {}", + Optional.ofNullable(ex.getCause()) + .orElse(ex) + .getMessage(), service); } } } From 5bf3114ad0a0dfeb99c2148d57336635ef353844 Mon Sep 17 00:00:00 2001 From: Mark Lowe Date: Fri, 26 Apr 2019 13:57:51 +0100 Subject: [PATCH 6/7] Add case and fix for case sensitive. Add cli options. --- .../tessera/config/cli/OverrideUtilTest.java | 22 ++++++++++--------- .../tessera/server/jaxrs/OriginMatchUtil.java | 1 + .../server/jaxrs/OriginMatchUtilTest.java | 18 +++++++++++++++ 3 files changed, 31 insertions(+), 10 deletions(-) diff --git a/config-cli/src/test/java/com/quorum/tessera/config/cli/OverrideUtilTest.java b/config-cli/src/test/java/com/quorum/tessera/config/cli/OverrideUtilTest.java index b67a351475..5f1accca47 100644 --- a/config-cli/src/test/java/com/quorum/tessera/config/cli/OverrideUtilTest.java +++ b/config-cli/src/test/java/com/quorum/tessera/config/cli/OverrideUtilTest.java @@ -54,7 +54,10 @@ public void buildOptions() { "useWhiteList", "disablePeerDiscovery", "serverConfigs.serverAddress", - "serverConfigs.corsDomains", + "serverConfigs.cors.allowedOrigins", + "serverConfigs.cors.allowCredentials", + "serverConfigs.cors.allowedHeaders", + "serverConfigs.cors.allowedMethods", "serverConfigs.sslConfig.serverTrustStore", "serverConfigs.influxConfig.dbName", "serverConfigs.sslConfig.knownClientsFile", @@ -121,8 +124,8 @@ public void buildOptions() { final Map results = OverrideUtil.buildConfigOptions(); assertThat(results.keySet()) - .filteredOn(s -> !s.contains("$jacocoData")) - .containsExactlyInAnyOrderElementsOf(expected); + .filteredOn(s -> !s.contains("$jacocoData")) + .containsExactlyInAnyOrderElementsOf(expected); assertThat(results.get("serverConfigs.sslConfig.knownClientsFile")).isEqualTo(Path.class); assertThat(results.get("keys.passwords")).isEqualTo(String[].class); @@ -210,9 +213,9 @@ public void isSimple() { @Test public void toArrayType() { assertThat(OverrideUtil.toArrayType(String.class)) - .isEqualTo(String[].class); + .isEqualTo(String[].class); assertThat(OverrideUtil.toArrayType(Path.class)) - .isEqualTo(Path[].class); + .isEqualTo(Path[].class); } @Test @@ -242,7 +245,7 @@ public void convertTo() { assertThat(OverrideUtil.convertTo(Boolean.class, "true")).isTrue(); assertThat(OverrideUtil.convertTo(SslAuthenticationMode.class, "STRICT")) - .isEqualTo(SslAuthenticationMode.STRICT); + .isEqualTo(SslAuthenticationMode.STRICT); assertThat(OverrideUtil.convertTo(String.class, null)).isNull(); @@ -321,7 +324,7 @@ public void setValueOnNullDoesNothing() { @Test public void setValuePreservePreDefined() throws Exception { final Config config; - try(InputStream data = getClass().getResourceAsStream("/sample-config.json")) { + try (InputStream data = getClass().getResourceAsStream("/sample-config.json")) { config = JaxbUtil.unmarshal(data, Config.class); } @@ -340,11 +343,10 @@ public void definePrivateAndPublicKeyWithOverridesOnly() throws Exception { Config config = OverrideUtil.createInstance(Config.class); - OverrideUtil.setValue(config, "keys.keyData.publicKey", "PUBLICKEY"); OverrideUtil.setValue(config, "keys.keyData.privateKey", "PRIVATEKEY"); //UNmarshlling to COnfig to - try(ByteArrayOutputStream bout = new ByteArrayOutputStream()) { + try (ByteArrayOutputStream bout = new ByteArrayOutputStream()) { JaxbUtil.marshalWithNoValidation(config, bout); Config result = JaxbUtil.unmarshal(new ByteArrayInputStream(bout.toByteArray()), Config.class); assertThat(result.getKeys()).isNotNull(); @@ -366,7 +368,7 @@ public void defineAlwaysSendToWithOverridesOnly() throws Exception { OverrideUtil.setValue(config, "alwaysSendTo", "ONE", "TWO"); - try(ByteArrayOutputStream bout = new ByteArrayOutputStream()) { + try (ByteArrayOutputStream bout = new ByteArrayOutputStream()) { JaxbUtil.marshalWithNoValidation(config, bout); Config result = JaxbUtil.unmarshal(new ByteArrayInputStream(bout.toByteArray()), Config.class); diff --git a/server/jersey-server/src/main/java/com/quorum/tessera/server/jaxrs/OriginMatchUtil.java b/server/jersey-server/src/main/java/com/quorum/tessera/server/jaxrs/OriginMatchUtil.java index daf5cfbfad..708e6e473e 100644 --- a/server/jersey-server/src/main/java/com/quorum/tessera/server/jaxrs/OriginMatchUtil.java +++ b/server/jersey-server/src/main/java/com/quorum/tessera/server/jaxrs/OriginMatchUtil.java @@ -13,6 +13,7 @@ public class OriginMatchUtil { public OriginMatchUtil(List tokens) { this.tokens = tokens.stream() + .map(String::toLowerCase) .map(s -> ("\\Q" + s + "\\E")) .map(s -> s.replace("*", "\\E.*\\Q")) .collect(Collectors.toList()); diff --git a/server/jersey-server/src/test/java/com/quorum/tessera/server/jaxrs/OriginMatchUtilTest.java b/server/jersey-server/src/test/java/com/quorum/tessera/server/jaxrs/OriginMatchUtilTest.java index 31eb2a0216..1e47aa8c23 100644 --- a/server/jersey-server/src/test/java/com/quorum/tessera/server/jaxrs/OriginMatchUtilTest.java +++ b/server/jersey-server/src/test/java/com/quorum/tessera/server/jaxrs/OriginMatchUtilTest.java @@ -75,4 +75,22 @@ public void withDifferentScheme() { OriginMatchUtil matcher = new OriginMatchUtil(tokens); assertThat(matcher.matches(origin)).isFalse(); } + + @Test + public void withSamePorts() { + List tokens = Arrays.asList("http://bogus.com:989"); + String origin = "http://bogus.com:989"; + + OriginMatchUtil matcher = new OriginMatchUtil(tokens); + assertThat(matcher.matches(origin)).isTrue(); + } + + @Test + public void withDifferentCases() { + List tokens = Arrays.asList("HTTP://BOGUS.cOm:989"); + String origin = "http://bogus.com:989"; + + OriginMatchUtil matcher = new OriginMatchUtil(tokens); + assertThat(matcher.matches(origin)).isTrue(); + } } From b949155cb644174490f311dbc3cf41c4934dc1b0 Mon Sep 17 00:00:00 2001 From: Mark Lowe Date: Fri, 26 Apr 2019 14:03:09 +0100 Subject: [PATCH 7/7] Switch cases around --- .../com/quorum/tessera/server/jaxrs/OriginMatchUtil.java | 2 +- .../quorum/tessera/server/jaxrs/OriginMatchUtilTest.java | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/server/jersey-server/src/main/java/com/quorum/tessera/server/jaxrs/OriginMatchUtil.java b/server/jersey-server/src/main/java/com/quorum/tessera/server/jaxrs/OriginMatchUtil.java index 708e6e473e..1942c73c8b 100644 --- a/server/jersey-server/src/main/java/com/quorum/tessera/server/jaxrs/OriginMatchUtil.java +++ b/server/jersey-server/src/main/java/com/quorum/tessera/server/jaxrs/OriginMatchUtil.java @@ -25,7 +25,7 @@ public boolean matches(String origin) { return false; } - Predicate subdomainMatch = s -> origin.matches(s); + Predicate subdomainMatch = s -> origin.toLowerCase().matches(s); Predicate matchingCritera = wildcardMatch.or(subdomainMatch); return tokens.stream().anyMatch(matchingCritera); diff --git a/server/jersey-server/src/test/java/com/quorum/tessera/server/jaxrs/OriginMatchUtilTest.java b/server/jersey-server/src/test/java/com/quorum/tessera/server/jaxrs/OriginMatchUtilTest.java index 1e47aa8c23..7afaeae93a 100644 --- a/server/jersey-server/src/test/java/com/quorum/tessera/server/jaxrs/OriginMatchUtilTest.java +++ b/server/jersey-server/src/test/java/com/quorum/tessera/server/jaxrs/OriginMatchUtilTest.java @@ -90,6 +90,15 @@ public void withDifferentCases() { List tokens = Arrays.asList("HTTP://BOGUS.cOm:989"); String origin = "http://bogus.com:989"; + OriginMatchUtil matcher = new OriginMatchUtil(tokens); + assertThat(matcher.matches(origin)).isTrue(); + } + + @Test + public void withDifferentCases2() { + List tokens = Arrays.asList("http://bogus.com:989"); + String origin = "HTTP://BOGUS.cOm:989"; + OriginMatchUtil matcher = new OriginMatchUtil(tokens); assertThat(matcher.matches(origin)).isTrue(); }