diff --git a/README.md b/README.md index ac7742d..d966358 100644 --- a/README.md +++ b/README.md @@ -52,8 +52,6 @@ Soll noch das alte bwHC-Backend verwendet werden, so ist die Umgebungsvariable In Versionen des ETL-Processors **nach Version 0.10** werden die folgenden Konfigurationsoptionen entfernt: -* `APP_PSEUDONYMIZE_GPAS_SSLCALOCATION`: Nutzen Sie hier, wie unter [_Integration eines eigenen Root CA - Zertifikats_](#integration-eines-eigenen-root-ca-zertifikats) beschrieben, das Einbinden eigener Zertifikate. * `APP_KAFKA_TOPIC`: Nutzen Sie nun die Konfigurationsoption `APP_KAFKA_OUTPUT_TOPIC` * `APP_KAFKA_RESPONSE_TOPIC`: Nutzen Sie nun die Konfigurationsoption `APP_KAFKA_OUTPUT_RESPONSE_TOPIC` @@ -90,13 +88,6 @@ Wurde die Verwendung von gPAS konfiguriert, so sind weitere Angaben zu konfiguri * `APP_PSEUDONYMIZE_GPAS_TARGET`: gPas Domänenname * `APP_PSEUDONYMIZE_GPAS_USERNAME`: gPas Basic-Auth Benutzername * `APP_PSEUDONYMIZE_GPAS_PASSWORD`: gPas Basic-Auth Passwort -* ~~`APP_PSEUDONYMIZE_GPAS_SSLCALOCATION`~~: **Veraltet** - Root Zertifikat für gPas, falls es dediziert hinzugefügt werden muss. - **Wird in nach Version 0.10 entfernt** - -Der Konfigurationsparameter `APP_PSEUDONYMIZE_GPAS_SSLCALOCATION` sollte nicht mehr verwendet werden und wird nach -Version 0.10 entfernt. -Stattdessen sollte das Root Zertifikat wie unter [_Integration eines eigenen Root CA -Zertifikats_](#integration-eines-eigenen-root-ca-zertifikats) beschrieben eingebunden werden. ### Anmeldung mit einem Passwort 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 da0c55c..2e539e9 100644 --- a/src/integrationTest/kotlin/dev/dnpm/etl/processor/pseudonym/GpasPseudonymGeneratorTest.kt +++ b/src/integrationTest/kotlin/dev/dnpm/etl/processor/pseudonym/GpasPseudonymGeneratorTest.kt @@ -1,7 +1,7 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2024 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2025 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -47,10 +47,9 @@ class GpasPseudonymGeneratorTest { fun setup() { val retryTemplate = RetryTemplateBuilder().customPolicy(SimpleRetryPolicy(1)).build() val gPasConfigProperties = GPasConfigProperties( - "http://localhost/ttp-fhir/fhir/gpas/\$pseudonymizeAllowCreate", + "https://localhost/ttp-fhir/fhir/gpas/\$pseudonymizeAllowCreate", "test", null, - null, null ) @@ -63,7 +62,7 @@ class GpasPseudonymGeneratorTest { fun shouldReturnExpectedPseudonym() { this.mockRestServiceServer.expect { method(HttpMethod.POST) - requestTo("http://localhost/ttp-fhir/fhir/gpas/\$pseudonymizeAllowCreate") + requestTo("https://localhost/ttp-fhir/fhir/gpas/\$pseudonymizeAllowCreate") }.andRespond { withStatus(HttpStatus.OK).body(getDummyResponseBody("1234", "test", "test1234ABCDEF567890")) .createResponse(it) @@ -76,7 +75,7 @@ class GpasPseudonymGeneratorTest { fun shouldThrowExceptionIfGpasNotAvailable() { this.mockRestServiceServer.expect { method(HttpMethod.POST) - requestTo("http://localhost/ttp-fhir/fhir/gpas/\$pseudonymizeAllowCreate") + requestTo("https://localhost/ttp-fhir/fhir/gpas/\$pseudonymizeAllowCreate") }.andRespond { withException(IOException("Simulated IO error")).createResponse(it) } @@ -88,7 +87,7 @@ class GpasPseudonymGeneratorTest { fun shouldThrowExceptionIfGpasDoesNotReturn2xxResponse() { this.mockRestServiceServer.expect { method(HttpMethod.POST) - requestTo("http://localhost/ttp-fhir/fhir/gpas/\$pseudonymizeAllowCreate") + requestTo("https://localhost/ttp-fhir/fhir/gpas/\$pseudonymizeAllowCreate") }.andRespond { withStatus(HttpStatus.FOUND) .header(HttpHeaders.LOCATION, "https://localhost/ttp-fhir/fhir/gpas/\$pseudonymizeAllowCreate") 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 7c192c8..7a077c3 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/config/AppConfigProperties.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/config/AppConfigProperties.kt @@ -1,7 +1,7 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2024 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2025 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -56,10 +56,6 @@ data class GPasConfigProperties( val target: String = "etl-processor", val username: String?, val password: String?, - @get:DeprecatedConfigurationProperty( - reason = "Deprecated in favor of including Root CA" - ) - val sslCaLocation: String? ) { companion object { const val NAME = "app.pseudonymize.gpas" 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 66af288..9002d15 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/config/AppConfiguration.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/config/AppConfiguration.kt @@ -1,7 +1,7 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2024 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2025 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -32,12 +32,6 @@ import dev.dnpm.etl.processor.security.TokenRepository import dev.dnpm.etl.processor.security.TokenService import dev.dnpm.etl.processor.services.Transformation import dev.dnpm.etl.processor.services.TransformationService -import org.apache.hc.client5.http.impl.classic.HttpClients -import org.apache.hc.client5.http.impl.io.BasicHttpClientConnectionManager -import org.apache.hc.client5.http.socket.ConnectionSocketFactory -import org.apache.hc.client5.http.socket.PlainConnectionSocketFactory -import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory -import org.apache.hc.core5.http.config.RegistryBuilder import org.slf4j.LoggerFactory import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty @@ -45,7 +39,6 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.data.jdbc.repository.config.AbstractJdbcConfiguration -import org.springframework.http.client.HttpComponentsClientHttpRequestFactory import org.springframework.retry.RetryCallback import org.springframework.retry.RetryContext import org.springframework.retry.RetryListener @@ -58,13 +51,6 @@ import org.springframework.security.provisioning.InMemoryUserDetailsManager import org.springframework.web.client.HttpClientErrorException import org.springframework.web.client.RestTemplate import reactor.core.publisher.Sinks -import java.io.BufferedInputStream -import java.io.FileInputStream -import java.security.KeyStore -import java.security.cert.CertificateFactory -import java.security.cert.X509Certificate -import javax.net.ssl.SSLContext -import javax.net.ssl.TrustManagerFactory import kotlin.time.Duration.Companion.seconds import kotlin.time.toJavaDuration @@ -90,18 +76,6 @@ class AppConfiguration { @ConditionalOnProperty(value = ["app.pseudonymize.generator"], havingValue = "GPAS") @Bean fun gpasPseudonymGenerator(configProperties: GPasConfigProperties, retryTemplate: RetryTemplate, restTemplate: RestTemplate): Generator { - try { - if (!configProperties.sslCaLocation.isNullOrBlank()) { - return GpasPseudonymGenerator( - configProperties, - retryTemplate, - createCustomGpasRestTemplate(configProperties) - ) - } - } catch (e: Exception) { - throw RuntimeException(e) - } - return GpasPseudonymGenerator(configProperties, retryTemplate, restTemplate) } @@ -115,81 +89,9 @@ class AppConfiguration { @ConditionalOnMissingBean @Bean fun gpasPseudonymGeneratorOnDeprecatedProperty(configProperties: GPasConfigProperties, retryTemplate: RetryTemplate, restTemplate: RestTemplate): Generator { - try { - if (!configProperties.sslCaLocation.isNullOrBlank()) { - return GpasPseudonymGenerator( - configProperties, - retryTemplate, - createCustomGpasRestTemplate(configProperties) - ) - } - } catch (e: Exception) { - throw RuntimeException(e) - } - return GpasPseudonymGenerator(configProperties, retryTemplate, restTemplate) } - private fun createCustomGpasRestTemplate(configProperties: GPasConfigProperties): RestTemplate { - fun getSslContext(certificateLocation: String): SSLContext? { - val ks = KeyStore.getInstance(KeyStore.getDefaultType()) - - val fis = FileInputStream(certificateLocation) - val ca = CertificateFactory.getInstance("X.509") - .generateCertificate(BufferedInputStream(fis)) as X509Certificate - - ks.load(null, null) - ks.setCertificateEntry(1.toString(), ca) - - val tmf = TrustManagerFactory.getInstance( - TrustManagerFactory.getDefaultAlgorithm() - ) - tmf.init(ks) - - val sslContext = SSLContext.getInstance("TLS") - sslContext.init(null, tmf.trustManagers, null) - - return sslContext - } - - fun getCustomRestTemplate(customSslContext: SSLContext): RestTemplate { - val sslsf = SSLConnectionSocketFactory(customSslContext) - val socketFactoryRegistry = RegistryBuilder.create() - .register("https", sslsf).register("http", PlainConnectionSocketFactory()).build() - - val connectionManager = BasicHttpClientConnectionManager( - socketFactoryRegistry - ) - val httpClient = HttpClients.custom() - .setConnectionManager(connectionManager).build() - - val requestFactory = HttpComponentsClientHttpRequestFactory( - httpClient - ) - return RestTemplate(requestFactory) - } - - try { - if (!configProperties.sslCaLocation.isNullOrBlank()) { - val customSslContext = getSslContext(configProperties.sslCaLocation) - logger.warn( - String.format( - "%s has been initialized with SSL certificate %s. This is deprecated in favor of including Root CA.", - this.javaClass.name, configProperties.sslCaLocation - ) - ) - - if (customSslContext != null) { - return getCustomRestTemplate(customSslContext) - } - } - } catch (e: Exception) { - throw RuntimeException(e) - } - - throw RuntimeException("Custom SSL configuration for gPAS not usable") - } - @ConditionalOnProperty(value = ["app.pseudonymizer"], havingValue = "BUILDIN") @ConditionalOnMissingBean @Bean