This is an automated email from the ASF dual-hosted git repository.

pvillard31 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nifi.git


The following commit(s) were added to refs/heads/main by this push:
     new ee33ae84e9e NIFI-15948 Switched HashiCorp Vault Client Service to 
SSLContextProvider (#11257)
ee33ae84e9e is described below

commit ee33ae84e9e24a218fa2090e13e71e7441c8c2df
Author: David Handermann <[email protected]>
AuthorDate: Sat May 16 04:57:05 2026 -0500

    NIFI-15948 Switched HashiCorp Vault Client Service to SSLContextProvider 
(#11257)
---
 ...StandardHashiCorpVaultCommunicationService.java | 35 +++++++++++++++++--
 .../config/HashiCorpVaultConfiguration.java        | 25 --------------
 ...StandardHashiCorpVaultCommunicationService.java |  8 ++---
 .../hashicorp/HashiCorpVaultClientService.java     |  7 ++--
 .../StandardHashiCorpVaultClientService.java       | 39 +++++-----------------
 5 files changed, 48 insertions(+), 66 deletions(-)

diff --git 
a/nifi-commons/nifi-hashicorp-vault/src/main/java/org/apache/nifi/vault/hashicorp/StandardHashiCorpVaultCommunicationService.java
 
b/nifi-commons/nifi-hashicorp-vault/src/main/java/org/apache/nifi/vault/hashicorp/StandardHashiCorpVaultCommunicationService.java
index 48c5cb1135a..fc8cccab810 100644
--- 
a/nifi-commons/nifi-hashicorp-vault/src/main/java/org/apache/nifi/vault/hashicorp/StandardHashiCorpVaultCommunicationService.java
+++ 
b/nifi-commons/nifi-hashicorp-vault/src/main/java/org/apache/nifi/vault/hashicorp/StandardHashiCorpVaultCommunicationService.java
@@ -23,6 +23,7 @@ import 
org.apache.nifi.vault.hashicorp.config.HashiCorpVaultProperties;
 import org.apache.nifi.vault.hashicorp.config.HashiCorpVaultPropertySource;
 import org.springframework.core.env.PropertySource;
 import org.springframework.http.client.ClientHttpRequestFactory;
+import org.springframework.http.client.JdkClientHttpRequestFactory;
 import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
 import org.springframework.vault.authentication.LifecycleAwareSessionManager;
 import org.springframework.vault.client.ClientHttpRequestFactoryFactory;
@@ -32,12 +33,15 @@ import 
org.springframework.vault.core.VaultKeyValueOperationsSupport.KeyValueBac
 import org.springframework.vault.core.VaultTemplate;
 import org.springframework.vault.core.VaultTransitOperations;
 import org.springframework.vault.support.Ciphertext;
+import org.springframework.vault.support.ClientOptions;
 import org.springframework.vault.support.Plaintext;
 import org.springframework.vault.support.VaultResponseSupport;
 import org.springframework.web.client.RestOperations;
 
 import java.io.Closeable;
 import java.io.IOException;
+import java.net.http.HttpClient;
+import java.security.GeneralSecurityException;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
@@ -45,11 +49,14 @@ import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
+import javax.net.ssl.SSLContext;
 
 /**
  * Implements the VaultCommunicationService using Spring Vault
  */
 public class StandardHashiCorpVaultCommunicationService implements 
HashiCorpVaultCommunicationService, Closeable {
+    private static final String SSL_CONTEXT_PROPERTY = 
SSLContext.class.getName();
+
     private final LifecycleAwareSessionManager sessionManager;
     private final ThreadPoolTaskScheduler taskScheduler;
     private final VaultTemplate vaultTemplate;
@@ -70,7 +77,31 @@ public class StandardHashiCorpVaultCommunicationService 
implements HashiCorpVaul
         taskScheduler.setDaemon(true);
         taskScheduler.afterPropertiesSet();
 
-        final ClientHttpRequestFactory clientHttpRequestFactory = 
ClientHttpRequestFactoryFactory.create(vaultConfiguration.clientOptions(), 
vaultConfiguration.sslConfiguration());
+        final ClientHttpRequestFactory clientHttpRequestFactory;
+
+        final ClientOptions clientOptions = vaultConfiguration.clientOptions();
+        final PropertySource<?> propertySource = propertySources[0];
+        final Object sslContextProperty = 
propertySource.getProperty(SSL_CONTEXT_PROPERTY);
+        if (sslContextProperty instanceof SSLContext sslContext) {
+            // Customize HttpClient construction with configured SSLContext
+            final HttpClient.Builder httpClientBuilder = 
HttpClient.newBuilder();
+            
httpClientBuilder.connectTimeout(clientOptions.getConnectionTimeout());
+            httpClientBuilder.followRedirects(HttpClient.Redirect.ALWAYS);
+            httpClientBuilder.sslContext(sslContext);
+
+            final HttpClient httpClient = httpClientBuilder.build();
+            final JdkClientHttpRequestFactory jdkClientHttpRequestFactory = 
new JdkClientHttpRequestFactory(httpClient);
+            
jdkClientHttpRequestFactory.setReadTimeout(clientOptions.getReadTimeout());
+            clientHttpRequestFactory = jdkClientHttpRequestFactory;
+        } else {
+            // Build with standard Spring Vault methods
+            try {
+                clientHttpRequestFactory = 
ClientHttpRequestFactoryFactory.JdkHttpClient.usingJdkHttpClient(clientOptions, 
vaultConfiguration.sslConfiguration());
+            } catch (final GeneralSecurityException | IOException e) {
+                throw new IllegalArgumentException(e);
+            }
+        }
+
         final RestOperations restOperations = 
VaultClients.createRestTemplate(vaultConfiguration.vaultEndpoint(), 
clientHttpRequestFactory);
 
         sessionManager = new 
LifecycleAwareSessionManager(vaultConfiguration.clientAuthentication(), 
taskScheduler, restOperations);
@@ -96,7 +127,7 @@ public class StandardHashiCorpVaultCommunicationService 
implements HashiCorpVaul
     }
 
     @Override
-    public void close() throws IOException {
+    public void close() {
         sessionManager.destroy();
         taskScheduler.destroy();
     }
diff --git 
a/nifi-commons/nifi-hashicorp-vault/src/main/java/org/apache/nifi/vault/hashicorp/config/HashiCorpVaultConfiguration.java
 
b/nifi-commons/nifi-hashicorp-vault/src/main/java/org/apache/nifi/vault/hashicorp/config/HashiCorpVaultConfiguration.java
index 46c406fb6f8..3188733c3bf 100644
--- 
a/nifi-commons/nifi-hashicorp-vault/src/main/java/org/apache/nifi/vault/hashicorp/config/HashiCorpVaultConfiguration.java
+++ 
b/nifi-commons/nifi-hashicorp-vault/src/main/java/org/apache/nifi/vault/hashicorp/config/HashiCorpVaultConfiguration.java
@@ -36,7 +36,6 @@ import org.springframework.vault.support.SslConfiguration;
 import java.io.IOException;
 import java.nio.file.Paths;
 import java.time.Duration;
-import java.util.Objects;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -123,7 +122,6 @@ public class HashiCorpVaultConfiguration extends 
EnvironmentVaultConfiguration {
             }
         }
         this.keyValueBackend = keyValueBackend;
-        validateProperties(env);
 
         this.setApplicationContext(new HashiCorpVaultApplicationContext(env));
 
@@ -133,29 +131,6 @@ public class HashiCorpVaultConfiguration extends 
EnvironmentVaultConfiguration {
         clientOptions = getClientOptions();
     }
 
-    private void validateProperties(final ConfigurableEnvironment environment) 
{
-        try {
-            final String vaultUri = 
Objects.requireNonNull(environment.getProperty(VaultConfigurationKey.URI.key),
-                    "Missing required property " + 
VaultConfigurationKey.URI.key);
-            if (vaultUri.startsWith(HTTPS)) {
-                requireSslProperty("vault.ssl.key-store", environment);
-                requireSslProperty("vault.ssl.key-store-password", 
environment);
-                requireSslProperty("vault.ssl.key-store-type", environment);
-                requireSslProperty("vault.ssl.trust-store", environment);
-                requireSslProperty("vault.ssl.trust-store-password", 
environment);
-                requireSslProperty("vault.ssl.trust-store-type", environment);
-            }
-        } catch (final NullPointerException e) {
-            // Rethrow as IllegalArgumentException
-            throw new IllegalArgumentException(e.getMessage(), e);
-        }
-
-    }
-
-    private void requireSslProperty(final String propertyName, final 
ConfigurableEnvironment environment) {
-        Objects.requireNonNull(environment.getProperty(propertyName), 
propertyName + " is required with an https URI");
-    }
-
     public KeyValueBackend getKeyValueBackend() {
         return keyValueBackend;
     }
diff --git 
a/nifi-commons/nifi-hashicorp-vault/src/test/java/org/apache/nifi/vault/hashicorp/TestStandardHashiCorpVaultCommunicationService.java
 
b/nifi-commons/nifi-hashicorp-vault/src/test/java/org/apache/nifi/vault/hashicorp/TestStandardHashiCorpVaultCommunicationService.java
index e013449a986..8960bff1494 100644
--- 
a/nifi-commons/nifi-hashicorp-vault/src/test/java/org/apache/nifi/vault/hashicorp/TestStandardHashiCorpVaultCommunicationService.java
+++ 
b/nifi-commons/nifi-hashicorp-vault/src/test/java/org/apache/nifi/vault/hashicorp/TestStandardHashiCorpVaultCommunicationService.java
@@ -63,10 +63,10 @@ public class TestStandardHashiCorpVaultCommunicationService 
{
     }
 
     @Test
-    public void testBasicConfiguration() throws Exception {
+    public void testBasicConfiguration() {
         try (StandardHashiCorpVaultCommunicationService ignored = 
this.configureService()) {
-            // Once to check if the URI is https, once by VaultTemplate, once 
by RestTemplate, and once to validate
-            Mockito.verify(properties, Mockito.times(4)).getUri();
+            // Once to check if the URI is https, once by VaultTemplate, once 
by RestTemplate
+            Mockito.verify(properties, Mockito.times(3)).getUri();
 
             // Once to check if the property is set, and once to retrieve the 
value
             Mockito.verify(properties, 
Mockito.times(2)).getAuthPropertiesFilename();
@@ -74,7 +74,7 @@ public class TestStandardHashiCorpVaultCommunicationService {
     }
 
     @Test
-    public void testTimeouts() throws Exception {
+    public void testTimeouts() {
         when(properties.getConnectionTimeout()).thenReturn(Optional.of("20 
secs"));
         when(properties.getReadTimeout()).thenReturn(Optional.of("40 secs"));
         try (StandardHashiCorpVaultCommunicationService ignored = 
this.configureService()) {
diff --git 
a/nifi-extension-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-client-service-api/src/main/java/org/apache/nifi/vault/hashicorp/HashiCorpVaultClientService.java
 
b/nifi-extension-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-client-service-api/src/main/java/org/apache/nifi/vault/hashicorp/HashiCorpVaultClientService.java
index 02290a3d368..6b9b1b74466 100644
--- 
a/nifi-extension-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-client-service-api/src/main/java/org/apache/nifi/vault/hashicorp/HashiCorpVaultClientService.java
+++ 
b/nifi-extension-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-client-service-api/src/main/java/org/apache/nifi/vault/hashicorp/HashiCorpVaultClientService.java
@@ -25,7 +25,7 @@ import org.apache.nifi.controller.VerifiableControllerService;
 import org.apache.nifi.expression.ExpressionLanguageScope;
 import org.apache.nifi.migration.PropertyConfiguration;
 import org.apache.nifi.processor.util.StandardValidators;
-import org.apache.nifi.ssl.SSLContextService;
+import org.apache.nifi.ssl.SSLContextProvider;
 
 /**
  * Provides a HashiCorpVaultCommunicationService.
@@ -70,10 +70,9 @@ public interface HashiCorpVaultClientService extends 
ControllerService, Verifiab
     PropertyDescriptor SSL_CONTEXT_SERVICE = new PropertyDescriptor.Builder()
             .name("vault.ssl.context.service")
             .displayName("SSL Context Service")
-            .description("The SSL Context Service used to provide client 
certificate information for TLS/SSL connections to the " +
-                    "HashiCorp Vault server.")
+            .description("SSL Context Provider for TLS encrypted communication 
with HashiCorp Vault Server")
             .required(false)
-            .identifiesControllerService(SSLContextService.class)
+            .identifiesControllerService(SSLContextProvider.class)
             .dependsOn(CONFIGURATION_STRATEGY, DIRECT_PROPERTIES)
             .build();
 
diff --git 
a/nifi-extension-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-client-service/src/main/java/org/apache/nifi/vault/hashicorp/StandardHashiCorpVaultClientService.java
 
b/nifi-extension-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-client-service/src/main/java/org/apache/nifi/vault/hashicorp/StandardHashiCorpVaultClientService.java
index bdf2d3cdba2..073691fb1e1 100644
--- 
a/nifi-extension-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-client-service/src/main/java/org/apache/nifi/vault/hashicorp/StandardHashiCorpVaultClientService.java
+++ 
b/nifi-extension-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-client-service/src/main/java/org/apache/nifi/vault/hashicorp/StandardHashiCorpVaultClientService.java
@@ -32,7 +32,7 @@ import org.apache.nifi.expression.ExpressionLanguageScope;
 import org.apache.nifi.logging.ComponentLog;
 import org.apache.nifi.processor.util.StandardValidators;
 import org.apache.nifi.reporting.InitializationException;
-import org.apache.nifi.ssl.SSLContextService;
+import org.apache.nifi.ssl.SSLContextProvider;
 import org.apache.nifi.vault.hashicorp.config.HashiCorpVaultConfiguration;
 import org.springframework.core.env.PropertySource;
 
@@ -40,6 +40,7 @@ import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
+import javax.net.ssl.SSLContext;
 
 @Tags({"hashicorp", "vault", "client"})
 @CapabilityDescription("A controller service for interacting with HashiCorp 
Vault.")
@@ -47,9 +48,7 @@ import java.util.Map;
 @DynamicProperties(
         @DynamicProperty(name = "A Spring Vault configuration property name",
                 value = "The property value",
-                description = "Allows any Spring Vault property keys to be 
specified, as described in " +
-                        
"(https://docs.spring.io/spring-vault/docs/2.3.x/reference/html/#vault.core.environment-vault-configuration).
 " +
-                        "See Additional Details for more information.",
+                description = "Allows any Spring Vault property keys to be 
specified. See Additional Details for more information.",
                 expressionLanguageScope = ExpressionLanguageScope.ENVIRONMENT
         )
 )
@@ -171,7 +170,7 @@ public class StandardHashiCorpVaultClientService extends 
AbstractControllerServi
 
     static class DirectPropertySource extends 
PropertySource<ConfigurationContext> {
 
-        private static final String VAULT_SSL_KEY_PATTERN = 
"vault.ssl.(key.*|trust.*|enabledProtocols)";
+        private static final String SSL_CONTEXT_PROPERTY = 
SSLContext.class.getName();
 
         public DirectPropertySource(final String name, final 
ConfigurationContext source) {
             super(name, source);
@@ -179,35 +178,13 @@ public class StandardHashiCorpVaultClientService extends 
AbstractControllerServi
 
         @Override
         public Object getProperty(final String name) {
-            if (name.matches(VAULT_SSL_KEY_PATTERN)) {
-                return getSslProperty(name);
+            if (SSL_CONTEXT_PROPERTY.equals(name)) {
+                // Create SSLContext for property matching class
+                final SSLContextProvider sslContextProvider = 
getSource().getProperty(SSL_CONTEXT_SERVICE).asControllerService(SSLContextProvider.class);
+                return sslContextProvider == null ? null : 
sslContextProvider.createContext();
             }
 
             return getSource().getAllProperties().get(name);
         }
-
-        private String getSslProperty(final String name) {
-            if (getSource().getProperty(SSL_CONTEXT_SERVICE).isSet()) {
-                final SSLContextService sslContextService = 
getSource().getProperty(SSL_CONTEXT_SERVICE).asControllerService(SSLContextService.class);
-                switch (name) {
-                    case "vault.ssl.key-store":
-                        return sslContextService.getKeyStoreFile();
-                    case "vault.ssl.key-store-password":
-                        return sslContextService.getKeyStorePassword();
-                    case "vault.ssl.key-store-type":
-                        return sslContextService.getKeyStoreType();
-                    case "vault.ssl.trust-store":
-                        return sslContextService.getTrustStoreFile();
-                    case "vault.ssl.trust-store-password":
-                        return sslContextService.getTrustStorePassword();
-                    case "vault.ssl.trust-store-type":
-                        return sslContextService.getTrustStoreType();
-                    case "vault.ssl.enabledProtocols":
-                        return sslContextService.getSslAlgorithm();
-                }
-            }
-
-            return null;
-        }
     }
 }

Reply via email to