diff --git a/codex-process-data-transfer/pom.xml b/codex-process-data-transfer/pom.xml index edab793..5deab4c 100644 --- a/codex-process-data-transfer/pom.xml +++ b/codex-process-data-transfer/pom.xml @@ -24,7 +24,7 @@ org.springframework spring-web - 6.0.11 + 6.1.6 provided @@ -181,7 +181,7 @@ commons-codec,commons-io,crypto-utils,jakarta.activation,jakarta.annotation-api,jakarta.ws.rs-api,jakarta.xml.bind-api,commons-compress,commons-lang3,commons-text, httpclient,httpcore,log4j-api,log4j-core,log4j-slf4j2-impl,bcpkix-jdk18on,bcprov-jdk18on,bcutil-jdk18on,ucum,hk2-api,hk2-locator,hk2-utils,osgi-resource-locator, aopalliance-repackaged,jakarta.inject-api,jersey-apache-connector,jersey-client,jersey-common,jersey-entity-filtering,jersey-hk2,jersey-media-jaxb,jersey-media-json-jackson, - dsf-bpe-process-api-v1,dsf-fhir-auth,dsf-fhir-rest-adapter,dsf-fhir-validation,dsf-openehr-model,jcl-over-slf4j, + dsf-bpe-process-api-v1,dsf-fhir-auth,dsf-fhir-rest-adapter,dsf-fhir-validation,dsf-openehr-model,jcl-over-slf4j,jackson-module-jakarta-xmlbind-annotations, slf4j-api,spring-aop,spring-beans,spring-context,spring-core,spring-expression,spring-jcl,thymeleaf,unbescape,xpp3,xpp3_xpath diff --git a/codex-process-data-transfer/src/main/java/de/netzwerk_universitaetsmedizin/codex/processes/data_transfer/spring/config/TransferDataConfig.java b/codex-process-data-transfer/src/main/java/de/netzwerk_universitaetsmedizin/codex/processes/data_transfer/spring/config/TransferDataConfig.java index 6d6266e..4289655 100644 --- a/codex-process-data-transfer/src/main/java/de/netzwerk_universitaetsmedizin/codex/processes/data_transfer/spring/config/TransferDataConfig.java +++ b/codex-process-data-transfer/src/main/java/de/netzwerk_universitaetsmedizin/codex/processes/data_transfer/spring/config/TransferDataConfig.java @@ -21,6 +21,7 @@ import de.netzwerk_universitaetsmedizin.codex.processes.data_transfer.logging.DataLogger; import de.netzwerk_universitaetsmedizin.codex.processes.data_transfer.logging.ErrorLogger; import dev.dsf.bpe.v1.ProcessPluginApi; +import dev.dsf.bpe.v1.config.ProxyConfig; import dev.dsf.bpe.v1.documentation.ProcessDocumentation; @Configuration @@ -367,4 +368,11 @@ public ErrorLogger errorLogger() { return new ErrorLogger(api.getMailService(), sendValidationFailedMail, sendProcessFailedMail); } + + // for validation config + @Bean + public ProxyConfig proxyConfig() + { + return api.getProxyConfig(); + } } diff --git a/codex-process-data-transfer/src/main/java/de/netzwerk_universitaetsmedizin/codex/processes/data_transfer/spring/config/ValidationConfig.java b/codex-process-data-transfer/src/main/java/de/netzwerk_universitaetsmedizin/codex/processes/data_transfer/spring/config/ValidationConfig.java index 98a5372..cf97626 100644 --- a/codex-process-data-transfer/src/main/java/de/netzwerk_universitaetsmedizin/codex/processes/data_transfer/spring/config/ValidationConfig.java +++ b/codex-process-data-transfer/src/main/java/de/netzwerk_universitaetsmedizin/codex/processes/data_transfer/spring/config/ValidationConfig.java @@ -54,7 +54,7 @@ import de.rwh.utils.crypto.CertificateHelper; import de.rwh.utils.crypto.io.CertificateReader; import de.rwh.utils.crypto.io.PemIo; -import dev.dsf.bpe.v1.ProcessPluginApi; +import dev.dsf.bpe.v1.config.ProxyConfig; import dev.dsf.bpe.v1.documentation.ProcessDocumentation; import dev.dsf.fhir.validation.SnapshotGenerator; import dev.dsf.fhir.validation.ValueSetExpander; @@ -66,9 +66,6 @@ public class ValidationConfig { private static final Logger logger = LoggerFactory.getLogger(ValidationConfig.class); - @Autowired - private ProcessPluginApi api; - public static enum TerminologyServerConnectionTestStatus { OK, NOT_OK, DISABLED @@ -209,6 +206,9 @@ public static enum TerminologyServerConnectionTestStatus @Autowired private ObjectMapper objectMapper; + // not using process plugin api to enable reuse of this config class in stand-alone validator + @Autowired + private ProxyConfig proxyConfig; @Bean public ValidationPackageManager validationPackageManager() @@ -376,11 +376,11 @@ private ValidationPackageClientJersey validationPackageClientJersey() String proxyUrl = null, proxyUsername = null; char[] proxyPassword = null; - if (api.getProxyConfig().isEnabled() && !api.getProxyConfig().isNoProxyUrl(packageServerBaseUrl)) + if (proxyConfig.isEnabled() && !proxyConfig.isNoProxyUrl(packageServerBaseUrl)) { - proxyUrl = api.getProxyConfig().getUrl(); - proxyUsername = api.getProxyConfig().getUsername(); - proxyPassword = api.getProxyConfig().getPassword() == null ? null : api.getProxyConfig().getPassword(); + proxyUrl = proxyConfig.getUrl(); + proxyUsername = proxyConfig.getUsername(); + proxyPassword = proxyConfig.getPassword() == null ? null : proxyConfig.getPassword(); } KeyStore packageClientTrustStore = trustStore("FHIR package client", packageClientTrustCertificates); @@ -439,11 +439,11 @@ private ValueSetExpansionClient valueSetExpansionClientJersey() String proxyUrl = null, proxyUsername = null; char[] proxyPassword = null; - if (api.getProxyConfig().isEnabled() && !api.getProxyConfig().isNoProxyUrl(valueSetExpansionServerBaseUrl)) + if (proxyConfig.isEnabled() && !proxyConfig.isNoProxyUrl(valueSetExpansionServerBaseUrl)) { - proxyUrl = api.getProxyConfig().getUrl(); - proxyUsername = api.getProxyConfig().getUsername(); - proxyPassword = api.getProxyConfig().getPassword() == null ? null : api.getProxyConfig().getPassword(); + proxyUrl = proxyConfig.getUrl(); + proxyUsername = proxyConfig.getUsername(); + proxyPassword = proxyConfig.getPassword() == null ? null : proxyConfig.getPassword(); } KeyStore valueSetExpansionClientTrustStore = trustStore("ValueSet expansion client", diff --git a/codex-process-data-transfer/src/main/java/de/netzwerk_universitaetsmedizin/codex/processes/data_transfer/validation/ValidationMain.java b/codex-process-data-transfer/src/main/java/de/netzwerk_universitaetsmedizin/codex/processes/data_transfer/validation/ValidationMain.java index 048d70a..a807dc9 100644 --- a/codex-process-data-transfer/src/main/java/de/netzwerk_universitaetsmedizin/codex/processes/data_transfer/validation/ValidationMain.java +++ b/codex-process-data-transfer/src/main/java/de/netzwerk_universitaetsmedizin/codex/processes/data_transfer/validation/ValidationMain.java @@ -1,6 +1,8 @@ package de.netzwerk_universitaetsmedizin.codex.processes.data_transfer.validation; import java.io.InputStream; +import java.net.URI; +import java.net.URISyntaxException; import java.nio.file.Files; import java.nio.file.Paths; import java.util.ArrayList; @@ -8,6 +10,7 @@ import java.util.List; import java.util.Locale; import java.util.Objects; +import java.util.stream.Collectors; import java.util.stream.Stream; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -36,6 +39,8 @@ import ca.uhn.fhir.validation.ValidationResult; import de.netzwerk_universitaetsmedizin.codex.processes.data_transfer.spring.config.ValidationConfig; import de.netzwerk_universitaetsmedizin.codex.processes.data_transfer.spring.config.ValidationConfig.TerminologyServerConnectionTestStatus; +import dev.dsf.bpe.v1.config.ProxyConfig; +import dev.dsf.bpe.v1.documentation.ProcessDocumentation; public class ValidationMain implements InitializingBean { @@ -73,6 +78,22 @@ public static class TestConfig @Value("${de.netzwerk.universitaetsmedizin.rdp.validation.output.pretty:true}") private boolean outputPretty; + @ProcessDocumentation(description = "Forward (http/https) proxy url, use *DEV_DSF_BPE_PROXY_NOPROXY* to list domains that do not require a forward proxy", example = "http://proxy.foo:8080") + @Value("${de.netzwerk.universitaetsmedizin.rdp.validation.proxy.url:#{null}}") + private String proxyUrl; + + @ProcessDocumentation(description = "Forward proxy username", recommendation = "Configure username if proxy requires authentication") + @Value("${de.netzwerk.universitaetsmedizin.rdp.validation.proxy.username:#{null}}") + private String proxyUsername; + + @ProcessDocumentation(description = "Forward Proxy password", recommendation = "Configure password if proxy requires authentication, use docker secret file to configure using *${env_variable}_FILE*") + @Value("${de.netzwerk.universitaetsmedizin.rdp.validation.proxy.password:#{null}}") + private char[] proxyPassword; + + @ProcessDocumentation(description = "Forward proxy no-proxy list, entries will match exactly or agianst (one level) sub-domains, if no port is specified - all ports are matched; comma or space separated list, YAML block scalars supported", example = "foo.bar, test.com:8080") + @Value("#{'${de.netzwerk.universitaetsmedizin.rdp.validation.proxy.noProxy:}'.trim().split('(,[ ]?)|(\\n)')}") + private List proxyNoProxy; + @Autowired private ValidationPackageManager packageManager; @@ -86,7 +107,7 @@ public static class TestConfig private ConfigurableEnvironment environment; @Bean - public ObjectMapper getObjectMapper() + public ObjectMapper objectMapper() { return JsonMapper.builder().serializationInclusion(Include.NON_NULL) .serializationInclusion(Include.NON_EMPTY).disable(MapperFeature.AUTO_DETECT_CREATORS) @@ -94,7 +115,7 @@ public ObjectMapper getObjectMapper() } @Bean - public FhirContext getFhirContext() + public FhirContext fhirContext() { FhirContext context = FhirContext.forR4(); HapiLocalizer localizer = new HapiLocalizer() @@ -109,11 +130,91 @@ public Locale getLocale() return context; } + @Bean + public ProxyConfig proxyConfig() + { + return new ProxyConfig() + { + @Override + public boolean isNoProxyUrl(String targetUrl) + { + if (proxyNoProxy.contains("*")) + return true; + + if (targetUrl == null || targetUrl.isBlank()) + return false; + + try + { + URI u = new URI(targetUrl); + + String host = u.getHost(); + if (host == null) + { + logger.debug("Given targetUrl '{}' is malformed, no host value", targetUrl); + return false; + } + + String subHost = Stream.of(u.getHost().split("\\.")).skip(1).collect(Collectors.joining(".")); + int port = u.getPort() == -1 ? getDefaultPort(u.getScheme()) : u.getPort(); + + return proxyNoProxy.stream().anyMatch(s -> s.equals(host) || s.equals(host + ":" + port) + || s.equals(subHost) || s.equals(subHost + ":" + port)); + } + catch (URISyntaxException e) + { + logger.debug("Given targetUrl '{}' is malformed: {}", targetUrl, e.getMessage()); + return false; + } + } + + private int getDefaultPort(String scheme) + { + return switch (scheme) + { + case "http", "ws" -> 80; + case "https", "wss" -> 443; + default -> throw new IllegalArgumentException("Schema " + scheme + " not supported"); + }; + } + + @Override + public boolean isEnabled() + { + return getUrl() != null; + } + + @Override + public String getUsername() + { + return proxyUsername; + } + + @Override + public String getUrl() + { + return proxyUrl; + } + + @Override + public char[] getPassword() + { + return proxyPassword; + } + + @Override + public List getNoProxyUrls() + { + return proxyNoProxy; + } + }; + } + @Bean public ValidationMain validatorMain() { - return new ValidationMain(environment, getFhirContext(), packageManager, validationPackageIdentifiers, - output, outputPretty, valueSetExpansionClient); + return new ValidationMain(environment, fhirContext(), packageManager, validationPackageIdentifiers, output, + outputPretty, valueSetExpansionClient); } } diff --git a/codex-processes-ap1-docker-test-setup/docker-compose.yml b/codex-processes-ap1-docker-test-setup/docker-compose.yml index f40c528..60b98ca 100644 --- a/codex-processes-ap1-docker-test-setup/docker-compose.yml +++ b/codex-processes-ap1-docker-test-setup/docker-compose.yml @@ -74,7 +74,7 @@ services: dic-fhir: - image: ghcr.io/datasharingframework/fhir:1.5.0 + image: ghcr.io/datasharingframework/fhir:1.5.1 restart: "no" ports: - 127.0.0.1:5000:5000 @@ -136,7 +136,7 @@ services: - db - proxy dic-bpe: - image: ghcr.io/datasharingframework/bpe:1.5.0 + image: ghcr.io/datasharingframework/bpe:1.5.1 restart: "no" ports: - 127.0.0.1:5003:5003 @@ -232,7 +232,7 @@ services: dts-fhir: - image: ghcr.io/datasharingframework/fhir:1.5.0 + image: ghcr.io/datasharingframework/fhir:1.5.1 restart: "no" ports: - 127.0.0.1:5001:5001 @@ -293,7 +293,7 @@ services: - db - proxy dts-bpe: - image: ghcr.io/datasharingframework/bpe:1.5.0 + image: ghcr.io/datasharingframework/bpe:1.5.1 restart: "no" ports: - 127.0.0.1:5004:5004 @@ -356,7 +356,7 @@ services: crr-fhir: - image: ghcr.io/datasharingframework/fhir:1.5.0 + image: ghcr.io/datasharingframework/fhir:1.5.1 restart: "no" ports: - 127.0.0.1:5002:5002 @@ -417,7 +417,7 @@ services: - db - proxy crr-bpe: - image: ghcr.io/datasharingframework/bpe:1.5.0 + image: ghcr.io/datasharingframework/bpe:1.5.1 restart: "no" ports: - 127.0.0.1:5005:5005 diff --git a/pom.xml b/pom.xml index c3a6c4e..97842bc 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ ${project.basedir} 5.1.0 - 1.5.0 + 1.5.1 Business processes for the NUM RDP project (AP1) as plugins for the Data Sharing Framework.