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;
+    }
+}