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

acosentino pushed a commit to branch CAMEL-21834
in repository https://gitbox.apache.org/repos/asf/camel-spring-boot.git

commit 9cdd0b00bab4ac85a9c55c5cf23da97686e7772a
Author: Andrea Cosentino <anco...@gmail.com>
AuthorDate: Thu Mar 6 14:38:58 2025 +0100

    CAMEL-21834 - Camel-Spring-Boot: Create ApplicationEnvironmentPreparedEvent 
for Vault IBM Secrets Manager
    
    Signed-off-by: Andrea Cosentino <anco...@gmail.com>
---
 .../camel-ibm-secrets-manager-starter/pom.xml      |  12 ++
 .../IBMSecretsManagerVaultPropertiesParser.java    |  74 ++++++++++++
 .../src/main/resources/META-INF/spring.factories   |   2 +
 .../src/test/java/EarlyResolvedPropertiesTest.java | 126 +++++++++++++++++++++
 .../src/test/resources/application.properties      |   2 +
 5 files changed, 216 insertions(+)

diff --git a/components-starter/camel-ibm-secrets-manager-starter/pom.xml 
b/components-starter/camel-ibm-secrets-manager-starter/pom.xml
index 39d3174d7c1..50c85d550e5 100644
--- a/components-starter/camel-ibm-secrets-manager-starter/pom.xml
+++ b/components-starter/camel-ibm-secrets-manager-starter/pom.xml
@@ -38,6 +38,18 @@
       <artifactId>camel-ibm-secrets-manager</artifactId>
       <version>${camel-version}</version>
     </dependency>
+    <!-- for testing -->
+    <dependency>
+      <groupId>org.awaitility</groupId>
+      <artifactId>awaitility</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-test</artifactId>
+      <version>${spring-boot-version}</version>
+      <scope>test</scope>
+    </dependency>
     <!--START OF GENERATED CODE-->
     <dependency>
       <groupId>org.apache.camel.springboot</groupId>
diff --git 
a/components-starter/camel-ibm-secrets-manager-starter/src/main/java/org/apache/camel/component/ibm/secrets/manager/springboot/IBMSecretsManagerVaultPropertiesParser.java
 
b/components-starter/camel-ibm-secrets-manager-starter/src/main/java/org/apache/camel/component/ibm/secrets/manager/springboot/IBMSecretsManagerVaultPropertiesParser.java
new file mode 100644
index 00000000000..82f42dcbcec
--- /dev/null
+++ 
b/components-starter/camel-ibm-secrets-manager-starter/src/main/java/org/apache/camel/component/ibm/secrets/manager/springboot/IBMSecretsManagerVaultPropertiesParser.java
@@ -0,0 +1,74 @@
+package org.apache.camel.component.ibm.secrets.manager.springboot;
+
+import com.ibm.cloud.sdk.core.security.IamAuthenticator;
+import com.ibm.cloud.secrets_manager_sdk.secrets_manager.v2.SecretsManager;
+import org.apache.camel.RuntimeCamelException;
+import 
org.apache.camel.component.ibm.secrets.manager.IBMSecretsManagerPropertiesFunction;
+import org.apache.camel.util.ObjectHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import 
org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
+import org.springframework.boot.origin.OriginTrackedValue;
+import org.springframework.context.ApplicationListener;
+import org.springframework.core.env.ConfigurableEnvironment;
+import org.springframework.core.env.MapPropertySource;
+import org.springframework.core.env.PropertiesPropertySource;
+import org.springframework.core.env.PropertySource;
+
+import java.util.Properties;
+
+public class IBMSecretsManagerVaultPropertiesParser implements 
ApplicationListener<ApplicationEnvironmentPreparedEvent> {
+    private static final Logger LOG = 
LoggerFactory.getLogger(IBMSecretsManagerVaultPropertiesParser.class);
+
+    @Override
+    public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
+        SecretsManager client;
+        ConfigurableEnvironment environment = event.getEnvironment();
+        String token;
+        String serviceUrl;
+        if 
(Boolean.parseBoolean(environment.getProperty("camel.component.ibm-secrets-manager.early-resolve-properties")))
 {
+            token = environment.getProperty("camel.vault.ibm.token");
+            serviceUrl = environment.getProperty("camel.vault.ibm.serviceUrl");
+            if (ObjectHelper.isNotEmpty(token) && 
ObjectHelper.isNotEmpty(serviceUrl)) {
+                IamAuthenticator iamAuthenticator = new 
IamAuthenticator.Builder()
+                        .apikey(token)
+                        .build();
+                client = new SecretsManager("Camel Secrets Manager Service for 
Properties", iamAuthenticator);
+                client.setServiceUrl(serviceUrl);
+            } else {
+                throw new RuntimeCamelException(
+                        "Using the IBM Secrets Manager Properties Function 
requires setting IBM Credentials and service url as application properties or 
environment variables");
+            }
+            IBMSecretsManagerPropertiesFunction 
secretsManagerPropertiesFunction = new 
IBMSecretsManagerPropertiesFunction(client);
+            final Properties props = new Properties();
+            for (PropertySource mutablePropertySources : 
event.getEnvironment().getPropertySources()) {
+                if (mutablePropertySources instanceof MapPropertySource 
mapPropertySource) {
+                    mapPropertySource.getSource().forEach((key, value) -> {
+                        String stringValue = null;
+                        if ((value instanceof OriginTrackedValue 
originTrackedValue &&
+                                originTrackedValue.getValue() instanceof 
String v)) {
+                            stringValue = v;
+                        } else if (value instanceof String v) {
+                            stringValue = v;
+                        }
+                        if (stringValue != null &&
+                                stringValue.startsWith("{{ibm:") &&
+                                stringValue.endsWith("}}")) {
+                            LOG.debug("decrypting and overriding property {}", 
key);
+                            try {
+                                String element = 
secretsManagerPropertiesFunction.apply(stringValue
+                                        .replace("{{ibm:", "")
+                                        .replace("}}", ""));
+                                props.put(key, element);
+                            } catch (Exception e) {
+                                // Log and do nothing
+                                LOG.debug("failed to parse property {}. This 
exception is ignored.", key, e);
+                            }
+                        }
+                    });
+                }
+            }
+            environment.getPropertySources().addFirst(new 
PropertiesPropertySource("overridden-ibm-secrets-manager-properties", props));
+        }
+    }
+}
\ No newline at end of file
diff --git 
a/components-starter/camel-ibm-secrets-manager-starter/src/main/resources/META-INF/spring.factories
 
b/components-starter/camel-ibm-secrets-manager-starter/src/main/resources/META-INF/spring.factories
new file mode 100644
index 00000000000..0cd64be49cc
--- /dev/null
+++ 
b/components-starter/camel-ibm-secrets-manager-starter/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,2 @@
+org.springframework.context.ApplicationListener=\
+ 
org.apache.camel.component.ibm.secrets.manager.springboot.IBMSecretsManagerVaultPropertiesParser
\ No newline at end of file
diff --git 
a/components-starter/camel-ibm-secrets-manager-starter/src/test/java/EarlyResolvedPropertiesTest.java
 
b/components-starter/camel-ibm-secrets-manager-starter/src/test/java/EarlyResolvedPropertiesTest.java
new file mode 100644
index 00000000000..9c30283ac5e
--- /dev/null
+++ 
b/components-starter/camel-ibm-secrets-manager-starter/src/test/java/EarlyResolvedPropertiesTest.java
@@ -0,0 +1,126 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.ibm.secrets.manager.springboot;
+
+import com.ibm.cloud.sdk.core.http.Response;
+import com.ibm.cloud.sdk.core.security.IamAuthenticator;
+import com.ibm.cloud.secrets_manager_sdk.secrets_manager.v2.SecretsManager;
+import 
com.ibm.cloud.secrets_manager_sdk.secrets_manager.v2.model.CreateSecretOptions;
+import 
com.ibm.cloud.secrets_manager_sdk.secrets_manager.v2.model.DeleteSecretOptions;
+import 
com.ibm.cloud.secrets_manager_sdk.secrets_manager.v2.model.KVSecretPrototype;
+import com.ibm.cloud.secrets_manager_sdk.secrets_manager.v2.model.Secret;
+import 
org.apache.camel.component.ibm.secrets.manager.IBMSecretsManagerConstants;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.apache.camel.test.spring.junit5.CamelSpringBootTest;
+import org.apache.camel.util.ObjectHelper;
+import org.assertj.core.api.Assertions;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
+import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariables;
+import org.junit.jupiter.api.condition.EnabledIfSystemProperties;
+import org.junit.jupiter.api.condition.EnabledIfSystemProperty;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.AutoConfigureBefore;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.annotation.DirtiesContext;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+@CamelSpringBootTest
+@DirtiesContext
+@SpringBootApplication
+@SpringBootTest(
+        classes = { EarlyResolvedPropertiesTest.TestConfiguration.class },
+        properties = {
+                
"camel.component.ibm-secrets-manager.early-resolve-properties=true",
+                
"early.resolved.property.simple={{ibm:default:databaseTestPassword#username}}"
+        })
+
+// Must be manually tested. Provide your own accessKey and secretKey using 
-Dsecrets-manager and -Dcamel.ibm.sm.serviceurl
+@EnabledIfSystemProperties({
+        @EnabledIfSystemProperty(named = "camel.ibm.test.sm.token", matches = 
".*",
+                disabledReason = "Secrets Manager Token not provided"),
+        @EnabledIfSystemProperty(named = "camel.ibm.test.sm.serviceurl", 
matches = ".*",
+                disabledReason = "Secrets Manager Service URL not provided")
+})
+public class EarlyResolvedPropertiesTest {
+
+    static SecretsManager client;
+
+    static String secretId = "";
+    @BeforeAll
+    public static void setup() throws IOException {
+        String token = System.getProperty("camel.ibm.test.sm.token");
+        String serviceUrl = System.getProperty("camel.ibm.test.sm.serviceurl");
+        System.setProperty("camel.vault.ibm.token", token);
+        System.setProperty("camel.vault.ibm.serviceUrl", serviceUrl);
+
+        IamAuthenticator iamAuthenticator = new IamAuthenticator.Builder()
+                .apikey(token)
+                .build();
+        client = new SecretsManager("Camel Secrets Manager Service for 
Properties", iamAuthenticator);
+        client.setServiceUrl(serviceUrl);
+
+        KVSecretPrototype.Builder kvSecretResourceBuilder = new 
KVSecretPrototype.Builder();
+        kvSecretResourceBuilder
+                    .name("databaseTestPassword");
+        Map<String, Object> payload = new HashMap<>();
+        payload.put("username", "admin");
+        payload.put("password", "password");
+        kvSecretResourceBuilder.data(payload);
+        kvSecretResourceBuilder.secretType(KVSecretPrototype.SecretType.KV);
+        KVSecretPrototype kvSecretResource = kvSecretResourceBuilder.build();
+
+        CreateSecretOptions createSecretOptions = new 
CreateSecretOptions.Builder()
+                .secretPrototype(kvSecretResource)
+                .build();
+        Response<Secret> createResp = 
client.createSecret(createSecretOptions).execute();
+
+        secretId = createResp.getResult().getId();
+    }
+
+    @AfterAll
+    public static void teardown() throws IOException {
+
+        DeleteSecretOptions.Builder deleteSecretOptionsBuilder = new 
DeleteSecretOptions.Builder();
+        deleteSecretOptionsBuilder.id(secretId);
+        client.deleteSecret(deleteSecretOptionsBuilder.build());
+    }
+
+    @Value("${early.resolved.property}")
+    private String earlyResolvedProperty;
+
+    @Value("${early.resolved.property.simple}")
+    private String earlyResolvedPropertySimple;
+
+    @Test
+    public void testEarlyResolvedProperties() {
+        Assertions.assertThat(earlyResolvedProperty).isEqualTo("admin");
+        Assertions.assertThat(earlyResolvedPropertySimple).isEqualTo("admin");
+    }
+
+    @Configuration
+    @AutoConfigureBefore(CamelAutoConfiguration.class)
+    public static class TestConfiguration {
+    }
+}
diff --git 
a/components-starter/camel-ibm-secrets-manager-starter/src/test/resources/application.properties
 
b/components-starter/camel-ibm-secrets-manager-starter/src/test/resources/application.properties
new file mode 100644
index 00000000000..d36a9066a6d
--- /dev/null
+++ 
b/components-starter/camel-ibm-secrets-manager-starter/src/test/resources/application.properties
@@ -0,0 +1,2 @@
+# Needed by EarlyResolvedPropertiesTest
+early.resolved.property = {{ibm:default:databaseTestPassword#username}}
\ No newline at end of file

Reply via email to