From e3737c977f24f161e416101a5384def7fd617957 Mon Sep 17 00:00:00 2001 From: kcichonczyk Date: Sun, 12 Feb 2023 21:45:09 +0100 Subject: [PATCH 01/11] [ACS-4460] introduce mTLS when communicating with SFS (T-Engines communicating with SFS) --- engines/base/pom.xml | 1 - .../transform/base/config/MTLSConfig.java | 172 ++++++++++++++++++ .../base/config/WebApplicationConfig.java | 7 - .../base/sfs/SharedFileStoreClient.java | 15 +- 4 files changed, 173 insertions(+), 22 deletions(-) create mode 100644 engines/base/src/main/java/org/alfresco/transform/base/config/MTLSConfig.java diff --git a/engines/base/pom.xml b/engines/base/pom.xml index c6f504b5..675b40ac 100644 --- a/engines/base/pom.xml +++ b/engines/base/pom.xml @@ -90,7 +90,6 @@ org.apache.httpcomponents httpclient - test org.apache.httpcomponents diff --git a/engines/base/src/main/java/org/alfresco/transform/base/config/MTLSConfig.java b/engines/base/src/main/java/org/alfresco/transform/base/config/MTLSConfig.java new file mode 100644 index 00000000..56777971 --- /dev/null +++ b/engines/base/src/main/java/org/alfresco/transform/base/config/MTLSConfig.java @@ -0,0 +1,172 @@ +/* + * #%L + * Alfresco Transform Core + * %% + * Copyright (C) 2005 - 2023 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * - + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * - + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * - + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * - + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.transform.base.config; + +import io.netty.handler.ssl.SslContext; +import io.netty.handler.ssl.SslContextBuilder; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.ssl.SSLContextBuilder; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.Resource; +import org.springframework.http.client.ClientHttpRequestFactory; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.http.client.reactive.ReactorClientHttpConnector; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.netty.http.client.HttpClient; + +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManagerFactory; +import java.io.IOException; +import java.io.InputStream; +import java.security.KeyManagementException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; + +import static org.springframework.http.HttpHeaders.ACCEPT; +import static org.springframework.http.HttpHeaders.CONTENT_TYPE; +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; + +@Configuration +public class MTLSConfig { + + @Value("${filestore-url}") + private String url; + + @Value("${server.ssl.enabled}") + boolean sslEnabled; + + @Value("${server.ssl.key.store}") + private Resource keyStoreResource; + + //TODO: use some hashing algorithm + @Value("${server.ssl.key.password}") + private char[] keyPassword; + + //TODO: use some hashing algorithm + @Value("${server.ssl.key.store.password}") + private char[] keyStorePassword; + + @Value("${server.ssl.key.store.type}") + private String keyStoreType; + + @Value("${server.ssl.trust.store}") + private Resource trustStoreResource; + + //TODO: use some hashing algorithm + @Value("${server.ssl.trust.store.password}") + private char[] trustStorePassword; + + @Value("${server.ssl.trust.store.type}") + private String trustStoreType; + + @Bean + public WebClient client() throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException, UnrecoverableKeyException + { + if(sslEnabled) + { + HttpClient httpClient = getHttpClientWithMTLS(); + + return WebClient.builder().baseUrl(url.endsWith("/") ? url : url + "/") + .defaultHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE) + .defaultHeader(ACCEPT, APPLICATION_JSON_VALUE) + .clientConnector(new ReactorClientHttpConnector(httpClient)) + .build(); + } else { + return WebClient.builder().baseUrl(url.endsWith("/") ? url : url + "/") + .defaultHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE) + .defaultHeader(ACCEPT, APPLICATION_JSON_VALUE) + .build(); + } + } + + private HttpClient getHttpClientWithMTLS() throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException { + KeyManagerFactory keyManagerFactory = initKeyManagerFactory(); + TrustManagerFactory trustManagerFactory = initTrustManagerFactory(); + + SslContext sslContext = SslContextBuilder.forClient() + .trustManager(trustManagerFactory) + .keyManager(keyManagerFactory) + .build(); + + HttpClient httpClient = HttpClient.create().secure(p -> p.sslContext(sslContext)); + return httpClient; + } + + private TrustManagerFactory initTrustManagerFactory() throws NoSuchAlgorithmException, KeyStoreException, IOException, CertificateException { + TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + KeyStore trustStore = getKeyStore(trustStoreType, trustStoreResource, trustStorePassword); + trustManagerFactory.init(trustStore); + return trustManagerFactory; + } + + private KeyManagerFactory initKeyManagerFactory() throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException { + KeyStore clientKeyStore = getKeyStore(keyStoreType, keyStoreResource, keyStorePassword); + KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(keyStoreType); + keyManagerFactory.init(clientKeyStore, keyPassword); + return keyManagerFactory; + } + + private KeyStore getKeyStore(String keyStoreType, Resource keyStoreResource, char[] keyStorePassword) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException { + KeyStore keyStore = KeyStore.getInstance(keyStoreType); + try (InputStream keyStoreInputStream = keyStoreResource.getInputStream()) { + keyStore.load(keyStoreInputStream, keyStorePassword); + } + return keyStore; + } + + @Bean + public RestTemplate restTemplate() throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException, UnrecoverableKeyException { + if(sslEnabled) + { + return getRestTemplateWithMTLS(); + } else { + return new RestTemplate(); + } + } + + private RestTemplate getRestTemplateWithMTLS() throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, KeyManagementException, UnrecoverableKeyException { + KeyStore keyStore = getKeyStore(keyStoreType, keyStoreResource, keyStorePassword); + SSLContext sslContext = new SSLContextBuilder() + .loadKeyMaterial(keyStore, keyPassword) + .loadTrustMaterial(trustStoreResource.getURL(), trustStorePassword) + .build(); + + SSLConnectionSocketFactory sslContextFactory = new SSLConnectionSocketFactory(sslContext); + CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(sslContextFactory).build(); + ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient); + return new RestTemplate(requestFactory); + } +} diff --git a/engines/base/src/main/java/org/alfresco/transform/base/config/WebApplicationConfig.java b/engines/base/src/main/java/org/alfresco/transform/base/config/WebApplicationConfig.java index 4ffb51e3..57f61390 100644 --- a/engines/base/src/main/java/org/alfresco/transform/base/config/WebApplicationConfig.java +++ b/engines/base/src/main/java/org/alfresco/transform/base/config/WebApplicationConfig.java @@ -35,7 +35,6 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.FilterType; -import org.springframework.web.client.RestTemplate; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @@ -64,12 +63,6 @@ public class WebApplicationConfig implements WebMvcConfigurer .addPathPatterns(ENDPOINT_TRANSFORM, "/live", "/ready"); } - @Bean - public RestTemplate restTemplate() - { - return new RestTemplate(); - } - @Bean public TransformRequestValidator transformRequestValidator() { diff --git a/engines/base/src/main/java/org/alfresco/transform/base/sfs/SharedFileStoreClient.java b/engines/base/src/main/java/org/alfresco/transform/base/sfs/SharedFileStoreClient.java index 2772a35a..13e4b098 100644 --- a/engines/base/src/main/java/org/alfresco/transform/base/sfs/SharedFileStoreClient.java +++ b/engines/base/src/main/java/org/alfresco/transform/base/sfs/SharedFileStoreClient.java @@ -26,10 +26,7 @@ */ package org.alfresco.transform.base.sfs; -import static org.springframework.http.HttpHeaders.ACCEPT; -import static org.springframework.http.HttpHeaders.CONTENT_TYPE; import static org.springframework.http.HttpMethod.POST; -import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; import static org.springframework.http.MediaType.MULTIPART_FORM_DATA; import java.io.File; @@ -52,8 +49,6 @@ import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.RestTemplate; import org.springframework.web.reactive.function.client.WebClient; -import javax.annotation.PostConstruct; - /** * Simple Rest client that call Alfresco Shared File Store */ @@ -68,17 +63,9 @@ public class SharedFileStoreClient @Autowired private RestTemplate restTemplate; + @Autowired private WebClient client; - @PostConstruct - public void init() - { - client = WebClient.builder().baseUrl(url.endsWith("/") ? url : url + "/") - .defaultHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE) - .defaultHeader(ACCEPT, APPLICATION_JSON_VALUE) - .build(); - } - /** * Retrieves a file from Shared File Store using given file reference * From 139a18f8ac51e402f03e7d626851a6556487289c Mon Sep 17 00:00:00 2001 From: kcichonczyk Date: Mon, 13 Feb 2023 19:27:18 +0100 Subject: [PATCH 02/11] [ACS-4460] introduce mTLS when communicating with SFS (T-Engines communicating with SFS), added WebClient Builder as a bean --- .../transform/base/config/MTLSConfig.java | 66 ++++++++++--------- 1 file changed, 35 insertions(+), 31 deletions(-) diff --git a/engines/base/src/main/java/org/alfresco/transform/base/config/MTLSConfig.java b/engines/base/src/main/java/org/alfresco/transform/base/config/MTLSConfig.java index 56777971..69cf3fae 100644 --- a/engines/base/src/main/java/org/alfresco/transform/base/config/MTLSConfig.java +++ b/engines/base/src/main/java/org/alfresco/transform/base/config/MTLSConfig.java @@ -65,54 +65,53 @@ public class MTLSConfig { @Value("${filestore-url}") private String url; - @Value("${server.ssl.enabled}") + @Value("${server.ssl.enabled:false}") boolean sslEnabled; - @Value("${server.ssl.key.store}") + @Value("${server.ssl.key.store:}") private Resource keyStoreResource; - //TODO: use some hashing algorithm - @Value("${server.ssl.key.password}") + @Value("${server.ssl.key.password:}") private char[] keyPassword; - //TODO: use some hashing algorithm - @Value("${server.ssl.key.store.password}") + @Value("${server.ssl.key.store.password:}") private char[] keyStorePassword; - @Value("${server.ssl.key.store.type}") + @Value("${server.ssl.key.store.type:}") private String keyStoreType; - @Value("${server.ssl.trust.store}") + @Value("${server.ssl.trust.store:}") private Resource trustStoreResource; - //TODO: use some hashing algorithm - @Value("${server.ssl.trust.store.password}") + @Value("${server.ssl.trust.store.password:}") private char[] trustStorePassword; - @Value("${server.ssl.trust.store.type}") + @Value("${server.ssl.trust.store.type:}") private String trustStoreType; @Bean - public WebClient client() throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException, UnrecoverableKeyException + public WebClient client(WebClient.Builder clientBuilder) + { + return clientBuilder.baseUrl(url.endsWith("/") ? url : url + "/") + .defaultHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE) + .defaultHeader(ACCEPT, APPLICATION_JSON_VALUE) + .build(); + } + + @Bean + public WebClient.Builder clientBuilder() throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException, UnrecoverableKeyException { if(sslEnabled) { HttpClient httpClient = getHttpClientWithMTLS(); - - return WebClient.builder().baseUrl(url.endsWith("/") ? url : url + "/") - .defaultHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE) - .defaultHeader(ACCEPT, APPLICATION_JSON_VALUE) - .clientConnector(new ReactorClientHttpConnector(httpClient)) - .build(); + return WebClient.builder().clientConnector(new ReactorClientHttpConnector(httpClient)); } else { - return WebClient.builder().baseUrl(url.endsWith("/") ? url : url + "/") - .defaultHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE) - .defaultHeader(ACCEPT, APPLICATION_JSON_VALUE) - .build(); + return WebClient.builder(); } } - private HttpClient getHttpClientWithMTLS() throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException { + private HttpClient getHttpClientWithMTLS() throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException + { KeyManagerFactory keyManagerFactory = initKeyManagerFactory(); TrustManagerFactory trustManagerFactory = initTrustManagerFactory(); @@ -121,34 +120,38 @@ public class MTLSConfig { .keyManager(keyManagerFactory) .build(); - HttpClient httpClient = HttpClient.create().secure(p -> p.sslContext(sslContext)); - return httpClient; + return HttpClient.create().secure(p -> p.sslContext(sslContext)); } - private TrustManagerFactory initTrustManagerFactory() throws NoSuchAlgorithmException, KeyStoreException, IOException, CertificateException { + private TrustManagerFactory initTrustManagerFactory() throws NoSuchAlgorithmException, KeyStoreException, IOException, CertificateException + { TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); KeyStore trustStore = getKeyStore(trustStoreType, trustStoreResource, trustStorePassword); trustManagerFactory.init(trustStore); return trustManagerFactory; } - private KeyManagerFactory initKeyManagerFactory() throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException { + private KeyManagerFactory initKeyManagerFactory() throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException + { KeyStore clientKeyStore = getKeyStore(keyStoreType, keyStoreResource, keyStorePassword); KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(keyStoreType); keyManagerFactory.init(clientKeyStore, keyPassword); return keyManagerFactory; } - private KeyStore getKeyStore(String keyStoreType, Resource keyStoreResource, char[] keyStorePassword) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException { + private KeyStore getKeyStore(String keyStoreType, Resource keyStoreResource, char[] keyStorePassword) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException + { KeyStore keyStore = KeyStore.getInstance(keyStoreType); - try (InputStream keyStoreInputStream = keyStoreResource.getInputStream()) { + try (InputStream keyStoreInputStream = keyStoreResource.getInputStream()) + { keyStore.load(keyStoreInputStream, keyStorePassword); } return keyStore; } @Bean - public RestTemplate restTemplate() throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException, UnrecoverableKeyException { + public RestTemplate restTemplate() throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException, UnrecoverableKeyException + { if(sslEnabled) { return getRestTemplateWithMTLS(); @@ -157,7 +160,8 @@ public class MTLSConfig { } } - private RestTemplate getRestTemplateWithMTLS() throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, KeyManagementException, UnrecoverableKeyException { + private RestTemplate getRestTemplateWithMTLS() throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, KeyManagementException, UnrecoverableKeyException + { KeyStore keyStore = getKeyStore(keyStoreType, keyStoreResource, keyStorePassword); SSLContext sslContext = new SSLContextBuilder() .loadKeyMaterial(keyStore, keyPassword) From 0af53e93f3fd191310915971922069f4f89ad432 Mon Sep 17 00:00:00 2001 From: kcichonczyk Date: Tue, 14 Feb 2023 16:26:00 +0100 Subject: [PATCH 03/11] [ACS-4460] introduce mTLS, config fix --- .../alfresco/transform/base/config/MTLSConfig.java | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/engines/base/src/main/java/org/alfresco/transform/base/config/MTLSConfig.java b/engines/base/src/main/java/org/alfresco/transform/base/config/MTLSConfig.java index 69cf3fae..2ccc4739 100644 --- a/engines/base/src/main/java/org/alfresco/transform/base/config/MTLSConfig.java +++ b/engines/base/src/main/java/org/alfresco/transform/base/config/MTLSConfig.java @@ -99,8 +99,7 @@ public class MTLSConfig { } @Bean - public WebClient.Builder clientBuilder() throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException, UnrecoverableKeyException - { + public WebClient.Builder clientBuilder() throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException, UnrecoverableKeyException { if(sslEnabled) { HttpClient httpClient = getHttpClientWithMTLS(); @@ -110,8 +109,7 @@ public class MTLSConfig { } } - private HttpClient getHttpClientWithMTLS() throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException - { + private HttpClient getHttpClientWithMTLS() throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException { KeyManagerFactory keyManagerFactory = initKeyManagerFactory(); TrustManagerFactory trustManagerFactory = initTrustManagerFactory(); @@ -125,16 +123,15 @@ public class MTLSConfig { private TrustManagerFactory initTrustManagerFactory() throws NoSuchAlgorithmException, KeyStoreException, IOException, CertificateException { - TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); KeyStore trustStore = getKeyStore(trustStoreType, trustStoreResource, trustStorePassword); + TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); trustManagerFactory.init(trustStore); return trustManagerFactory; } - private KeyManagerFactory initKeyManagerFactory() throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException - { + private KeyManagerFactory initKeyManagerFactory() throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException { KeyStore clientKeyStore = getKeyStore(keyStoreType, keyStoreResource, keyStorePassword); - KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(keyStoreType); + KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); keyManagerFactory.init(clientKeyStore, keyPassword); return keyManagerFactory; } From 149f3a500cff796bceca8f2945ef822f9b209d9b Mon Sep 17 00:00:00 2001 From: kcichonczyk Date: Mon, 20 Feb 2023 16:53:07 +0100 Subject: [PATCH 04/11] [ACS-4460] simplify required bean definitions --- .../transform/base/config/MTLSConfig.java | 16 ---------------- .../base/sfs/SharedFileStoreClient.java | 18 +++++++++++++++++- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/engines/base/src/main/java/org/alfresco/transform/base/config/MTLSConfig.java b/engines/base/src/main/java/org/alfresco/transform/base/config/MTLSConfig.java index 2ccc4739..84d932c8 100644 --- a/engines/base/src/main/java/org/alfresco/transform/base/config/MTLSConfig.java +++ b/engines/base/src/main/java/org/alfresco/transform/base/config/MTLSConfig.java @@ -55,16 +55,9 @@ import java.security.NoSuchAlgorithmException; import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; -import static org.springframework.http.HttpHeaders.ACCEPT; -import static org.springframework.http.HttpHeaders.CONTENT_TYPE; -import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; - @Configuration public class MTLSConfig { - @Value("${filestore-url}") - private String url; - @Value("${server.ssl.enabled:false}") boolean sslEnabled; @@ -89,15 +82,6 @@ public class MTLSConfig { @Value("${server.ssl.trust.store.type:}") private String trustStoreType; - @Bean - public WebClient client(WebClient.Builder clientBuilder) - { - return clientBuilder.baseUrl(url.endsWith("/") ? url : url + "/") - .defaultHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE) - .defaultHeader(ACCEPT, APPLICATION_JSON_VALUE) - .build(); - } - @Bean public WebClient.Builder clientBuilder() throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException, UnrecoverableKeyException { if(sslEnabled) diff --git a/engines/base/src/main/java/org/alfresco/transform/base/sfs/SharedFileStoreClient.java b/engines/base/src/main/java/org/alfresco/transform/base/sfs/SharedFileStoreClient.java index 13e4b098..40fe931f 100644 --- a/engines/base/src/main/java/org/alfresco/transform/base/sfs/SharedFileStoreClient.java +++ b/engines/base/src/main/java/org/alfresco/transform/base/sfs/SharedFileStoreClient.java @@ -2,7 +2,7 @@ * #%L * Alfresco Transform Core * %% - * Copyright (C) 2005 - 2022 Alfresco Software Limited + * Copyright (C) 2005 - 2023 Alfresco Software Limited * %% * This file is part of the Alfresco software. * - @@ -26,7 +26,10 @@ */ package org.alfresco.transform.base.sfs; +import static org.springframework.http.HttpHeaders.ACCEPT; +import static org.springframework.http.HttpHeaders.CONTENT_TYPE; import static org.springframework.http.HttpMethod.POST; +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; import static org.springframework.http.MediaType.MULTIPART_FORM_DATA; import java.io.File; @@ -49,6 +52,8 @@ import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.RestTemplate; import org.springframework.web.reactive.function.client.WebClient; +import javax.annotation.PostConstruct; + /** * Simple Rest client that call Alfresco Shared File Store */ @@ -64,8 +69,19 @@ public class SharedFileStoreClient private RestTemplate restTemplate; @Autowired + private WebClient.Builder clientBuilder; + private WebClient client; + @PostConstruct + public void init() + { + client = clientBuilder.baseUrl(url.endsWith("/") ? url : url + "/") + .defaultHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE) + .defaultHeader(ACCEPT, APPLICATION_JSON_VALUE) + .build(); + } + /** * Retrieves a file from Shared File Store using given file reference * From 4c7702179625e732ff0a7979cb4e400fde60c08b Mon Sep 17 00:00:00 2001 From: kcichonczyk Date: Thu, 23 Feb 2023 17:37:13 +0100 Subject: [PATCH 05/11] [ACS-4460] configure beans for WebClient.Builder and RestTemplate --- .../transform/base/config/MTLSConfig.java | 111 +++++++++++------- 1 file changed, 70 insertions(+), 41 deletions(-) diff --git a/engines/base/src/main/java/org/alfresco/transform/base/config/MTLSConfig.java b/engines/base/src/main/java/org/alfresco/transform/base/config/MTLSConfig.java index 84d932c8..32cd7a23 100644 --- a/engines/base/src/main/java/org/alfresco/transform/base/config/MTLSConfig.java +++ b/engines/base/src/main/java/org/alfresco/transform/base/config/MTLSConfig.java @@ -35,6 +35,7 @@ import org.apache.http.ssl.SSLContextBuilder; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Scope; import org.springframework.core.io.Resource; import org.springframework.http.client.ClientHttpRequestFactory; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; @@ -58,51 +59,81 @@ import java.security.cert.CertificateException; @Configuration public class MTLSConfig { - @Value("${server.ssl.enabled:false}") - boolean sslEnabled; - - @Value("${server.ssl.key.store:}") + @Value("${client.ssl.key-store:#{null}}") private Resource keyStoreResource; - @Value("${server.ssl.key.password:}") - private char[] keyPassword; - - @Value("${server.ssl.key.store.password:}") + @Value("${client.ssl.key-store-password:}") private char[] keyStorePassword; - @Value("${server.ssl.key.store.type:}") + @Value("${client.ssl.key-store-type:}") private String keyStoreType; - @Value("${server.ssl.trust.store:}") + @Value("${client.ssl.trust-store:#{null}}") private Resource trustStoreResource; - @Value("${server.ssl.trust.store.password:}") + @Value("${client.ssl.trust-store-password:}") private char[] trustStorePassword; - @Value("${server.ssl.trust.store.type:}") + @Value("${client.ssl.trust-store-type:}") private String trustStoreType; - @Bean - public WebClient.Builder clientBuilder() throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException, UnrecoverableKeyException { - if(sslEnabled) + @Bean() + @Scope("prototype") + public WebClient.Builder clientBuilder() throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException, UnrecoverableKeyException + { + if(isTlsOrMtlsConfigured()) { - HttpClient httpClient = getHttpClientWithMTLS(); - return WebClient.builder().clientConnector(new ReactorClientHttpConnector(httpClient)); + return createWebClientBuilderWithSslContext(); } else { return WebClient.builder(); } } - private HttpClient getHttpClientWithMTLS() throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException { - KeyManagerFactory keyManagerFactory = initKeyManagerFactory(); - TrustManagerFactory trustManagerFactory = initTrustManagerFactory(); + @Bean + public RestTemplate restTemplate() throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException, UnrecoverableKeyException + { + if(isTlsOrMtlsConfigured()) + { + return createRestTemplateWithSslContext(); + } else { + return new RestTemplate(); + } + } - SslContext sslContext = SslContextBuilder.forClient() - .trustManager(trustManagerFactory) - .keyManager(keyManagerFactory) - .build(); + private boolean isTlsOrMtlsConfigured() + { + return isTruststoreConfigured() || isKeystoreConfigured(); + } - return HttpClient.create().secure(p -> p.sslContext(sslContext)); + private boolean isTruststoreConfigured() + { + return trustStoreResource != null; + } + + private boolean isKeystoreConfigured() + { + return keyStoreResource != null; + } + + private WebClient.Builder createWebClientBuilderWithSslContext() throws UnrecoverableKeyException, CertificateException, KeyStoreException, IOException, NoSuchAlgorithmException + { + SslContextBuilder sslContextBuilder = SslContextBuilder.forClient(); + + if(isKeystoreConfigured()) + { + KeyManagerFactory keyManagerFactory = initKeyManagerFactory(); + sslContextBuilder.keyManager(keyManagerFactory); + } + + if(isTruststoreConfigured()) + { + TrustManagerFactory trustManagerFactory = initTrustManagerFactory(); + sslContextBuilder.trustManager(trustManagerFactory); + } + + SslContext sslContext = sslContextBuilder.build(); + HttpClient httpClient = HttpClient.create().secure(p -> p.sslContext(sslContext)); + return WebClient.builder().clientConnector(new ReactorClientHttpConnector(httpClient)); } private TrustManagerFactory initTrustManagerFactory() throws NoSuchAlgorithmException, KeyStoreException, IOException, CertificateException @@ -113,10 +144,11 @@ public class MTLSConfig { return trustManagerFactory; } - private KeyManagerFactory initKeyManagerFactory() throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException { + private KeyManagerFactory initKeyManagerFactory() throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException + { KeyStore clientKeyStore = getKeyStore(keyStoreType, keyStoreResource, keyStorePassword); KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); - keyManagerFactory.init(clientKeyStore, keyPassword); + keyManagerFactory.init(clientKeyStore, keyStorePassword); return keyManagerFactory; } @@ -130,25 +162,22 @@ public class MTLSConfig { return keyStore; } - @Bean - public RestTemplate restTemplate() throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException, UnrecoverableKeyException + private RestTemplate createRestTemplateWithSslContext() throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, KeyManagementException, UnrecoverableKeyException { - if(sslEnabled) + SSLContextBuilder sslContextBuilder = new SSLContextBuilder(); + + if(isKeystoreConfigured()) { - return getRestTemplateWithMTLS(); - } else { - return new RestTemplate(); + KeyStore keyStore = getKeyStore(keyStoreType, keyStoreResource, keyStorePassword); + sslContextBuilder.loadKeyMaterial(keyStore, keyStorePassword); } - } - private RestTemplate getRestTemplateWithMTLS() throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, KeyManagementException, UnrecoverableKeyException - { - KeyStore keyStore = getKeyStore(keyStoreType, keyStoreResource, keyStorePassword); - SSLContext sslContext = new SSLContextBuilder() - .loadKeyMaterial(keyStore, keyPassword) - .loadTrustMaterial(trustStoreResource.getURL(), trustStorePassword) - .build(); + if(isTruststoreConfigured()) + { + sslContextBuilder.loadTrustMaterial(trustStoreResource.getURL(), trustStorePassword); + } + SSLContext sslContext = sslContextBuilder.build(); SSLConnectionSocketFactory sslContextFactory = new SSLConnectionSocketFactory(sslContext); CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(sslContextFactory).build(); ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient); From 807699bedab781c4b12b4973334d5c5054438bdf Mon Sep 17 00:00:00 2001 From: kcichonczyk Date: Tue, 7 Mar 2023 12:28:16 +0100 Subject: [PATCH 06/11] [ACS-4460] integration tests fix --- .../transform/base/MtlsTestUtils.java | 69 +++++++++++++++++++ .../transform/base/clients/HttpClient.java | 5 +- .../transform/base/clients/SfsClient.java | 8 ++- 3 files changed, 77 insertions(+), 5 deletions(-) create mode 100644 engines/base/src/test/java/org/alfresco/transform/base/MtlsTestUtils.java diff --git a/engines/base/src/test/java/org/alfresco/transform/base/MtlsTestUtils.java b/engines/base/src/test/java/org/alfresco/transform/base/MtlsTestUtils.java new file mode 100644 index 00000000..6b209df2 --- /dev/null +++ b/engines/base/src/test/java/org/alfresco/transform/base/MtlsTestUtils.java @@ -0,0 +1,69 @@ +package org.alfresco.transform.base; + +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.conn.ssl.TrustAllStrategy; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.ssl.SSLContextBuilder; +import org.springframework.http.client.ClientHttpRequestFactory; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.web.client.RestTemplate; + +import javax.net.ssl.SSLContext; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.KeyManagementException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; + +public class MtlsTestUtils { + + public static boolean isMtlsEnabled() + { + return Boolean.parseBoolean(System.getProperty("mtls-enabled")); + } + + public static CloseableHttpClient httpClientWithMtls() throws NoSuchAlgorithmException, KeyManagementException, UnrecoverableKeyException, KeyStoreException, IOException, CertificateException { + + String keyStoreFile = System.getProperty("mtls-keystore-file"); + String keyStoreType = System.getProperty("mtls-keystore-type"); + char[] keyStorePassword = System.getProperty("mtls-keystore-password").toCharArray(); + String trustStoreFile = System.getProperty("mtls-truststore-file"); + String trustStoreType = System.getProperty("mtls-truststore-type"); + char[] trustStorePassword = System.getProperty("mtls-truststore-password").toCharArray(); + + SSLContextBuilder sslContextBuilder = new SSLContextBuilder(); + KeyStore keyStore = KeyStore.getInstance(keyStoreType); + try (InputStream keyStoreInputStream = new FileInputStream(keyStoreFile)) + { + keyStore.load(keyStoreInputStream, keyStorePassword); + sslContextBuilder.loadKeyMaterial(keyStore, keyStorePassword); + } + + KeyStore trustStore = KeyStore.getInstance(trustStoreType); + try (InputStream trustStoreInputStream = new FileInputStream(trustStoreFile)) + { + trustStore.load(trustStoreInputStream, trustStorePassword); + sslContextBuilder.loadTrustMaterial(trustStore, TrustAllStrategy.INSTANCE); + } + + SSLContext sslContext = sslContextBuilder.build(); + SSLConnectionSocketFactory sslContextFactory = new SSLConnectionSocketFactory(sslContext); + return HttpClients.custom().setSSLSocketFactory(sslContextFactory).build(); + } + + public static RestTemplate restTemplateWithMtls() + { + ClientHttpRequestFactory requestFactory = null; + try { + requestFactory = new HttpComponentsClientHttpRequestFactory(httpClientWithMtls()); + } catch (Exception e) { + e.printStackTrace(); + } + return new RestTemplate(requestFactory); + } +} diff --git a/engines/base/src/test/java/org/alfresco/transform/base/clients/HttpClient.java b/engines/base/src/test/java/org/alfresco/transform/base/clients/HttpClient.java index 842682e8..def26b7b 100644 --- a/engines/base/src/test/java/org/alfresco/transform/base/clients/HttpClient.java +++ b/engines/base/src/test/java/org/alfresco/transform/base/clients/HttpClient.java @@ -13,6 +13,7 @@ import static org.springframework.http.MediaType.MULTIPART_FORM_DATA; import java.util.Map; +import org.alfresco.transform.base.MtlsTestUtils; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import org.springframework.http.HttpEntity; @@ -27,8 +28,8 @@ import org.springframework.web.client.RestTemplate; */ public class HttpClient { - private static final RestTemplate REST_TEMPLATE = new RestTemplate(); - + private static final RestTemplate REST_TEMPLATE = MtlsTestUtils.isMtlsEnabled() ? MtlsTestUtils.restTemplateWithMtls() : new RestTemplate(); + public static ResponseEntity sendTRequest( final String engineUrl, final String sourceFile, final String sourceMimetype, final String targetMimetype, final String targetExtension) diff --git a/engines/base/src/test/java/org/alfresco/transform/base/clients/SfsClient.java b/engines/base/src/test/java/org/alfresco/transform/base/clients/SfsClient.java index 025c9ef0..69f0d683 100644 --- a/engines/base/src/test/java/org/alfresco/transform/base/clients/SfsClient.java +++ b/engines/base/src/test/java/org/alfresco/transform/base/clients/SfsClient.java @@ -21,6 +21,7 @@ import java.nio.file.Files; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; +import org.alfresco.transform.base.MtlsTestUtils; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; @@ -56,7 +57,7 @@ public class SfsClient ((Logger) LoggerFactory.getLogger("org.apache.http.wire")).setAdditive(false); } - private static final String SFS_BASE_URL = "http://localhost:8099"; + private static final String SFS_BASE_URL = MtlsTestUtils.isMtlsEnabled() ? "https://localhost:8099" : "http://localhost:8099"; public static String uploadFile(final String fileToUploadName) throws Exception { @@ -75,7 +76,7 @@ public class SfsClient .addPart("file", new FileBody(file, ContentType.DEFAULT_BINARY)) .build()); - try (CloseableHttpClient client = HttpClients.createDefault()) + try (CloseableHttpClient client = MtlsTestUtils.isMtlsEnabled() ? MtlsTestUtils.httpClientWithMtls() : HttpClients.createDefault()) { final HttpResponse response = client.execute(post); int status = response.getStatusLine().getStatusCode(); @@ -134,7 +135,8 @@ public class SfsClient sfsBaseUrl+"/alfresco/api/-default-/private/sfs/versions/1/file/{0}", uuid)); - try (CloseableHttpClient client = HttpClients.createDefault()) + try (CloseableHttpClient client = MtlsTestUtils.isMtlsEnabled() ? + MtlsTestUtils.httpClientWithMtls() : HttpClients.createDefault()) { final HttpResponse response = client.execute(head); final int status = response.getStatusLine().getStatusCode(); From 3706bef72bfc3bde45ca822d1035df826823e6b4 Mon Sep 17 00:00:00 2001 From: kcichonczyk Date: Mon, 13 Mar 2023 20:21:35 +0100 Subject: [PATCH 07/11] [ACS-4460] MtlsTestUtils simplified --- .../org/alfresco/transform/base/MtlsTestUtils.java | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/engines/base/src/test/java/org/alfresco/transform/base/MtlsTestUtils.java b/engines/base/src/test/java/org/alfresco/transform/base/MtlsTestUtils.java index 6b209df2..1ad3384f 100644 --- a/engines/base/src/test/java/org/alfresco/transform/base/MtlsTestUtils.java +++ b/engines/base/src/test/java/org/alfresco/transform/base/MtlsTestUtils.java @@ -1,7 +1,6 @@ package org.alfresco.transform.base; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; -import org.apache.http.conn.ssl.TrustAllStrategy; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.ssl.SSLContextBuilder; @@ -10,6 +9,7 @@ import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.web.client.RestTemplate; import javax.net.ssl.SSLContext; +import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; @@ -27,8 +27,8 @@ public class MtlsTestUtils { return Boolean.parseBoolean(System.getProperty("mtls-enabled")); } - public static CloseableHttpClient httpClientWithMtls() throws NoSuchAlgorithmException, KeyManagementException, UnrecoverableKeyException, KeyStoreException, IOException, CertificateException { - + public static CloseableHttpClient httpClientWithMtls() throws NoSuchAlgorithmException, KeyManagementException, UnrecoverableKeyException, KeyStoreException, IOException, CertificateException + { String keyStoreFile = System.getProperty("mtls-keystore-file"); String keyStoreType = System.getProperty("mtls-keystore-type"); char[] keyStorePassword = System.getProperty("mtls-keystore-password").toCharArray(); @@ -44,12 +44,8 @@ public class MtlsTestUtils { sslContextBuilder.loadKeyMaterial(keyStore, keyStorePassword); } - KeyStore trustStore = KeyStore.getInstance(trustStoreType); - try (InputStream trustStoreInputStream = new FileInputStream(trustStoreFile)) - { - trustStore.load(trustStoreInputStream, trustStorePassword); - sslContextBuilder.loadTrustMaterial(trustStore, TrustAllStrategy.INSTANCE); - } + File trustStore = new File(trustStoreFile); + sslContextBuilder.loadTrustMaterial(trustStore, trustStorePassword); SSLContext sslContext = sslContextBuilder.build(); SSLConnectionSocketFactory sslContextFactory = new SSLConnectionSocketFactory(sslContext); From ad0d4c68f0525ca0c4e0856f1a06de11d4d41e3f Mon Sep 17 00:00:00 2001 From: kcichonczyk Date: Mon, 13 Mar 2023 20:56:44 +0100 Subject: [PATCH 08/11] [ACS-4460] MtlsTestUtils simplify methods --- .../java/org/alfresco/transform/base/MtlsTestUtils.java | 9 +++++++++ .../org/alfresco/transform/base/clients/HttpClient.java | 2 +- .../org/alfresco/transform/base/clients/SfsClient.java | 8 +++----- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/engines/base/src/test/java/org/alfresco/transform/base/MtlsTestUtils.java b/engines/base/src/test/java/org/alfresco/transform/base/MtlsTestUtils.java index 1ad3384f..f8e70401 100644 --- a/engines/base/src/test/java/org/alfresco/transform/base/MtlsTestUtils.java +++ b/engines/base/src/test/java/org/alfresco/transform/base/MtlsTestUtils.java @@ -62,4 +62,13 @@ public class MtlsTestUtils { } return new RestTemplate(requestFactory); } + + public static RestTemplate getRestTemplate() + { + return MtlsTestUtils.isMtlsEnabled() ? MtlsTestUtils.restTemplateWithMtls() : new RestTemplate(); + } + + public static CloseableHttpClient getHttpClient() throws UnrecoverableKeyException, CertificateException, NoSuchAlgorithmException, KeyStoreException, IOException, KeyManagementException { + return MtlsTestUtils.isMtlsEnabled() ? MtlsTestUtils.httpClientWithMtls() : HttpClients.createDefault(); + } } diff --git a/engines/base/src/test/java/org/alfresco/transform/base/clients/HttpClient.java b/engines/base/src/test/java/org/alfresco/transform/base/clients/HttpClient.java index def26b7b..ab51b887 100644 --- a/engines/base/src/test/java/org/alfresco/transform/base/clients/HttpClient.java +++ b/engines/base/src/test/java/org/alfresco/transform/base/clients/HttpClient.java @@ -28,7 +28,7 @@ import org.springframework.web.client.RestTemplate; */ public class HttpClient { - private static final RestTemplate REST_TEMPLATE = MtlsTestUtils.isMtlsEnabled() ? MtlsTestUtils.restTemplateWithMtls() : new RestTemplate(); + private static final RestTemplate REST_TEMPLATE = MtlsTestUtils.getRestTemplate(); public static ResponseEntity sendTRequest( final String engineUrl, final String sourceFile, diff --git a/engines/base/src/test/java/org/alfresco/transform/base/clients/SfsClient.java b/engines/base/src/test/java/org/alfresco/transform/base/clients/SfsClient.java index 69f0d683..f31db5da 100644 --- a/engines/base/src/test/java/org/alfresco/transform/base/clients/SfsClient.java +++ b/engines/base/src/test/java/org/alfresco/transform/base/clients/SfsClient.java @@ -32,7 +32,6 @@ import org.apache.http.entity.mime.HttpMultipartMode; import org.apache.http.entity.mime.MultipartEntityBuilder; import org.apache.http.entity.mime.content.FileBody; import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; import org.slf4j.LoggerFactory; @@ -76,7 +75,7 @@ public class SfsClient .addPart("file", new FileBody(file, ContentType.DEFAULT_BINARY)) .build()); - try (CloseableHttpClient client = MtlsTestUtils.isMtlsEnabled() ? MtlsTestUtils.httpClientWithMtls() : HttpClients.createDefault()) + try (CloseableHttpClient client = MtlsTestUtils.getHttpClient()) { final HttpResponse response = client.execute(post); int status = response.getStatusLine().getStatusCode(); @@ -135,8 +134,7 @@ public class SfsClient sfsBaseUrl+"/alfresco/api/-default-/private/sfs/versions/1/file/{0}", uuid)); - try (CloseableHttpClient client = MtlsTestUtils.isMtlsEnabled() ? - MtlsTestUtils.httpClientWithMtls() : HttpClients.createDefault()) + try (CloseableHttpClient client = MtlsTestUtils.getHttpClient()) { final HttpResponse response = client.execute(head); final int status = response.getStatusLine().getStatusCode(); @@ -155,7 +153,7 @@ public class SfsClient sfsBaseUrl+"/alfresco/api/-default-/private/sfs/versions/1/file/{0}", uuid)); - try (CloseableHttpClient client = HttpClients.createDefault()) + try (CloseableHttpClient client = MtlsTestUtils.getHttpClient()) { final HttpResponse response = client.execute(get); final int status = response.getStatusLine().getStatusCode(); From 0abe3c1f89b0276982b0b268b9ca1593f2f67923 Mon Sep 17 00:00:00 2001 From: kcichonczyk Date: Tue, 14 Mar 2023 14:17:22 +0100 Subject: [PATCH 09/11] [ACS-4460] MTLSConfig refactor, add ssl context beans --- .../transform/base/config/MTLSConfig.java | 109 ++++++++++-------- .../transform/base/MtlsTestUtils.java | 16 +-- 2 files changed, 67 insertions(+), 58 deletions(-) diff --git a/engines/base/src/main/java/org/alfresco/transform/base/config/MTLSConfig.java b/engines/base/src/main/java/org/alfresco/transform/base/config/MTLSConfig.java index 32cd7a23..8c964146 100644 --- a/engines/base/src/main/java/org/alfresco/transform/base/config/MTLSConfig.java +++ b/engines/base/src/main/java/org/alfresco/transform/base/config/MTLSConfig.java @@ -79,27 +79,61 @@ public class MTLSConfig { @Bean() @Scope("prototype") - public WebClient.Builder clientBuilder() throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException, UnrecoverableKeyException + public WebClient.Builder clientBuilder(SslContextBuilder nettySslContextBuilder) throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException, UnrecoverableKeyException { if(isTlsOrMtlsConfigured()) { - return createWebClientBuilderWithSslContext(); + return createWebClientBuilderWithSslContext(nettySslContextBuilder); } else { return WebClient.builder(); } } @Bean - public RestTemplate restTemplate() throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException, UnrecoverableKeyException + public RestTemplate restTemplate(SSLContextBuilder apacheSSLContextBuilder) throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException, UnrecoverableKeyException { if(isTlsOrMtlsConfigured()) { - return createRestTemplateWithSslContext(); + return createRestTemplateWithSslContext(apacheSSLContextBuilder); } else { return new RestTemplate(); } } + @Bean + public SSLContextBuilder apacheSSLContextBuilder() throws CertificateException, KeyStoreException, IOException, NoSuchAlgorithmException, UnrecoverableKeyException { + SSLContextBuilder sslContextBuilder = new SSLContextBuilder(); + if(isKeystoreConfigured()) + { + KeyStore keyStore = getKeyStore(keyStoreType, keyStoreResource, keyStorePassword); + sslContextBuilder.loadKeyMaterial(keyStore, keyStorePassword); + } + if(isTruststoreConfigured()) + { + sslContextBuilder.loadTrustMaterial(trustStoreResource.getURL(), trustStorePassword); + } + + return sslContextBuilder; + } + + @Bean + public SslContextBuilder nettySslContextBuilder() throws UnrecoverableKeyException, CertificateException, KeyStoreException, IOException, NoSuchAlgorithmException { + SslContextBuilder sslContextBuilder = SslContextBuilder.forClient(); + if(isKeystoreConfigured()) + { + KeyManagerFactory keyManagerFactory = initKeyManagerFactory(); + sslContextBuilder.keyManager(keyManagerFactory); + } + + if(isTruststoreConfigured()) + { + TrustManagerFactory trustManagerFactory = initTrustManagerFactory(); + sslContextBuilder.trustManager(trustManagerFactory); + } + + return sslContextBuilder; + } + private boolean isTlsOrMtlsConfigured() { return isTruststoreConfigured() || isKeystoreConfigured(); @@ -115,27 +149,32 @@ public class MTLSConfig { return keyStoreResource != null; } - private WebClient.Builder createWebClientBuilderWithSslContext() throws UnrecoverableKeyException, CertificateException, KeyStoreException, IOException, NoSuchAlgorithmException + private WebClient.Builder createWebClientBuilderWithSslContext(SslContextBuilder sslContextBuilder) throws UnrecoverableKeyException, CertificateException, KeyStoreException, IOException, NoSuchAlgorithmException { - SslContextBuilder sslContextBuilder = SslContextBuilder.forClient(); - - if(isKeystoreConfigured()) - { - KeyManagerFactory keyManagerFactory = initKeyManagerFactory(); - sslContextBuilder.keyManager(keyManagerFactory); - } - - if(isTruststoreConfigured()) - { - TrustManagerFactory trustManagerFactory = initTrustManagerFactory(); - sslContextBuilder.trustManager(trustManagerFactory); - } - SslContext sslContext = sslContextBuilder.build(); HttpClient httpClient = HttpClient.create().secure(p -> p.sslContext(sslContext)); return WebClient.builder().clientConnector(new ReactorClientHttpConnector(httpClient)); } + private RestTemplate createRestTemplateWithSslContext(SSLContextBuilder sslContextBuilder) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, KeyManagementException, UnrecoverableKeyException + { + SSLContext sslContext = sslContextBuilder.build(); + SSLConnectionSocketFactory sslContextFactory = new SSLConnectionSocketFactory(sslContext); + CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(sslContextFactory).build(); + ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient); + return new RestTemplate(requestFactory); + } + + private KeyStore getKeyStore(String keyStoreType, Resource keyStoreResource, char[] keyStorePassword) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException + { + KeyStore keyStore = KeyStore.getInstance(keyStoreType); + try (InputStream keyStoreInputStream = keyStoreResource.getInputStream()) + { + keyStore.load(keyStoreInputStream, keyStorePassword); + } + return keyStore; + } + private TrustManagerFactory initTrustManagerFactory() throws NoSuchAlgorithmException, KeyStoreException, IOException, CertificateException { KeyStore trustStore = getKeyStore(trustStoreType, trustStoreResource, trustStorePassword); @@ -151,36 +190,4 @@ public class MTLSConfig { keyManagerFactory.init(clientKeyStore, keyStorePassword); return keyManagerFactory; } - - private KeyStore getKeyStore(String keyStoreType, Resource keyStoreResource, char[] keyStorePassword) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException - { - KeyStore keyStore = KeyStore.getInstance(keyStoreType); - try (InputStream keyStoreInputStream = keyStoreResource.getInputStream()) - { - keyStore.load(keyStoreInputStream, keyStorePassword); - } - return keyStore; - } - - private RestTemplate createRestTemplateWithSslContext() throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, KeyManagementException, UnrecoverableKeyException - { - SSLContextBuilder sslContextBuilder = new SSLContextBuilder(); - - if(isKeystoreConfigured()) - { - KeyStore keyStore = getKeyStore(keyStoreType, keyStoreResource, keyStorePassword); - sslContextBuilder.loadKeyMaterial(keyStore, keyStorePassword); - } - - if(isTruststoreConfigured()) - { - sslContextBuilder.loadTrustMaterial(trustStoreResource.getURL(), trustStorePassword); - } - - SSLContext sslContext = sslContextBuilder.build(); - SSLConnectionSocketFactory sslContextFactory = new SSLConnectionSocketFactory(sslContext); - CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(sslContextFactory).build(); - ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient); - return new RestTemplate(requestFactory); - } } diff --git a/engines/base/src/test/java/org/alfresco/transform/base/MtlsTestUtils.java b/engines/base/src/test/java/org/alfresco/transform/base/MtlsTestUtils.java index f8e70401..3f7f5fdf 100644 --- a/engines/base/src/test/java/org/alfresco/transform/base/MtlsTestUtils.java +++ b/engines/base/src/test/java/org/alfresco/transform/base/MtlsTestUtils.java @@ -22,19 +22,21 @@ import java.security.cert.CertificateException; public class MtlsTestUtils { + private static final boolean MTLS_ENABLED = Boolean.parseBoolean(System.getProperty("test-mtls-enabled")); + public static boolean isMtlsEnabled() { - return Boolean.parseBoolean(System.getProperty("mtls-enabled")); + return MTLS_ENABLED; } public static CloseableHttpClient httpClientWithMtls() throws NoSuchAlgorithmException, KeyManagementException, UnrecoverableKeyException, KeyStoreException, IOException, CertificateException { - String keyStoreFile = System.getProperty("mtls-keystore-file"); - String keyStoreType = System.getProperty("mtls-keystore-type"); - char[] keyStorePassword = System.getProperty("mtls-keystore-password").toCharArray(); - String trustStoreFile = System.getProperty("mtls-truststore-file"); - String trustStoreType = System.getProperty("mtls-truststore-type"); - char[] trustStorePassword = System.getProperty("mtls-truststore-password").toCharArray(); + String keyStoreFile = System.getProperty("test-client-keystore-file"); + String keyStoreType = System.getProperty("test-client-keystore-type"); + char[] keyStorePassword = System.getProperty("test-client-keystore-password").toCharArray(); + String trustStoreFile = System.getProperty("test-client-truststore-file"); + String trustStoreType = System.getProperty("test-client-truststore-type"); + char[] trustStorePassword = System.getProperty("test-client-truststore-password").toCharArray(); SSLContextBuilder sslContextBuilder = new SSLContextBuilder(); KeyStore keyStore = KeyStore.getInstance(keyStoreType); From 3b2d1c56391c95dc190fc6e552265ad22929f6ce Mon Sep 17 00:00:00 2001 From: kcichonczyk Date: Tue, 14 Mar 2023 14:19:58 +0100 Subject: [PATCH 10/11] [ACS-4460] remove '()' --- .../java/org/alfresco/transform/base/config/MTLSConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engines/base/src/main/java/org/alfresco/transform/base/config/MTLSConfig.java b/engines/base/src/main/java/org/alfresco/transform/base/config/MTLSConfig.java index 8c964146..ed1c1256 100644 --- a/engines/base/src/main/java/org/alfresco/transform/base/config/MTLSConfig.java +++ b/engines/base/src/main/java/org/alfresco/transform/base/config/MTLSConfig.java @@ -77,7 +77,7 @@ public class MTLSConfig { @Value("${client.ssl.trust-store-type:}") private String trustStoreType; - @Bean() + @Bean @Scope("prototype") public WebClient.Builder clientBuilder(SslContextBuilder nettySslContextBuilder) throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException, UnrecoverableKeyException { From 5a2665dc2f0867514c2fdcc67fc0eb566128ec93 Mon Sep 17 00:00:00 2001 From: kcichonczyk Date: Wed, 15 Mar 2023 12:17:28 +0100 Subject: [PATCH 11/11] [ACS-4460] simplify WebClient.Builder creation --- .../base/WebClientBuilderAdjuster.java | 16 +++++++++ .../transform/base/config/MTLSConfig.java | 33 ++++++++++--------- .../base/sfs/SharedFileStoreClient.java | 9 +++-- 3 files changed, 40 insertions(+), 18 deletions(-) create mode 100644 engines/base/src/main/java/org/alfresco/transform/base/WebClientBuilderAdjuster.java diff --git a/engines/base/src/main/java/org/alfresco/transform/base/WebClientBuilderAdjuster.java b/engines/base/src/main/java/org/alfresco/transform/base/WebClientBuilderAdjuster.java new file mode 100644 index 00000000..1147ee67 --- /dev/null +++ b/engines/base/src/main/java/org/alfresco/transform/base/WebClientBuilderAdjuster.java @@ -0,0 +1,16 @@ +/* + * Copyright 2015-2023 Alfresco Software, Ltd. All rights reserved. + * + * License rights for this program may be obtained from Alfresco Software, Ltd. + * pursuant to a written agreement and any use of this program without such an + * agreement is prohibited. + */ +package org.alfresco.transform.base; + +import org.springframework.web.reactive.function.client.WebClient; + +@FunctionalInterface +public interface WebClientBuilderAdjuster +{ + void adjust(WebClient.Builder builder); +} diff --git a/engines/base/src/main/java/org/alfresco/transform/base/config/MTLSConfig.java b/engines/base/src/main/java/org/alfresco/transform/base/config/MTLSConfig.java index ed1c1256..12ed0761 100644 --- a/engines/base/src/main/java/org/alfresco/transform/base/config/MTLSConfig.java +++ b/engines/base/src/main/java/org/alfresco/transform/base/config/MTLSConfig.java @@ -28,6 +28,7 @@ package org.alfresco.transform.base.config; import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.SslContextBuilder; +import org.alfresco.transform.base.WebClientBuilderAdjuster; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; @@ -35,7 +36,6 @@ import org.apache.http.ssl.SSLContextBuilder; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Scope; import org.springframework.core.io.Resource; import org.springframework.http.client.ClientHttpRequestFactory; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; @@ -46,6 +46,7 @@ import reactor.netty.http.client.HttpClient; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLException; import javax.net.ssl.TrustManagerFactory; import java.io.IOException; import java.io.InputStream; @@ -78,15 +79,20 @@ public class MTLSConfig { private String trustStoreType; @Bean - @Scope("prototype") - public WebClient.Builder clientBuilder(SslContextBuilder nettySslContextBuilder) throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException, UnrecoverableKeyException + public WebClientBuilderAdjuster webClientBuilderAdjuster(SslContextBuilder nettySslContextBuilder) { - if(isTlsOrMtlsConfigured()) - { - return createWebClientBuilderWithSslContext(nettySslContextBuilder); - } else { - return WebClient.builder(); - } + return builder -> { + if(isTlsOrMtlsConfigured()) + { + HttpClient httpClientWithSslContext = null; + try { + httpClientWithSslContext = createHttpClientWithSslContext(nettySslContextBuilder); + } catch (SSLException e) { + throw new RuntimeException(e); + } + builder.clientConnector(new ReactorClientHttpConnector(httpClientWithSslContext)); + } + }; } @Bean @@ -149,15 +155,12 @@ public class MTLSConfig { return keyStoreResource != null; } - private WebClient.Builder createWebClientBuilderWithSslContext(SslContextBuilder sslContextBuilder) throws UnrecoverableKeyException, CertificateException, KeyStoreException, IOException, NoSuchAlgorithmException - { + private HttpClient createHttpClientWithSslContext(SslContextBuilder sslContextBuilder) throws SSLException { SslContext sslContext = sslContextBuilder.build(); - HttpClient httpClient = HttpClient.create().secure(p -> p.sslContext(sslContext)); - return WebClient.builder().clientConnector(new ReactorClientHttpConnector(httpClient)); + return HttpClient.create().secure(p -> p.sslContext(sslContext)); } - private RestTemplate createRestTemplateWithSslContext(SSLContextBuilder sslContextBuilder) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, KeyManagementException, UnrecoverableKeyException - { + private RestTemplate createRestTemplateWithSslContext(SSLContextBuilder sslContextBuilder) throws NoSuchAlgorithmException, KeyManagementException { SSLContext sslContext = sslContextBuilder.build(); SSLConnectionSocketFactory sslContextFactory = new SSLConnectionSocketFactory(sslContext); CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(sslContextFactory).build(); diff --git a/engines/base/src/main/java/org/alfresco/transform/base/sfs/SharedFileStoreClient.java b/engines/base/src/main/java/org/alfresco/transform/base/sfs/SharedFileStoreClient.java index 40fe931f..b54f07ef 100644 --- a/engines/base/src/main/java/org/alfresco/transform/base/sfs/SharedFileStoreClient.java +++ b/engines/base/src/main/java/org/alfresco/transform/base/sfs/SharedFileStoreClient.java @@ -34,6 +34,7 @@ import static org.springframework.http.MediaType.MULTIPART_FORM_DATA; import java.io.File; +import org.alfresco.transform.base.WebClientBuilderAdjuster; import org.alfresco.transform.exceptions.TransformException; import org.alfresco.transform.base.model.FileRefResponse; import org.slf4j.Logger; @@ -53,6 +54,7 @@ import org.springframework.web.client.RestTemplate; import org.springframework.web.reactive.function.client.WebClient; import javax.annotation.PostConstruct; +import javax.net.ssl.SSLException; /** * Simple Rest client that call Alfresco Shared File Store @@ -69,13 +71,14 @@ public class SharedFileStoreClient private RestTemplate restTemplate; @Autowired - private WebClient.Builder clientBuilder; + private WebClientBuilderAdjuster adjuster; private WebClient client; @PostConstruct - public void init() - { + public void init() throws SSLException { + final WebClient.Builder clientBuilder = WebClient.builder(); + adjuster.adjust(clientBuilder); client = clientBuilder.baseUrl(url.endsWith("/") ? url : url + "/") .defaultHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE) .defaultHeader(ACCEPT, APPLICATION_JSON_VALUE)