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

exceptionfactory 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 f828907df5 NIFI-12080 Added support for KV_2 in HashiCorp Parameter 
Provider
f828907df5 is described below

commit f828907df50de98abdc73cb6d1c08cba4c4f8271
Author: Pierre Villard <[email protected]>
AuthorDate: Wed Aug 28 22:12:41 2024 +0200

    NIFI-12080 Added support for KV_2 in HashiCorp Parameter Provider
    
    This closes #9209
    
    Signed-off-by: David Handermann <[email protected]>
---
 .../HashiCorpVaultCommunicationService.java        | 19 +++++++++++++++++
 ...StandardHashiCorpVaultCommunicationService.java | 18 ++++++++++------
 .../hashicorp/HashiCorpVaultParameterProvider.java | 24 ++++++++++++++++++----
 .../TestHashiCorpVaultParameterProvider.java       | 18 ++++++++++------
 4 files changed, 63 insertions(+), 16 deletions(-)

diff --git 
a/nifi-commons/nifi-hashicorp-vault-api/src/main/java/org/apache/nifi/vault/hashicorp/HashiCorpVaultCommunicationService.java
 
b/nifi-commons/nifi-hashicorp-vault-api/src/main/java/org/apache/nifi/vault/hashicorp/HashiCorpVaultCommunicationService.java
index adbb4764b0..33ab496f66 100644
--- 
a/nifi-commons/nifi-hashicorp-vault-api/src/main/java/org/apache/nifi/vault/hashicorp/HashiCorpVaultCommunicationService.java
+++ 
b/nifi-commons/nifi-hashicorp-vault-api/src/main/java/org/apache/nifi/vault/hashicorp/HashiCorpVaultCommunicationService.java
@@ -89,10 +89,29 @@ public interface HashiCorpVaultCommunicationService {
      */
     Map<String, String> readKeyValueSecretMap(String keyValuePath, String 
secretKey);
 
+    /**
+     * Reads a secret with multiple key/value pairs from Vault's Key/Value 
Secrets Engine.
+     *
+     * @see <a 
href="https://www.vaultproject.io/api-docs/secret/kv";>https://www.vaultproject.io/api-docs/secret/kv</a>
+     * @param keyValuePath The Vault path to use for the configured Key/Value 
Secrets Engine
+     * @param secretKey The secret key
+     * @param version the Key/Vault Secrets engine version
+     * @return A map from key to value from the secret key/values, or an empty 
map if not found
+     */
+    Map<String, String> readKeyValueSecretMap(String keyValuePath, String 
secretKey, String version);
+
     /**
      * Lists the secrets at the given Key/Value Version 1 Secrets Engine path.
      * @param keyValuePath The Vault path to list
      * @return The list of secret names
      */
     List<String> listKeyValueSecrets(String keyValuePath);
+
+    /**
+     * Lists the secrets at the given Key/Value Secrets Engine path.
+     * @param keyValuePath The Vault path to list
+     * @param version the Key/Vault Secrets engine version
+     * @return The list of secret names
+     */
+    List<String> listKeyValueSecrets(String keyValuePath, String version);
 }
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 38a6e5dfc6..533672af46 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
@@ -136,17 +136,23 @@ public class StandardHashiCorpVaultCommunicationService 
implements HashiCorpVaul
 
     @Override
     public Map<String, String> readKeyValueSecretMap(final String 
keyValuePath, final String key) {
-        final VaultKeyValueOperations keyValueOperations = 
keyValueOperationsMap
-                .computeIfAbsent(keyValuePath, path -> 
vaultTemplate.opsForKeyValue(path, keyValueBackend));
-        final VaultResponseSupport<Map> response = keyValueOperations.get(key, 
Map.class);
+        return readKeyValueSecretMap(keyValuePath, key, 
keyValueBackend.name());
+    }
+
+    @Override
+    public Map<String, String> readKeyValueSecretMap(final String 
keyValuePath, final String key, final String version) {
+        final VaultResponseSupport<Map> response = 
vaultTemplate.opsForKeyValue(keyValuePath, 
KeyValueBackend.valueOf(version)).get(key, Map.class);
         return response == null ? Collections.emptyMap() : (Map<String, 
String>) response.getRequiredData();
     }
 
     @Override
     public List<String> listKeyValueSecrets(final String keyValuePath) {
-        final VaultKeyValueOperations keyValueOperations = 
keyValueOperationsMap
-                .computeIfAbsent(keyValuePath, path -> 
vaultTemplate.opsForKeyValue(path, KeyValueBackend.KV_1));
-        return keyValueOperations.list("/");
+        return listKeyValueSecrets(keyValuePath, KeyValueBackend.KV_1.name());
+    }
+
+    @Override
+    public List<String> listKeyValueSecrets(final String keyValuePath, final 
String version) {
+        return vaultTemplate.opsForKeyValue(keyValuePath, 
KeyValueBackend.valueOf(version)).list("/");
     }
 
     private static class SecretData {
diff --git 
a/nifi-extension-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-parameter-provider/src/main/java/org/apache/nifi/vault/hashicorp/HashiCorpVaultParameterProvider.java
 
b/nifi-extension-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-parameter-provider/src/main/java/org/apache/nifi/vault/hashicorp/HashiCorpVaultParameterProvider.java
index 624e976aef..26d71fbb55 100644
--- 
a/nifi-extension-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-parameter-provider/src/main/java/org/apache/nifi/vault/hashicorp/HashiCorpVaultParameterProvider.java
+++ 
b/nifi-extension-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-parameter-provider/src/main/java/org/apache/nifi/vault/hashicorp/HashiCorpVaultParameterProvider.java
@@ -18,6 +18,7 @@ package org.apache.nifi.vault.hashicorp;
 
 import org.apache.nifi.annotation.documentation.CapabilityDescription;
 import org.apache.nifi.annotation.documentation.Tags;
+import org.apache.nifi.components.AllowableValue;
 import org.apache.nifi.components.ConfigVerificationResult;
 import org.apache.nifi.components.PropertyDescriptor;
 import org.apache.nifi.components.Validator;
@@ -37,11 +38,15 @@ import java.util.List;
 import java.util.Map;
 import java.util.stream.Collectors;
 
-@CapabilityDescription("Provides parameters from HashiCorp Vault Key/Value 
Version 1 Secrets.  Each Secret represents a parameter group, " +
+@CapabilityDescription("Provides parameters from HashiCorp Vault Key/Value 
Version 1 and Version 2 Secrets.  Each Secret represents a parameter group, " +
         "which will map to a Parameter Context.  The keys and values in the 
Secret map to Parameters.")
 @Tags({"hashicorp", "vault", "secret"})
 public class HashiCorpVaultParameterProvider extends AbstractParameterProvider 
implements ParameterProvider, VerifiableParameterProvider {
 
+    static final AllowableValue KV_1 = new AllowableValue("KV_1", "KV_1", 
"Key/Value Secret Engine Version 1.");
+    static final AllowableValue KV_2 = new AllowableValue("KV_2", "KV_2", 
"Key/Value Secret Engine Version 2. "
+            + "If multiple versions of the secret exist, latest will be 
used.");
+
     public static final PropertyDescriptor VAULT_CLIENT_SERVICE = new 
PropertyDescriptor.Builder()
             .name("vault-client-service")
             .displayName("HashiCorp Vault Client Service")
@@ -53,11 +58,20 @@ public class HashiCorpVaultParameterProvider extends 
AbstractParameterProvider i
     public static final PropertyDescriptor KV_PATH = new 
PropertyDescriptor.Builder()
             .name("kv-path")
             .displayName("Key/Value Path")
-            .description("The HashiCorp Vault path to the Key/Value Version 1 
Secrets Engine")
+            .description("The HashiCorp Vault path to the Key/Value Secrets 
Engine")
             .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
             .required(true)
             .defaultValue("kv")
             .build();
+    public static final PropertyDescriptor KV_VERSION = new 
PropertyDescriptor.Builder()
+            .name("kv-version")
+            .displayName("Key/Value Version")
+            .description("The version of the Key/Value Secrets Engine")
+            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
+            .required(true)
+            .allowableValues(KV_1, KV_2)
+            .defaultValue(KV_1)
+            .build();
     public static final PropertyDescriptor SECRET_NAME_PATTERN = new 
PropertyDescriptor.Builder()
             .name("secret-name-pattern")
             .displayName("Secret Name Pattern")
@@ -70,6 +84,7 @@ public class HashiCorpVaultParameterProvider extends 
AbstractParameterProvider i
     private static final List<PropertyDescriptor> PROPERTIES = 
Collections.unmodifiableList(Arrays.asList(
             VAULT_CLIENT_SERVICE,
             KV_PATH,
+            KV_VERSION,
             SECRET_NAME_PATTERN));
 
     private HashiCorpVaultCommunicationService vaultCommunicationService;
@@ -92,15 +107,16 @@ public class HashiCorpVaultParameterProvider extends 
AbstractParameterProvider i
     private List<ParameterGroup> getParameterGroups(final 
HashiCorpVaultCommunicationService vaultCommunicationService,
                                                             final 
ConfigurationContext context) {
         final String kvPath = context.getProperty(KV_PATH).getValue();
+        final String kvVersion = context.getProperty(KV_VERSION).getValue();
         final String secretIncludeRegex = 
context.getProperty(SECRET_NAME_PATTERN).getValue();
-        final List<String> allSecretNames = 
vaultCommunicationService.listKeyValueSecrets(kvPath);
+        final List<String> allSecretNames = 
vaultCommunicationService.listKeyValueSecrets(kvPath, kvVersion);
         final List<String> secretNames = allSecretNames.stream()
                 .filter(name -> name.matches(secretIncludeRegex))
                 .collect(Collectors.toList());
 
         final List<ParameterGroup> parameterGroups = new ArrayList<>();
         for (final String secretName : secretNames) {
-            final Map<String, String> keyValues = 
vaultCommunicationService.readKeyValueSecretMap(kvPath, secretName);
+            final Map<String, String> keyValues = 
vaultCommunicationService.readKeyValueSecretMap(kvPath, secretName, kvVersion);
             final List<Parameter> parameters = new ArrayList<>();
             keyValues.forEach( (key, value) -> {
                 parameters.add(new Parameter.Builder()
diff --git 
a/nifi-extension-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-parameter-provider/src/test/java/org/apache/nifi/vault/hashicorp/TestHashiCorpVaultParameterProvider.java
 
b/nifi-extension-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-parameter-provider/src/test/java/org/apache/nifi/vault/hashicorp/TestHashiCorpVaultParameterProvider.java
index 5973da3928..023152a95c 100644
--- 
a/nifi-extension-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-parameter-provider/src/test/java/org/apache/nifi/vault/hashicorp/TestHashiCorpVaultParameterProvider.java
+++ 
b/nifi-extension-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-parameter-provider/src/test/java/org/apache/nifi/vault/hashicorp/TestHashiCorpVaultParameterProvider.java
@@ -84,10 +84,12 @@ public class TestHashiCorpVaultParameterProvider {
 
     @Test
     public void testFetchParameters() {
-        mockSecrets("kv2", mockedGroups);
+        final String kvVersion = "KV_1";
+        mockSecrets("kv2", kvVersion, mockedGroups);
 
         final Map<PropertyDescriptor, String> properties = new HashMap<>();
         properties.put(HashiCorpVaultParameterProvider.KV_PATH, "kv2");
+        properties.put(HashiCorpVaultParameterProvider.KV_VERSION, kvVersion);
         properties.put(HashiCorpVaultParameterProvider.VAULT_CLIENT_SERVICE, 
"service");
         properties.put(HashiCorpVaultParameterProvider.SECRET_NAME_PATTERN, 
".*");
         final ConfigurationContext context = mockContext(properties);
@@ -101,10 +103,12 @@ public class TestHashiCorpVaultParameterProvider {
 
     @Test
     public void testFetchParametersSecretRegex() {
-        mockSecrets("kv2", mockedGroups);
+        final String kvVersion = "KV_2";
+        mockSecrets("kv2", kvVersion, mockedGroups);
 
         final Map<PropertyDescriptor, String> properties = new HashMap<>();
         properties.put(HashiCorpVaultParameterProvider.KV_PATH, "kv2");
+        properties.put(HashiCorpVaultParameterProvider.KV_VERSION, kvVersion);
         properties.put(HashiCorpVaultParameterProvider.VAULT_CLIENT_SERVICE, 
"service");
         properties.put(HashiCorpVaultParameterProvider.SECRET_NAME_PATTERN, 
".*A");
         final ConfigurationContext context = mockContext(properties);
@@ -118,10 +122,12 @@ public class TestHashiCorpVaultParameterProvider {
 
     @Test
     public void testVerifyParameters() {
-        mockSecrets("kv2", mockedGroups);
+        final String kvVersion = "KV_1";
+        mockSecrets("kv2", kvVersion, mockedGroups);
 
         final Map<PropertyDescriptor, String> properties = new HashMap<>();
         properties.put(HashiCorpVaultParameterProvider.KV_PATH, "kv2");
+        properties.put(HashiCorpVaultParameterProvider.KV_VERSION, kvVersion);
         properties.put(HashiCorpVaultParameterProvider.VAULT_CLIENT_SERVICE, 
"service");
         properties.put(HashiCorpVaultParameterProvider.SECRET_NAME_PATTERN, 
".*");
         final ConfigurationContext context = mockContext(properties);
@@ -145,13 +151,13 @@ public class TestHashiCorpVaultParameterProvider {
         
lenient().when(context.getProperty(descriptor)).thenReturn(propertyValue);
     }
 
-    private void mockSecrets(final String kvPath, final List<ParameterGroup> 
parameterGroups) {
-        when(vaultCommunicationService.listKeyValueSecrets(kvPath))
+    private void mockSecrets(final String kvPath, final String kvVersion, 
final List<ParameterGroup> parameterGroups) {
+        when(vaultCommunicationService.listKeyValueSecrets(kvPath, kvVersion))
                 .thenReturn(parameterGroups.stream().map(group -> 
group.getGroupName()).collect(Collectors.toList()));
         for (final ParameterGroup parameterGroup : parameterGroups) {
             final Map<String, String> keyValues = 
parameterGroup.getParameters().stream()
                     .collect(Collectors.toMap(parameter -> 
parameter.getDescriptor().getName(), parameter -> parameter.getValue()));
-            
lenient().when(vaultCommunicationService.readKeyValueSecretMap(kvPath, 
parameterGroup.getGroupName())).thenReturn(keyValues);
+            
lenient().when(vaultCommunicationService.readKeyValueSecretMap(kvPath, 
parameterGroup.getGroupName(), kvVersion)).thenReturn(keyValues);
         }
     }
 

Reply via email to