You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ac...@apache.org on 2022/04/01 12:28:10 UTC
[camel] 03/06: CAMEL-17686 - Support ability to load properties from Vault/Secrets cloud services - Azure Key Vault
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.git
commit 7f2e58d6a94eb7747b83e34eb824916fd883caf8
Author: Andrea Cosentino <an...@gmail.com>
AuthorDate: Fri Apr 1 12:55:31 2022 +0200
CAMEL-17686 - Support ability to load properties from Vault/Secrets cloud services - Azure Key Vault
---
.../org/apache/camel/properties-function/azure | 2 +
.../key/vault/KeyVaultPropertiesFunction.java | 192 +++++++++++++++++++++
2 files changed, 194 insertions(+)
diff --git a/components/camel-azure/camel-azure-key-vault/src/generated/resources/META-INF/services/org/apache/camel/properties-function/azure b/components/camel-azure/camel-azure-key-vault/src/generated/resources/META-INF/services/org/apache/camel/properties-function/azure
new file mode 100644
index 0000000..8536f13
--- /dev/null
+++ b/components/camel-azure/camel-azure-key-vault/src/generated/resources/META-INF/services/org/apache/camel/properties-function/azure
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.component.azure.key.vault.KeyVaultPropertiesFunction
diff --git a/components/camel-azure/camel-azure-key-vault/src/main/java/org/apache/camel/component/azure/key/vault/KeyVaultPropertiesFunction.java b/components/camel-azure/camel-azure-key-vault/src/main/java/org/apache/camel/component/azure/key/vault/KeyVaultPropertiesFunction.java
new file mode 100644
index 0000000..ea3e105
--- /dev/null
+++ b/components/camel-azure/camel-azure-key-vault/src/main/java/org/apache/camel/component/azure/key/vault/KeyVaultPropertiesFunction.java
@@ -0,0 +1,192 @@
+/*
+ * 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.azure.key.vault;
+
+import com.azure.identity.ClientSecretCredential;
+import com.azure.identity.ClientSecretCredentialBuilder;
+import com.azure.security.keyvault.secrets.SecretClient;
+import com.azure.security.keyvault.secrets.SecretClientBuilder;
+import com.azure.security.keyvault.secrets.models.KeyVaultSecret;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.camel.CamelContext;
+import org.apache.camel.CamelContextAware;
+import org.apache.camel.RuntimeCamelException;
+import org.apache.camel.spi.PropertiesFunction;
+import org.apache.camel.support.service.ServiceSupport;
+import org.apache.camel.util.ObjectHelper;
+import org.apache.camel.util.StringHelper;
+import org.apache.camel.vault.AzureVaultConfiguration;
+
+/**
+ * A {@link PropertiesFunction} that lookup the property value from Azure Key Vault service.
+ * <p/>
+ * The credentials to access Key vault is defined using three environment variables representing the static credentials:
+ * <ul>
+ * <li><tt>CAMEL_VAULT_AZURE_VAULT_NAME</tt></li>
+ * <li><tt>CAMEL_VAULT_AZURE_CLIENT_ID</tt></li>
+ * <li><tt>CAMEL_VAULT_AZURE_CLIENT_SECRET</tt></li>
+ * <li><tt>CAMEL_VAULT_AZURE_TENANT_ID</tt></li>
+ * </ul>
+ * <p/>
+ *
+ * Otherwise it is possible to specify the credentials as properties:
+ *
+ * <ul>
+ * <li><tt>camel.vault.azure.vaultName</tt></li>
+ * <li><tt>camel.vault.azure.clientId</tt></li>
+ * <li><tt>camel.vault.azure.clientSecret</tt></li>
+ * <li><tt>camel.vault.azure.tenantId</tt></li>
+ * </ul>
+ * <p/>
+ *
+ * This implementation is to return the secret value associated with a key. The properties related to this kind of
+ * Properties Function are all prefixed with <tt>azure:</tt>. For example asking for <tt>azure:token</tt>, will return
+ * the secret value associated to the secret named token on AWS Secrets Manager.
+ *
+ * Another way of retrieving a secret value is using the following notation <tt>azure:database/username</tt>: in this
+ * case the field username of the secret database will be returned. As a fallback, the user could provide a default
+ * value, which will be returned in case the secret doesn't exist, the secret has been marked for deletion or, for
+ * example, if a particular field of the secret doesn't exist. For using this feature, the user could use the following
+ * notation <tt>azure:database/username:admin</tt>. The admin value will be returned as default value, if the conditions
+ * above were all met.
+ */
+
+@org.apache.camel.spi.annotations.PropertiesFunction("azure")
+public class KeyVaultPropertiesFunction extends ServiceSupport implements PropertiesFunction, CamelContextAware {
+
+ private static final String CAMEL_VAULT_AZURE_VAULT_NAME = "CAMEL_VAULT_AZURE_VAULT_NAME";
+ private static final String CAMEL_VAULT_AZURE_CLIENT_ID = "CAMEL_VAULT_AZURE_CLIENT_ID";
+ private static final String CAMEL_VAULT_AZURE_CLIENT_SECRET = "CAMEL_VAULT_AZURE_CLIENT_SECRET";
+ private static final String CAMEL_VAULT_AZURE_TENANT_ID = "CAMEL_VAULT_AZURE_TENANT_ID";
+ private CamelContext camelContext;
+ private SecretClient client;
+
+ @Override
+ protected void doStart() throws Exception {
+ super.doStart();
+ String vaultName = System.getenv(CAMEL_VAULT_AZURE_VAULT_NAME);
+ String clientId = System.getenv(CAMEL_VAULT_AZURE_CLIENT_ID);
+ String clientSecret = System.getenv(CAMEL_VAULT_AZURE_CLIENT_SECRET);
+ String tenantId = System.getenv(CAMEL_VAULT_AZURE_TENANT_ID);
+ if (ObjectHelper.isEmpty(vaultName) && ObjectHelper.isEmpty(clientId) && ObjectHelper.isEmpty(clientSecret)
+ && ObjectHelper.isEmpty(tenantId)) {
+ AzureVaultConfiguration azureVaultConfiguration = getCamelContext().getVaultConfiguration().azure();
+ if (ObjectHelper.isNotEmpty(azureVaultConfiguration)) {
+ vaultName = azureVaultConfiguration.getVaultName();
+ clientId = azureVaultConfiguration.getClientId();
+ clientSecret = azureVaultConfiguration.getClientSecret();
+ tenantId = azureVaultConfiguration.getTenantId();
+ }
+ }
+ if (ObjectHelper.isNotEmpty(vaultName) && ObjectHelper.isNotEmpty(clientId) && ObjectHelper.isNotEmpty(clientSecret)
+ && ObjectHelper.isNotEmpty(tenantId)) {
+ String keyVaultUri = "https://" + vaultName + ".vault.azure.net";
+
+ // Credential
+ ClientSecretCredential credential = new ClientSecretCredentialBuilder()
+ .tenantId(tenantId)
+ .clientId(clientId)
+ .clientSecret(clientSecret)
+ .build();
+
+ // Build Client
+ client = new SecretClientBuilder()
+ .vaultUrl(keyVaultUri)
+ .credential(credential)
+ .buildClient();
+ } else {
+ throw new RuntimeCamelException(
+ "Using the Azure Key Vault Properties Function requires setting Azure credentials as application properties or environment variables");
+ }
+ }
+
+ @Override
+ public String getName() {
+ return "azure";
+ }
+
+ @Override
+ public String apply(String remainder) {
+ String key = remainder;
+ String subkey = null;
+ String returnValue = null;
+ String defaultValue = null;
+ if (remainder.contains("/")) {
+ key = StringHelper.before(remainder, "/");
+ subkey = StringHelper.after(remainder, "/");
+ defaultValue = StringHelper.after(subkey, ":");
+ if (subkey.contains(":")) {
+ subkey = StringHelper.before(subkey, ":");
+ }
+ } else if (remainder.contains(":")) {
+ key = StringHelper.before(remainder, ":");
+ defaultValue = StringHelper.after(remainder, ":");
+ }
+
+ if (key != null) {
+ try {
+ returnValue = getSecretFromSource(key, subkey, defaultValue);
+ } catch (JsonProcessingException e) {
+ throw new RuntimeCamelException("Something went wrong while recovering " + key + " from vault");
+ }
+ }
+
+ return returnValue;
+ }
+
+ private String getSecretFromSource(
+ String key, String subkey, String defaultValue)
+ throws JsonProcessingException {
+ String returnValue;
+ try {
+ KeyVaultSecret secret = client.getSecret(key);
+ returnValue = secret.getValue();
+ if (ObjectHelper.isNotEmpty(subkey)) {
+ ObjectMapper mapper = new ObjectMapper();
+ JsonNode actualObj = mapper.readTree(returnValue);
+ JsonNode field = actualObj.get(subkey);
+ if (ObjectHelper.isNotEmpty(field)) {
+ returnValue = field.textValue();
+ } else {
+ returnValue = null;
+ }
+ }
+ if (ObjectHelper.isEmpty(returnValue)) {
+ returnValue = defaultValue;
+ }
+ } catch (Exception ex) {
+ if (ObjectHelper.isNotEmpty(defaultValue)) {
+ returnValue = defaultValue;
+ } else {
+ throw ex;
+ }
+ }
+ return returnValue;
+ }
+
+ @Override
+ public void setCamelContext(CamelContext camelContext) {
+ this.camelContext = camelContext;
+ }
+
+ @Override
+ public CamelContext getCamelContext() {
+ return camelContext;
+ }
+}