diff options
8 files changed, 122 insertions, 7 deletions
@@ -112,9 +112,14 @@ Ab Version 2025.1 (Multi-Pseudonym Support) * `APP_PSEUDONYMIZE_GPAS_USERNAME`: gPas Basic-Auth Benutzername * `APP_PSEUDONYMIZE_GPAS_PASSWORD`: gPas Basic-Auth Passwort * `APP_PSEUDONYMIZE_GPAS_PID_DOMAIN`: gPas Domänenname für Patienten ID -* `APP_PSEUDONYMIZE_GPAS_GENOM_DE_TAN_DOMAIN`: gPas Multi-Pseudonym-Domäne für genomDE Vorgangsnummern ( +* `APP_PSEUDONYMIZE_GPAS_GENOM_DE_TAN_DOMAIN`: gPAS Multi-Pseudonym-Domäne für genomDE Vorgangsnummern ( Clinical data node) +Soll anstelle der REST-Schnittstelle von gPAS die SOAP-Schnittstelle verwendet werden, +so ist nicht die URI der gPAS-Instanz anzugeben, sondern der SOAP-Endpoint: + +* `APP_PSEUDONYMIZE_GPAS_SOAP_ENDPOINT`: SOAP-Endpoint der gPAS-Instanz (e.g. http://127.0.0.1:9990/gpas/gpasService) + ### (Externe) Consent-Services Consent-Services können konfiguriert werden. diff --git a/build.gradle.kts b/build.gradle.kts index 6a23aaf..738ed54 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -19,6 +19,7 @@ version = "0.12.0-SNAPSHOT" var versions = mapOf( "mtb-dto" to "0.1.0-SNAPSHOT", "hapi-fhir" to "8.4.0", + "apache-cxf" to "4.1.3", "mockito-kotlin" to "6.0.0", "archunit" to "1.4.1", // Webjars @@ -91,6 +92,9 @@ dependencies { implementation("org.webjars.npm:htmx.org:${versions["htmx.org"]}") // Fix for CVE-2025-48924 implementation("org.apache.commons:commons-lang3:3.18.0") + // gPAS via Soap + implementation("org.apache.cxf:cxf-rt-frontend-jaxws:${versions["apache-cxf"]}") + implementation("org.apache.cxf:cxf-rt-transports-http:${versions["apache-cxf"]}") runtimeOnly("org.mariadb.jdbc:mariadb-java-client") runtimeOnly("org.postgresql:postgresql") diff --git a/src/integrationTest/kotlin/dev/dnpm/etl/processor/config/AppConfigurationTest.kt b/src/integrationTest/kotlin/dev/dnpm/etl/processor/config/AppConfigurationTest.kt index 66b62c8..5e25428 100644 --- a/src/integrationTest/kotlin/dev/dnpm/etl/processor/config/AppConfigurationTest.kt +++ b/src/integrationTest/kotlin/dev/dnpm/etl/processor/config/AppConfigurationTest.kt @@ -29,6 +29,7 @@ import dev.dnpm.etl.processor.output.KafkaMtbFileSender import dev.dnpm.etl.processor.output.RestMtbFileSender import dev.dnpm.etl.processor.pseudonym.AnonymizingGenerator import dev.dnpm.etl.processor.pseudonym.GpasPseudonymGenerator +import dev.dnpm.etl.processor.pseudonym.GpasSoapPseudonymGenerator import dev.dnpm.etl.processor.security.TokenRepository import dev.dnpm.etl.processor.security.TokenService import dev.dnpm.etl.processor.services.RequestProcessor @@ -201,7 +202,8 @@ class AppConfigurationTest { @Nested @TestPropertySource( properties = [ - "app.pseudonymize.generator=gpas" + "app.pseudonymize.generator=gpas", + "app.pseudonymize.gpas.uri=http://localhost/" ] ) inner class AppConfigurationPseudonymizeGeneratorGpasTest(private val context: ApplicationContext) { @@ -216,6 +218,22 @@ class AppConfigurationTest { @Nested @TestPropertySource( properties = [ + "app.pseudonymize.generator=gpas", + "app.pseudonymize.gpas.soap-endpoint=http://localhost/" + ] + ) + inner class AppConfigurationPseudonymizeGeneratorGpasSoapTest(private val context: ApplicationContext) { + + @Test + fun shouldUseConfiguredGenerator() { + assertThat(context.getBean(GpasSoapPseudonymGenerator::class.java)).isNotNull + } + + } + + @Nested + @TestPropertySource( + properties = [ "app.security.enable-tokens=true" ] ) diff --git a/src/integrationTest/kotlin/dev/dnpm/etl/processor/pseudonym/GpasPseudonymGeneratorTest.kt b/src/integrationTest/kotlin/dev/dnpm/etl/processor/pseudonym/GpasPseudonymGeneratorTest.kt index c2a8ba6..10f2359 100644 --- a/src/integrationTest/kotlin/dev/dnpm/etl/processor/pseudonym/GpasPseudonymGeneratorTest.kt +++ b/src/integrationTest/kotlin/dev/dnpm/etl/processor/pseudonym/GpasPseudonymGeneratorTest.kt @@ -50,6 +50,7 @@ class GpasPseudonymGeneratorTest { val retryTemplate = RetryTemplateBuilder().customPolicy(SimpleRetryPolicy(1)).build() val gPasConfigProperties = GPasConfigProperties( "https://localhost:9990/ttp-fhir/fhir/gpas", + null, "test", "test2", null, null diff --git a/src/main/kotlin/dev/dnpm/etl/processor/config/AppConfigProperties.kt b/src/main/kotlin/dev/dnpm/etl/processor/config/AppConfigProperties.kt index 395dbd2..fc0727f 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/config/AppConfigProperties.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/config/AppConfigProperties.kt @@ -47,6 +47,7 @@ data class PseudonymizeConfigProperties( @ConfigurationProperties(GPasConfigProperties.NAME) data class GPasConfigProperties( val uri: String?, + val soapEndpoint: String?, val patientDomain: String = "etl-processor", val genomDeTanDomain: String = "ccdn", val username: String?, diff --git a/src/main/kotlin/dev/dnpm/etl/processor/config/AppConfiguration.kt b/src/main/kotlin/dev/dnpm/etl/processor/config/AppConfiguration.kt index b4fad3e..de302fd 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/config/AppConfiguration.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/config/AppConfiguration.kt @@ -20,19 +20,17 @@ package dev.dnpm.etl.processor.config import com.fasterxml.jackson.databind.ObjectMapper -import dev.dnpm.etl.processor.consent.MtbFileConsentService import dev.dnpm.etl.processor.consent.GicsConsentService import dev.dnpm.etl.processor.consent.IConsentService +import dev.dnpm.etl.processor.consent.MtbFileConsentService import dev.dnpm.etl.processor.monitoring.* -import dev.dnpm.etl.processor.pseudonym.AnonymizingGenerator -import dev.dnpm.etl.processor.pseudonym.Generator -import dev.dnpm.etl.processor.pseudonym.GpasPseudonymGenerator -import dev.dnpm.etl.processor.pseudonym.PseudonymizeService +import dev.dnpm.etl.processor.pseudonym.* import dev.dnpm.etl.processor.security.TokenRepository import dev.dnpm.etl.processor.security.TokenService import dev.dnpm.etl.processor.services.ConsentProcessor import dev.dnpm.etl.processor.services.Transformation import dev.dnpm.etl.processor.services.TransformationService +import org.apache.cxf.jaxws.JaxWsProxyFactoryBean import org.slf4j.LoggerFactory import org.springframework.boot.autoconfigure.condition.AnyNestedCondition import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean @@ -85,6 +83,37 @@ class AppConfiguration { } @ConditionalOnProperty(value = ["app.pseudonymize.generator"], havingValue = "GPAS") + @ConditionalOnProperty(value = ["app.pseudonymize.gpas.soap-endpoint"]) + @Bean + fun gpasSoapProxyFactoryBean(gpasConfigProperties: GPasConfigProperties): JaxWsProxyFactoryBean { + val proxyFactory = JaxWsProxyFactoryBean() + proxyFactory.serviceClass = GpasSoapService::class.java + proxyFactory.address = gpasConfigProperties.soapEndpoint + return proxyFactory + } + + @ConditionalOnProperty(value = ["app.pseudonymize.generator"], havingValue = "GPAS") + @ConditionalOnProperty(value = ["app.pseudonymize.gpas.soap-endpoint"]) + @Bean + fun gpasSoapProxy(gpasConfigProperties: GPasConfigProperties): GpasSoapService { + return gpasSoapProxyFactoryBean(gpasConfigProperties).create() as GpasSoapService + } + + @ConditionalOnProperty(value = ["app.pseudonymize.generator"], havingValue = "GPAS") + @ConditionalOnProperty(value = ["app.pseudonymize.gpas.soap-endpoint"]) + @Bean + fun gpasSoapPseudonymGenerator( + configProperties: GPasConfigProperties, + retryTemplate: RetryTemplate, + gpasSoapService: GpasSoapService, + appFhirConfig: AppFhirConfig + ): Generator { + logger.info("Selected 'GpasSoapPseudonym Generator'") + return GpasSoapPseudonymGenerator(configProperties, retryTemplate, gpasSoapService, appFhirConfig) + } + + @ConditionalOnProperty(value = ["app.pseudonymize.generator"], havingValue = "GPAS") + @ConditionalOnProperty(value = ["app.pseudonymize.gpas.uri"]) @Bean fun gpasPseudonymGenerator( configProperties: GPasConfigProperties, diff --git a/src/main/kotlin/dev/dnpm/etl/processor/pseudonym/GpasSoapPseudonymGenerator.kt b/src/main/kotlin/dev/dnpm/etl/processor/pseudonym/GpasSoapPseudonymGenerator.kt new file mode 100644 index 0000000..8215d23 --- /dev/null +++ b/src/main/kotlin/dev/dnpm/etl/processor/pseudonym/GpasSoapPseudonymGenerator.kt @@ -0,0 +1,26 @@ +package dev.dnpm.etl.processor.pseudonym + +import dev.dnpm.etl.processor.config.AppFhirConfig +import dev.dnpm.etl.processor.config.GPasConfigProperties +import org.springframework.retry.support.RetryTemplate + +class GpasSoapPseudonymGenerator( + private val gpasCfg: GPasConfigProperties, + private val retryTemplate: RetryTemplate, + private val gpasSoapService: GpasSoapService, + private val appFhirConfig: AppFhirConfig +) : Generator { + + override fun generate(id: String): String { + return retryTemplate.execute<String, Exception> { + gpasSoapService.getOrCreatePseudonymFor(id, gpasCfg.patientDomain) + } + } + + override fun generateGenomDeTan(id: String): String { + return retryTemplate.execute<String, Exception> { + gpasSoapService.createPseudonymsFor(id, gpasCfg.genomDeTanDomain, 1).first() + } + } +} + diff --git a/src/main/kotlin/dev/dnpm/etl/processor/pseudonym/GpasSoapService.kt b/src/main/kotlin/dev/dnpm/etl/processor/pseudonym/GpasSoapService.kt new file mode 100644 index 0000000..0909924 --- /dev/null +++ b/src/main/kotlin/dev/dnpm/etl/processor/pseudonym/GpasSoapService.kt @@ -0,0 +1,31 @@ +package dev.dnpm.etl.processor.pseudonym + +import jakarta.jws.WebMethod +import jakarta.jws.WebParam +import jakarta.jws.WebResult +import jakarta.jws.WebService +import jakarta.xml.bind.annotation.XmlElementWrapper + +@WebService( + name = "PSNManagerBeanService", + targetNamespace ="http://psn.ttp.ganimed.icmvc.emau.org/" +) +interface GpasSoapService { + + @WebMethod(operationName = "getOrCreatePseudonymFor") + @WebResult(name = "psn") + fun getOrCreatePseudonymFor( + @WebParam(name = "value") value: String, + @WebParam(name = "domainName") domainName: String + ): String + + @WebMethod(operationName = "createPseudonymsFor") + @WebResult(name = "psn") + @XmlElementWrapper(name = "return") + fun createPseudonymsFor( + @WebParam(name = "value") value: String, + @WebParam(name = "domainName") domainName: String, + @WebParam(name = "number") minNumber: Int + ): List<String> + +}
\ No newline at end of file |
