From aedf2d185709522faca54b0b2d6797c639a70170 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathias=20R=C3=BChle?= Date: Thu, 4 Jan 2024 15:45:08 +0100 Subject: [PATCH] Disable Logging of Requests by DSF Webservice Client Add environment variable to enable request logging if needed. --- README.md | 4 +- .../broker/dsf/DSFFhirWebClientProvider.java | 7 ++- .../query/broker/dsf/DSFSpringConfig.java | 5 +- src/main/resources/application.yml | 1 + .../dsf/DSFFhirWebClientProviderTest.java | 61 +++++++++++++++++++ 5 files changed, 73 insertions(+), 5 deletions(-) create mode 100644 src/test/java/de/numcodex/feasibility_gui_backend/query/broker/dsf/DSFFhirWebClientProviderTest.java diff --git a/README.md b/README.md index fa1764b7..6e0dc0ba 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ | QUERY_VALIDATION_ENABLED | When enabled, any structured query submitted via the `run-query` endpoint is validated against the JSON schema located in `src/main/resources/query/query-schema.json` | true / false | true | | QUERYRESULT_EXPIRY_MINUTES | How many minutes should query results be kept in memory? | | 5 | | QUERYRESULT_PUBLIC_KEY | The public key in Base64-encoded DER format without banners and line breaks. Mandatory if _QUERYRESULT_DISABLE_LOG_FILE_ENCRYPTION_ is _false_ | -| QUERYRESULT_DISABLE_LOG_FILE_ENCRYPTION | Disable encryption of the result log file. | true / false | | +| QUERYRESULT_DISABLE_LOG_FILE_ENCRYPTION | Disable encryption of the result log file. | true / false | | | ALLOWED_ORIGINS | Allowed origins for cross-origin requests. This should at least cover the frontend address. | | http://localhost | | MAX_SAVED_QUERIES_PER_USER | How many slots does a user have to store saved queries. | | 10 | @@ -94,10 +94,10 @@ In order to run the backend using the DSF path, the following environment variab | DSF_PROXY_USERNAME | Proxy username to be used. | | | | DSF_PROXY_PASSWORD | Proxy password to be used. | | | | DSF_WEBSERVICE_BASE_URL | Base URL pointing to the local ZARS FHIR server. | `https://zars/fhir` | | +| DSF_WEBSERVICE_LOG_REQUESTS | Log webservice client communication at log level INFO or below (**WARNING**: potentially contains sensitive data) | `true` | `false` | | DSF_WEBSOCKET_URL | URL pointing to the local ZARS FHIR server websocket endpoint. | `wss://zars/fhir/ws` | | | DSF_ORGANIZATION_ID | Identifier for the local organization this backend is part of. | `MY ZARS` | | - ### Privacy and Obfuscation In order to prevent potentially malicious attempts to obtain critical patient data, several diff --git a/src/main/java/de/numcodex/feasibility_gui_backend/query/broker/dsf/DSFFhirWebClientProvider.java b/src/main/java/de/numcodex/feasibility_gui_backend/query/broker/dsf/DSFFhirWebClientProvider.java index 673f4a2c..17d58d05 100644 --- a/src/main/java/de/numcodex/feasibility_gui_backend/query/broker/dsf/DSFFhirWebClientProvider.java +++ b/src/main/java/de/numcodex/feasibility_gui_backend/query/broker/dsf/DSFFhirWebClientProvider.java @@ -38,12 +38,14 @@ class DSFFhirWebClientProvider implements FhirWebClientProvider { private final FhirSecurityContextProvider securityContextProvider; private FhirSecurityContext securityContext; private final FhirProxyContext proxyContext; + private boolean logRequests; public DSFFhirWebClientProvider(FhirContext fhirContext, String webserviceBaseUrl, int webserviceReadTimeout, int webserviceConnectTimeout, String websocketUrl, FhirSecurityContextProvider securityContextProvider, - FhirProxyContext proxyContext) { + FhirProxyContext proxyContext, + boolean logRequests) { this.fhirContext = fhirContext; this.webserviceBaseUrl = webserviceBaseUrl; this.webserviceReadTimeout = webserviceReadTimeout; @@ -51,6 +53,7 @@ public DSFFhirWebClientProvider(FhirContext fhirContext, String webserviceBaseUr this.websocketUrl = websocketUrl; this.securityContextProvider = securityContextProvider; this.proxyContext = proxyContext; + this.logRequests = logRequests; } @Override @@ -76,7 +79,7 @@ public FhirWebserviceClient provideFhirWebserviceClient() throws FhirWebClientPr proxyContext.getPassword(), webserviceConnectTimeout, webserviceReadTimeout, - true, + logRequests, null, fhirContext, cleaner); diff --git a/src/main/java/de/numcodex/feasibility_gui_backend/query/broker/dsf/DSFSpringConfig.java b/src/main/java/de/numcodex/feasibility_gui_backend/query/broker/dsf/DSFSpringConfig.java index c2269ed0..f720d5c3 100644 --- a/src/main/java/de/numcodex/feasibility_gui_backend/query/broker/dsf/DSFSpringConfig.java +++ b/src/main/java/de/numcodex/feasibility_gui_backend/query/broker/dsf/DSFSpringConfig.java @@ -43,6 +43,9 @@ public class DSFSpringConfig { @Value("${app.broker.dsf.webservice.connectTimeout}") private int webserviceConnectTimeout; + @Value("${app.broker.dsf.webservice.logRequests}") + private boolean logRequests; + @Value("${app.broker.dsf.websocket.url}") private String websocketUrl; @@ -102,7 +105,7 @@ FhirWebClientProvider fhirWebClientProvider(FhirContext fhirContext, FhirSecurityContextProvider securityContextProvider, FhirProxyContext proxyContext) { return new DSFFhirWebClientProvider(fhirContext, webserviceBaseUrl, webserviceReadTimeout, - webserviceConnectTimeout, websocketUrl, securityContextProvider, proxyContext); + webserviceConnectTimeout, websocketUrl, securityContextProvider, proxyContext, logRequests); } } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index c5efbc35..da779be3 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -83,6 +83,7 @@ app: baseUrl: ${DSF_WEBSERVICE_BASE_URL} readTimeout: 20000 connectTimeout: 2000 + logRequests: ${DSF_WEBSERVICE_LOG_REQUESTS:false} websocket: url: ${DSF_WEBSOCKET_URL} organizationId: ${DSF_ORGANIZATION_ID} diff --git a/src/test/java/de/numcodex/feasibility_gui_backend/query/broker/dsf/DSFFhirWebClientProviderTest.java b/src/test/java/de/numcodex/feasibility_gui_backend/query/broker/dsf/DSFFhirWebClientProviderTest.java new file mode 100644 index 00000000..804bb670 --- /dev/null +++ b/src/test/java/de/numcodex/feasibility_gui_backend/query/broker/dsf/DSFFhirWebClientProviderTest.java @@ -0,0 +1,61 @@ +package de.numcodex.feasibility_gui_backend.query.broker.dsf; + +import ca.uhn.fhir.context.FhirContext; +import dev.dsf.fhir.client.FhirWebserviceClient; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.boot.test.system.CapturedOutput; +import org.springframework.boot.test.system.OutputCaptureExtension; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.Network; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +import static java.lang.String.format; +import static org.assertj.core.api.Assertions.assertThat; + +@Testcontainers +@ExtendWith(OutputCaptureExtension.class) +public class DSFFhirWebClientProviderTest { + + @Container + private GenericContainer blaze = new GenericContainer<>("samply/blaze:0.23.3") + .withExposedPorts(8080) + .withNetwork(Network.newNetwork()) + .withReuse(true); + + @Test + void settingLogRequestsFlagToTrueEnablesRequestLogs(CapturedOutput output) throws Exception { + String webserviceBaseUrl = format("http://%s:%s/fhir", blaze.getHost(), blaze.getFirstMappedPort()); + String websocketBaseUrl = format("ws://%s:%s/fhir", blaze.getHost(), blaze.getFirstMappedPort()); + FhirSecurityContextProvider securityContextProvider = () -> new FhirSecurityContext(null, null, null); + FhirProxyContext proxyContext = new FhirProxyContext(null, null, null); + DSFFhirWebClientProvider clientProvider = new DSFFhirWebClientProvider(FhirContext.forR4(), webserviceBaseUrl, + 20000, 2000, websocketBaseUrl, securityContextProvider, proxyContext, true); + FhirWebserviceClient client = clientProvider.provideFhirWebserviceClient(); + + client.getConformance(); + + assertThat(output) + .containsIgnoringCase("sending client request") + .containsIgnoringCase("client response received"); + } + + @Test + void settingLogRequestsFlagToFalseDisablesRequestLogs(CapturedOutput output) throws Exception { + String webserviceBaseUrl = format("http://%s:%s/fhir", blaze.getHost(), blaze.getFirstMappedPort()); + String websocketBaseUrl = format("ws://%s:%s/fhir", blaze.getHost(), blaze.getFirstMappedPort()); + FhirSecurityContextProvider securityContextProvider = () -> new FhirSecurityContext(null, null, null); + FhirProxyContext proxyContext = new FhirProxyContext(null, null, null); + DSFFhirWebClientProvider clientProvider = new DSFFhirWebClientProvider(FhirContext.forR4(), webserviceBaseUrl, + 20000, 2000, websocketBaseUrl, securityContextProvider, proxyContext, false); + FhirWebserviceClient client = clientProvider.provideFhirWebserviceClient(); + + client.getConformance(); + + assertThat(output) + .doesNotContainIgnoringCase("sending client request") + .doesNotContainIgnoringCase("client response received"); + } + +}