From 7b1e8bdee593f2e9ecb10dbb390aef2e16cca204 Mon Sep 17 00:00:00 2001 From: Kaiyuan Li Date: Fri, 26 Jan 2024 20:27:09 -0800 Subject: [PATCH] add Azure Vault Provider --- .../configuration/AzureVaultJsonProvider.java | 101 ++++++++++++++++++ .../AzureVaultSecretProvider.java | 46 +------- .../configuration/AzureVaultURLParser.java | 93 ++++++++++++++++ ...racle.jdbc.spi.OracleConfigurationProvider | 1 + .../SimpleAzureVaultJsonExample.java | 101 ++++++++++++++++++ 5 files changed, 298 insertions(+), 44 deletions(-) create mode 100644 ojdbc-provider-azure/src/main/java/oracle/jdbc/provider/azure/configuration/AzureVaultJsonProvider.java create mode 100644 ojdbc-provider-azure/src/main/java/oracle/jdbc/provider/azure/configuration/AzureVaultURLParser.java create mode 100644 ojdbc-provider-samples/src/main/java/oracle/jdbc/provider/azure/configuration/SimpleAzureVaultJsonExample.java diff --git a/ojdbc-provider-azure/src/main/java/oracle/jdbc/provider/azure/configuration/AzureVaultJsonProvider.java b/ojdbc-provider-azure/src/main/java/oracle/jdbc/provider/azure/configuration/AzureVaultJsonProvider.java new file mode 100644 index 00000000..e7b969b4 --- /dev/null +++ b/ojdbc-provider-azure/src/main/java/oracle/jdbc/provider/azure/configuration/AzureVaultJsonProvider.java @@ -0,0 +1,101 @@ +/* + ** Copyright (c) 2023 Oracle and/or its affiliates. + ** + ** The Universal Permissive License (UPL), Version 1.0 + ** + ** Subject to the condition set forth below, permission is hereby granted to any + ** person obtaining a copy of this software, associated documentation and/or data + ** (collectively the "Software"), free of charge and under any and all copyright + ** rights in the Software, and any and all patent rights owned or freely + ** licensable by each licensor hereunder covering either (i) the unmodified + ** Software as contributed to or provided by such licensor, or (ii) the Larger + ** Works (as defined below), to deal in both + ** + ** (a) the Software, and + ** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + ** one is included with the Software (each a "Larger Work" to which the Software + ** is contributed by such licensors), + ** + ** without restriction, including without limitation the rights to copy, create + ** derivative works of, display, perform, and distribute the Software and make, + ** use, sell, offer for sale, import, export, have made, and have sold the + ** Software and the Larger Work(s), and to sublicense the foregoing rights on + ** either these or other terms. + ** + ** This license is subject to the following condition: + ** The above copyright notice and either this complete permission notice or at + ** a minimum a reference to the UPL must be included in all copies or + ** substantial portions of the Software. + ** + ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + ** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + ** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + ** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + ** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + ** SOFTWARE. + */ + +package oracle.jdbc.provider.azure.configuration; + +import oracle.jdbc.driver.OracleConfigurationJsonProvider; +import oracle.jdbc.provider.azure.keyvault.KeyVaultSecretFactory; +import oracle.jdbc.provider.parameter.ParameterSet; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Map; + +import static oracle.jdbc.provider.azure.configuration.AzureVaultURLParser.PARAMETER_SET_PARSER; + +/** + * A provider for JSON payload which contains configuration from Azure Vault. + * See {@link #getJson(String)} for the spec of the JSON payload. + */ +public class AzureVaultJsonProvider extends OracleConfigurationJsonProvider { + + /** + * {@inheritDoc} + *

+ * Returns the JSON payload stored in Azure Vault Secret. + *

The {@code secretIdentifier} is a identifier of Vault Secret which + * can be acquired on the Azure Web Console. The Json payload is stored in + * the Secret Value of Vault Secret. + *

+ * @param secretIdentifier the identifier of secret used by this + * provider to retrieve JSON payload from AZURE + * @return JSON payload + **/ + @Override + public InputStream getJson(String secretIdentifier) throws SQLException { + final String valueFieldName = "value"; + Map options = new HashMap<>(); + options.put(valueFieldName, secretIdentifier); + + ParameterSet parameters = PARAMETER_SET_PARSER.parseNamedValues(options); + + String secretContent = KeyVaultSecretFactory.getInstance() + .request(parameters) + .getContent() + .getValue(); + + InputStream inputStream = + new ByteArrayInputStream(secretContent.getBytes()); + + return inputStream; + } + + /** + * {@inheritDoc} + * Returns type of this provider, which is a unique identifier for the + * Service Provider Interface. + * + * @return type of this provider + */ + @Override + public String getType() { + return "vaultazure"; + } +} diff --git a/ojdbc-provider-azure/src/main/java/oracle/jdbc/provider/azure/configuration/AzureVaultSecretProvider.java b/ojdbc-provider-azure/src/main/java/oracle/jdbc/provider/azure/configuration/AzureVaultSecretProvider.java index f9cd78e6..693f8bdf 100644 --- a/ojdbc-provider-azure/src/main/java/oracle/jdbc/provider/azure/configuration/AzureVaultSecretProvider.java +++ b/ojdbc-provider-azure/src/main/java/oracle/jdbc/provider/azure/configuration/AzureVaultSecretProvider.java @@ -49,24 +49,14 @@ import java.util.Base64; +import static oracle.jdbc.provider.azure.configuration.AzureVaultURLParser.PARAMETER_SET_PARSER; + /** * A provider of Secret values from Azure Key Vault. */ public final class AzureVaultSecretProvider implements OracleConfigurationJsonSecretProvider { - /** - * Parser that recognizes the "value" field and parses it as a Key Vault - * secret URL. - * {@link AzureConfigurationParameters#configureBuilder(ParameterSetParser.Builder)} - * configures the parser to recognize fields of the nested JSON object named - * "authentication". - */ - private static final ParameterSetParser PARAMETER_SET_PARSER = - AzureConfigurationParameters.configureBuilder(ParameterSetParser.builder() - .addParameter("value", AzureVaultSecretProvider::parseVaultSecretUri)) - .build(); - /** * {@inheritDoc} *

@@ -113,36 +103,4 @@ public char[] getSecret(OracleJsonObject secretJsonObject) { public String getSecretType() { return "vault-azure"; } - - /** - * Parses the "value" field of a JSON object as a vault URI with the path - * of a named secret. An example URI is: - *

-   * https://mykeyvaultpfs.vault.azure.net/secrets/mySecret2
-   * 
- * This parser configures the given {@code builder} with two distinct - * parameters accepted by {@link KeyVaultSecretFactory}: One parameter for the - * vault URL, and another for the secret name. - * @param vaultSecretUri Vault Secret URI which contains the path of a secret. - * Not null. - * @param builder Builder to configure with parsed parameters. Not null. - */ - private static void parseVaultSecretUri( - String vaultSecretUri, ParameterSetBuilder builder) { - - UrlBuilder urlBuilder = UrlBuilder.parse(vaultSecretUri); - - String vaultUrl = "https://" + urlBuilder.getHost(); - builder.add("value", KeyVaultSecretFactory.VAULT_URL, vaultUrl); - - String path = urlBuilder.getPath(); - - if (!path.contains("/secrets")) - throw new IllegalArgumentException("The Vault Secret URI should " + - "contain \"/secrets\" following by the name of the Secret: " + - vaultSecretUri); - - String secretName = path.replace("/secrets", ""); - builder.add("value", KeyVaultSecretFactory.SECRET_NAME, secretName); - } } diff --git a/ojdbc-provider-azure/src/main/java/oracle/jdbc/provider/azure/configuration/AzureVaultURLParser.java b/ojdbc-provider-azure/src/main/java/oracle/jdbc/provider/azure/configuration/AzureVaultURLParser.java new file mode 100644 index 00000000..90ba3065 --- /dev/null +++ b/ojdbc-provider-azure/src/main/java/oracle/jdbc/provider/azure/configuration/AzureVaultURLParser.java @@ -0,0 +1,93 @@ +/* + ** Copyright (c) 2023 Oracle and/or its affiliates. + ** + ** The Universal Permissive License (UPL), Version 1.0 + ** + ** Subject to the condition set forth below, permission is hereby granted to any + ** person obtaining a copy of this software, associated documentation and/or data + ** (collectively the "Software"), free of charge and under any and all copyright + ** rights in the Software, and any and all patent rights owned or freely + ** licensable by each licensor hereunder covering either (i) the unmodified + ** Software as contributed to or provided by such licensor, or (ii) the Larger + ** Works (as defined below), to deal in both + ** + ** (a) the Software, and + ** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + ** one is included with the Software (each a "Larger Work" to which the Software + ** is contributed by such licensors), + ** + ** without restriction, including without limitation the rights to copy, create + ** derivative works of, display, perform, and distribute the Software and make, + ** use, sell, offer for sale, import, export, have made, and have sold the + ** Software and the Larger Work(s), and to sublicense the foregoing rights on + ** either these or other terms. + ** + ** This license is subject to the following condition: + ** The above copyright notice and either this complete permission notice or at + ** a minimum a reference to the UPL must be included in all copies or + ** substantial portions of the Software. + ** + ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + ** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + ** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + ** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + ** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + ** SOFTWARE. + */ + +package oracle.jdbc.provider.azure.configuration; + +import com.azure.core.util.UrlBuilder; +import oracle.jdbc.provider.azure.keyvault.KeyVaultSecretFactory; +import oracle.jdbc.provider.parameter.ParameterSetBuilder; +import oracle.jdbc.provider.parameter.ParameterSetParser; + +/** + * A URL parser used by {@link AzureVaultSecretProvider} and {@link AzureVaultJsonProvider}. + */ +public class AzureVaultURLParser { + /** + * Parser that recognizes the "value" field and parses it as a Key Vault + * secret URL. + * {@link AzureConfigurationParameters#configureBuilder(ParameterSetParser.Builder)} + * configures the parser to recognize fields of the nested JSON object named + * "authentication". + */ + public static final ParameterSetParser PARAMETER_SET_PARSER = + AzureConfigurationParameters.configureBuilder(ParameterSetParser.builder() + .addParameter("value", AzureVaultURLParser::parseVaultSecretUri)) + .build(); + + /** + * Parses the "value" field of a JSON object as a vault URI with the path + * of a named secret. An example URI is: + *
+   * https://mykeyvaultpfs.vault.azure.net/secrets/mySecret2
+   * 
+ * This parser configures the given {@code builder} with two distinct + * parameters accepted by {@link KeyVaultSecretFactory}: One parameter for the + * vault URL, and another for the secret name. + * @param vaultSecretUri Vault Secret URI which contains the path of a secret. + * Not null. + * @param builder Builder to configure with parsed parameters. Not null. + */ + private static void parseVaultSecretUri( + String vaultSecretUri, ParameterSetBuilder builder) { + + UrlBuilder urlBuilder = UrlBuilder.parse(vaultSecretUri); + + String vaultUrl = "https://" + urlBuilder.getHost(); + builder.add("value", KeyVaultSecretFactory.VAULT_URL, vaultUrl); + + String path = urlBuilder.getPath(); + + if (!path.contains("/secrets")) + throw new IllegalArgumentException("The Vault Secret URI should " + + "contain \"/secrets\" following by the name of the Secret: " + + vaultSecretUri); + + String secretName = path.replace("/secrets", ""); + builder.add("value", KeyVaultSecretFactory.SECRET_NAME, secretName); + } +} diff --git a/ojdbc-provider-azure/src/main/resources/META-INF/services/oracle.jdbc.spi.OracleConfigurationProvider b/ojdbc-provider-azure/src/main/resources/META-INF/services/oracle.jdbc.spi.OracleConfigurationProvider index c6793a90..1b6e328d 100644 --- a/ojdbc-provider-azure/src/main/resources/META-INF/services/oracle.jdbc.spi.OracleConfigurationProvider +++ b/ojdbc-provider-azure/src/main/resources/META-INF/services/oracle.jdbc.spi.OracleConfigurationProvider @@ -1 +1,2 @@ oracle.jdbc.provider.azure.configuration.AzureAppConfigurationProvider +oracle.jdbc.provider.azure.configuration.AzureVaultJsonProvider diff --git a/ojdbc-provider-samples/src/main/java/oracle/jdbc/provider/azure/configuration/SimpleAzureVaultJsonExample.java b/ojdbc-provider-samples/src/main/java/oracle/jdbc/provider/azure/configuration/SimpleAzureVaultJsonExample.java new file mode 100644 index 00000000..8bad9207 --- /dev/null +++ b/ojdbc-provider-samples/src/main/java/oracle/jdbc/provider/azure/configuration/SimpleAzureVaultJsonExample.java @@ -0,0 +1,101 @@ +/* + ** Copyright (c) 2023 Oracle and/or its affiliates. + ** + ** The Universal Permissive License (UPL), Version 1.0 + ** + ** Subject to the condition set forth below, permission is hereby granted to any + ** person obtaining a copy of this software, associated documentation and/or data + ** (collectively the "Software"), free of charge and under any and all copyright + ** rights in the Software, and any and all patent rights owned or freely + ** licensable by each licensor hereunder covering either (i) the unmodified + ** Software as contributed to or provided by such licensor, or (ii) the Larger + ** Works (as defined below), to deal in both + ** + ** (a) the Software, and + ** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + ** one is included with the Software (each a "Larger Work" to which the Software + ** is contributed by such licensors), + ** + ** without restriction, including without limitation the rights to copy, create + ** derivative works of, display, perform, and distribute the Software and make, + ** use, sell, offer for sale, import, export, have made, and have sold the + ** Software and the Larger Work(s), and to sublicense the foregoing rights on + ** either these or other terms. + ** + ** This license is subject to the following condition: + ** The above copyright notice and either this complete permission notice or at + ** a minimum a reference to the UPL must be included in all copies or + ** substantial portions of the Software. + ** + ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + ** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + ** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + ** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + ** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + ** SOFTWARE. + */ +package oracle.jdbc.provider.azure.configuration; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +import oracle.jdbc.datasource.impl.OracleDataSource; + + +/** + *

+ * A standalone example that configures Oracle JDBC to be provided with the + * connection properties retrieved from Azure Vault Secret. + * For the default authentication, the following environment variables must be + * set: + *

+ * + *

The Oracle DataSource uses a new prefix + * jdbc:oracle:thin:@config-vaultazure: + * to be able to identify that the configuration parameters should be loaded + * using Azure Vault Secret. Users only need to indicate the Vault Secret's + * secret identifier, with the + * following syntax: + *

+ *
+ * jdbc:oracle:thin:@config-vaultazure:{secret-identifier}
+ * 
+ */ + +public class SimpleAzureVaultJsonExample { + private static String url; + + /** + * @param args the command line arguments + * @throws SQLException if an error occurs during the database calls + */ + public static void main(String[] args) throws SQLException { + // Sample default URL if non present + if (args.length == 0) { + url = "jdbc:oracle:thin:@config-vaultazure:https://{your-vault-name}.vault.azure.net/secrets/{secret-name}"; + } else { + url = args[0]; + } + + // No changes required, configuration provider is loaded at runtime + OracleDataSource ds = new OracleDataSource(); + ds.setURL(url); + + // Standard JDBC code + try (Connection cn = ds.getConnection()) { + Statement st = cn.createStatement(); + ResultSet rs = st.executeQuery("SELECT 'Hello, db' FROM sys.dual"); + if (rs.next()) + System.out.println(rs.getString(1)); + } + } +}