This is an automated email from the ASF dual-hosted git repository.
acosentino pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-spring-boot.git
The following commit(s) were added to refs/heads/main by this push:
new 35488e9e5e5 CAMEL-21834 - Camel-Spring-Boot: Create
ApplicationEnvironmentPreparedEvent for Vault IBM Secrets Manager (#1389)
35488e9e5e5 is described below
commit 35488e9e5e5b43057b0ea241f23ecdd159581b9c
Author: Andrea Cosentino <[email protected]>
AuthorDate: Thu Mar 6 14:59:39 2025 +0100
CAMEL-21834 - Camel-Spring-Boot: Create ApplicationEnvironmentPreparedEvent
for Vault IBM Secrets Manager (#1389)
* CAMEL-21834 - Camel-Spring-Boot: Create
ApplicationEnvironmentPreparedEvent for Vault IBM Secrets Manager
Signed-off-by: Andrea Cosentino <[email protected]>
* Fixed Camel-Jira-starter tests
Signed-off-by: Andrea Cosentino <[email protected]>
---------
Signed-off-by: Andrea Cosentino <[email protected]>
---
.../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 +
.../component/jira/springboot/test/Utils.java | 14 +--
6 files changed, 223 insertions(+), 7 deletions(-)
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
diff --git
a/components-starter/camel-jira-starter/src/test/java/org/apache/camel/component/jira/springboot/test/Utils.java
b/components-starter/camel-jira-starter/src/test/java/org/apache/camel/component/jira/springboot/test/Utils.java
index 9d72882e084..136d12a6c0a 100644
---
a/components-starter/camel-jira-starter/src/test/java/org/apache/camel/component/jira/springboot/test/Utils.java
+++
b/components-starter/camel-jira-starter/src/test/java/org/apache/camel/component/jira/springboot/test/Utils.java
@@ -71,21 +71,21 @@ public final class Utils {
URI selfUri = URI.create(TEST_JIRA_URL + "/rest/api/latest/issue/" +
id);
return new Issue(summary, selfUri, KEY + "-" + id, id, null,
issueType, null, description, priority, null, null,
null, assignee, null, null, null, null, null, components,
null, null, null, null, null, null, null,
- watchers, null, null, null, null, null, null);
+ watchers, null, null, null, null, null);
}
public static Issue transitionIssueDone(Issue issue, Status status,
Resolution resolution) {
return new Issue(issue.getSummary(), issue.getSelf(), issue.getKey(),
issue.getId(), null, issue.getIssueType(),
status, issue.getDescription(), issue.getPriority(),
resolution, null, null, issue.getAssignee(), null,
null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null,
- null, null);
+ null);
}
public static Issue setPriority(Issue issue, Priority p) {
return new Issue(issue.getSummary(), issue.getSelf(), issue.getKey(),
issue.getId(), null, issue.getIssueType(),
issue.getStatus(), issue.getDescription(), p,
issue.getResolution(), null, null, issue.getAssignee(),
null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null,
- null, null, null);
+ null, null);
}
public static Issue transitionIssueDone(Issue issue) {
@@ -102,7 +102,7 @@ public final class Utils {
URI selfUri = URI.create(TEST_JIRA_URL + "/rest/api/latest/issue/" +
id);
return new Issue(summary, selfUri, KEY + "-" + id, id, null,
issueType, null, description, priority, null,
attachments, null, assignee, null, null, null, null, null,
null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null);
+ null, null, null, null, null, null, null);
}
public static Issue createIssueWithComments(long id, int numComments) {
@@ -120,21 +120,21 @@ public final class Utils {
URI selfUri = URI.create(TEST_JIRA_URL + "/rest/api/latest/issue/" +
id);
return new Issue("jira summary test " + id, selfUri, KEY + "-" + id,
id, null, issueType, null,
"Description " + id, null, null, null, null, userAssignee,
null, null, null, null, null, null, null,
- null, comments, null, null, null, null, null, null, null,
null, null, null, null);
+ null, comments, null, null, null, null, null, null, null,
null, null, null);
}
public static Issue createIssueWithLinks(long id, Collection<IssueLink>
issueLinks) {
URI selfUri = URI.create(TEST_JIRA_URL + "/rest/api/latest/issue/" +
id);
return new Issue("jira summary test " + id, selfUri, KEY + "-" + id,
id, null, issueType, null,
"Description " + id, null, null, null, null, userAssignee,
null, null, null, null, null, null, null,
- null, null, null, issueLinks, null, null, null, null, null,
null, null, null, null);
+ null, null, null, issueLinks, null, null, null, null, null,
null, null, null);
}
public static Issue createIssueWithWorkLogs(long id, Collection<Worklog>
worklogs) {
URI selfUri = URI.create(TEST_JIRA_URL + "/rest/api/latest/issue/" +
id);
return new Issue("jira summary test " + id, selfUri, KEY + "-" + id,
id, null, issueType, null,
"Description " + id, null, null, null, null, userAssignee,
null, null, null, null, null, null, null,
- null, null, null, null, null, worklogs, null, null, null,
null, null, null, null);
+ null, null, null, null, null, worklogs, null, null, null,
null, null, null);
}
public static Comment newComment(long issueId, int newCommentId, String
comment) {