Skip to content

Commit

Permalink
add Azure Vault Provider
Browse files Browse the repository at this point in the history
  • Loading branch information
norah-li committed Jan 27, 2024
1 parent a626b7a commit 7b1e8bd
Show file tree
Hide file tree
Showing 5 changed files with 298 additions and 44 deletions.
Original file line number Diff line number Diff line change
@@ -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}
* <p>
* Returns the JSON payload stored in Azure Vault Secret.
* </p><p>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.
* </p>
* @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<String, String> 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";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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}
* <p>
Expand Down Expand Up @@ -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:
* <pre>
* https://mykeyvaultpfs.vault.azure.net/secrets/mySecret2
* </pre>
* 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);
}
}
Original file line number Diff line number Diff line change
@@ -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:
* <pre>
* https://mykeyvaultpfs.vault.azure.net/secrets/mySecret2
* </pre>
* 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);
}
}
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
oracle.jdbc.provider.azure.configuration.AzureAppConfigurationProvider
oracle.jdbc.provider.azure.configuration.AzureVaultJsonProvider
Original file line number Diff line number Diff line change
@@ -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;


/**
* <p>
* 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:
* </p>
* <ul>
* <li>AZURE_TENANT_ID The Azure Active Directory tenant(directory) ID.</li>
* <li>AZURE_CLIENT_ID The client(application) ID of an App Registration in the
* tenant.</li>
* <li>AZURE_CLIENT_SECRET A client secret that was generated for the App
* Registration.</li>
* </ul>
* <p>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:
* </p>
* <pre>
* jdbc:oracle:thin:@config-vaultazure:{secret-identifier}
* </pre>
*/

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

0 comments on commit 7b1e8bd

Please sign in to comment.