Skip to content

Commit d088b34

Browse files
Dedicated Vault centralized config
1 parent 190a9c0 commit d088b34

12 files changed

+569
-0
lines changed

ojdbc-provider-hashicorp/pom.xml

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
<modelVersion>4.0.0</modelVersion>
5+
6+
<name>Oracle JDBC Hashicorp Providers</name>
7+
8+
<artifactId>ojdbc-provider-hashicorp</artifactId>
9+
<packaging>jar</packaging>
10+
11+
<parent>
12+
<groupId>com.oracle.database.jdbc</groupId>
13+
<artifactId>ojdbc-extensions</artifactId>
14+
<version>${extensions-version}</version>
15+
</parent>
16+
17+
<dependencies>
18+
<dependency>
19+
<groupId>com.oracle.database.jdbc</groupId>
20+
<artifactId>ojdbc-provider-common</artifactId>
21+
</dependency>
22+
23+
<dependency>
24+
<groupId>com.oracle.database.jdbc</groupId>
25+
<artifactId>ojdbc-provider-common</artifactId>
26+
<classifier>tests</classifier>
27+
<type>test-jar</type>
28+
</dependency>
29+
<dependency>
30+
<groupId>org.junit.jupiter</groupId>
31+
<artifactId>junit-jupiter-api</artifactId>
32+
</dependency>
33+
<dependency>
34+
<groupId>org.junit.jupiter</groupId>
35+
<artifactId>junit-jupiter-engine</artifactId>
36+
</dependency>
37+
</dependencies>
38+
39+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package oracle.jdbc.provider.hashicorp;
2+
3+
import oracle.jdbc.provider.factory.Resource;
4+
import oracle.jdbc.provider.factory.ResourceFactory;
5+
import oracle.jdbc.provider.hashicorp.authentication.HashiCredentials;
6+
import oracle.jdbc.provider.hashicorp.authentication.HashicorpCredentialsFactory;
7+
import oracle.jdbc.provider.parameter.ParameterSet;
8+
9+
/**
10+
* Common super class for ResourceFactory implementations that request
11+
* a resource from Vault using HashiCredentials (Vault token).
12+
*/
13+
public abstract class HashiVaultResourceFactory<T> implements ResourceFactory<T> {
14+
15+
@Override
16+
public final Resource<T> request(ParameterSet parameterSet) {
17+
// Retrieve the Vault credentials (token) from the credentials factory
18+
HashiCredentials credentials = HashicorpCredentialsFactory.getInstance()
19+
.request(parameterSet)
20+
.getContent();
21+
22+
try {
23+
return request(credentials, parameterSet);
24+
} catch (Exception e) {
25+
throw new IllegalStateException(
26+
"Request failed with parameters: " + parameterSet, e);
27+
}
28+
}
29+
30+
/**
31+
* Subclasses implement to request the resource from Vault using
32+
* the given credentials and parameters.
33+
*/
34+
public abstract Resource<T> request(
35+
HashiCredentials credentials, ParameterSet parameterSet);
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package oracle.jdbc.provider.hashicorp.authentication;
2+
3+
/**
4+
* Simple credentials object for HashiCorp Vault that holds a token.
5+
*/
6+
public final class HashiCredentials {
7+
private final String vaultToken;
8+
9+
public HashiCredentials(String vaultToken) {
10+
this.vaultToken = vaultToken;
11+
}
12+
13+
public String getVaultToken() {
14+
return vaultToken;
15+
}
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package oracle.jdbc.provider.hashicorp.authentication;
2+
3+
/**
4+
* A method of authentication using HashiCorp Vault.
5+
*/
6+
public enum HashicorpAuthenticationMethod {
7+
/**
8+
* Authentication using a Vault token (e.g., read from environment variable).
9+
*/
10+
TOKEN
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package oracle.jdbc.provider.hashicorp.authentication;
2+
3+
import oracle.jdbc.provider.factory.Resource;
4+
import oracle.jdbc.provider.factory.ResourceFactory;
5+
import oracle.jdbc.provider.hashicorp.secrets.HashiVaultSecretsManagerFactory;
6+
import oracle.jdbc.provider.parameter.Parameter;
7+
import oracle.jdbc.provider.parameter.ParameterSet;
8+
9+
import static oracle.jdbc.provider.parameter.Parameter.CommonAttribute.REQUIRED;
10+
11+
/**
12+
* A factory for creating {@link HashiCredentials} objects for Vault.
13+
*/
14+
public final class HashicorpCredentialsFactory implements ResourceFactory<HashiCredentials> {
15+
16+
// Example parameter referencing the authentication method
17+
public static final Parameter<HashicorpAuthenticationMethod> AUTHENTICATION_METHOD =
18+
Parameter.create(REQUIRED);
19+
20+
private static final HashicorpCredentialsFactory INSTANCE =
21+
new HashicorpCredentialsFactory();
22+
23+
private HashicorpCredentialsFactory() { }
24+
25+
public static HashicorpCredentialsFactory getInstance() {
26+
return INSTANCE;
27+
}
28+
29+
@Override
30+
public Resource<HashiCredentials> request(ParameterSet parameterSet) {
31+
HashiCredentials credentials = getCredential(parameterSet);
32+
return Resource.createPermanentResource(credentials, true);
33+
}
34+
35+
private static HashiCredentials getCredential(ParameterSet parameterSet) {
36+
System.out.println("parameterSet = " + parameterSet);
37+
// Check which authentication method is requested
38+
HashicorpAuthenticationMethod method =
39+
parameterSet.getRequired(AUTHENTICATION_METHOD);
40+
41+
switch (method) {
42+
case TOKEN:
43+
return tokenCredentials(parameterSet);
44+
default:
45+
throw new IllegalArgumentException(
46+
"Unrecognized authentication method: " + method);
47+
}
48+
}
49+
50+
/**
51+
* Example: read Vault token from an environment variable "VAULT_TOKEN".
52+
*/
53+
private static HashiCredentials tokenCredentials(ParameterSet parameterSet) {
54+
// (1) Try parameter
55+
String paramToken = parameterSet.getOptional(HashiVaultSecretsManagerFactory.VAULT_TOKEN);
56+
if (paramToken != null && !paramToken.isEmpty()) {
57+
return new HashiCredentials(paramToken);
58+
}
59+
60+
// (2) Fallback to system property or env
61+
String vaultToken = System.getProperty("VAULT_TOKEN", System.getenv("VAULT_TOKEN"));
62+
if (vaultToken == null || vaultToken.isEmpty()) {
63+
throw new IllegalStateException("Vault token not provided in tokenCredentials()");
64+
}
65+
66+
return new HashiCredentials(vaultToken);
67+
}
68+
69+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package oracle.jdbc.provider.hashicorp.configuration;
2+
3+
import oracle.jdbc.provider.configuration.JsonSecretUtil;
4+
import oracle.jdbc.provider.hashicorp.secrets.HashiVaultSecretsManagerFactory;
5+
import oracle.jdbc.provider.parameter.ParameterSet;
6+
import oracle.jdbc.spi.OracleConfigurationJsonSecretProvider;
7+
import oracle.sql.json.OracleJsonObject;
8+
9+
import java.io.ByteArrayInputStream;
10+
import java.nio.charset.StandardCharsets;
11+
import java.util.Base64;
12+
13+
import static oracle.jdbc.provider.hashicorp.configuration.HashiVaultSecretsManagerConfigurationProvider.PARAMETER_SET_PARSER;
14+
import static oracle.jdbc.provider.hashicorp.secrets.HashiVaultSecretsManagerFactory.FIELD_NAME;
15+
16+
/**
17+
* Mirrors the AWS pattern for retrieving a single secret
18+
* field from HashiCorp Vault, base64-encoding it.
19+
*
20+
* Example JSON input might look like:
21+
* {
22+
* "password": {
23+
* "type": "hashicorpvault",
24+
* "value": "/v1/secret/data/test-config2"
25+
* }
26+
* }
27+
*
28+
* The provider will retrieve the secret from Vault, then
29+
* base64-encode it and return as a char[].
30+
*/
31+
public class HashiJsonVaultProvider implements OracleConfigurationJsonSecretProvider {
32+
33+
@Override
34+
public char[] getSecret(OracleJsonObject jsonObject) {
35+
// 1) Convert the JSON object to named key-value pairs
36+
ParameterSet parameterSet =
37+
PARAMETER_SET_PARSER.parseNamedValues(
38+
JsonSecretUtil.toNamedValues(jsonObject)
39+
);
40+
41+
// 2) Call the Vault factory to fetch the raw secret string
42+
String secretString = HashiVaultSecretsManagerFactory
43+
.getInstance()
44+
.request(parameterSet)
45+
.getContent();
46+
47+
ByteArrayInputStream inputStream = new ByteArrayInputStream(secretString.getBytes(StandardCharsets.UTF_8));
48+
49+
50+
// 3) Parse that JSON to find "myPassword"
51+
// Using the Oracle JSON library, for example:
52+
OracleJsonObject secretJsonObj =
53+
new oracle.sql.json.OracleJsonFactory()
54+
.createJsonTextValue(inputStream)
55+
.asJsonObject();
56+
57+
System.out.println(secretJsonObj);
58+
59+
// 4) Retrieve the field we want
60+
//String myPasswordValue = secretJsonObj.getString("myPassword");
61+
String myPasswordValue = parameterSet.getOptional(FIELD_NAME);
62+
System.out.println(myPasswordValue);
63+
64+
// 5) Base64-encode just that field
65+
return Base64.getEncoder()
66+
.encodeToString(myPasswordValue.getBytes())
67+
.toCharArray();
68+
}
69+
70+
@Override
71+
public String getSecretType() {
72+
// Must match the "type" field in your JSON.
73+
// E.g. "hashicorpvault" or "hashicorsecret"—your choice.
74+
return "hashicorpvault";
75+
}
76+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package oracle.jdbc.provider.hashicorp.configuration;
2+
3+
import oracle.jdbc.driver.OracleConfigurationJsonProvider;
4+
import oracle.jdbc.provider.hashicorp.secrets.HashiVaultSecretsManagerFactory;
5+
import oracle.jdbc.provider.parameter.ParameterSet;
6+
import oracle.jdbc.provider.parameter.ParameterSetParser;
7+
8+
import java.io.ByteArrayInputStream;
9+
import java.io.InputStream;
10+
import java.util.HashMap;
11+
import java.util.Map;
12+
13+
/**
14+
* A provider for JSON payload from Vault, analogous to AWS Secrets Manager version.
15+
*/
16+
public class HashiVaultSecretsManagerConfigurationProvider extends OracleConfigurationJsonProvider {
17+
18+
// Reuse a ParameterSetParser approach
19+
// If you need more parameters, add them below.
20+
static final ParameterSetParser PARAMETER_SET_PARSER =
21+
HashicorpConfigurationParameters.configureBuilder(
22+
ParameterSetParser.builder()
23+
.addParameter("value", HashiVaultSecretsManagerFactory.SECRET_PATH)
24+
.addParameter("key_name", HashiVaultSecretsManagerFactory.KEY_NAME)
25+
.addParameter(
26+
"VAULT_ADDR",
27+
HashiVaultSecretsManagerFactory.VAULT_ADDRESS
28+
)
29+
.addParameter(
30+
"VAULT_TOKEN",
31+
HashiVaultSecretsManagerFactory.VAULT_TOKEN
32+
)
33+
.addParameter("FILED_NAME",
34+
HashiVaultSecretsManagerFactory.FIELD_NAME)
35+
).build();
36+
37+
@Override
38+
public InputStream getJson(String secretPath) {
39+
// 'secretPath' is the location or name of the Vault secret
40+
final String valueFieldName = "value";
41+
42+
// Build a map of user-provided options
43+
Map<String, String> optionsWithSecret = new HashMap<>(options);
44+
optionsWithSecret.put(valueFieldName, secretPath);
45+
46+
// Parse into a ParameterSet
47+
ParameterSet parameters = PARAMETER_SET_PARSER.parseNamedValues(optionsWithSecret);
48+
49+
// Fetch the secret from Vault
50+
String secretString = HashiVaultSecretsManagerFactory
51+
.getInstance()
52+
.request(parameters)
53+
.getContent();
54+
55+
// Return the JSON as an InputStream
56+
return new ByteArrayInputStream(secretString.getBytes());
57+
}
58+
59+
@Override
60+
public String getType() {
61+
// We'll reference this in our JDBC URL, e.g. "jdbc:oracle:thin:@config-hashicorpvault://..."
62+
return "hashicorpvault";
63+
}
64+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package oracle.jdbc.provider.hashicorp.configuration;
2+
3+
import oracle.jdbc.provider.hashicorp.authentication.HashicorpAuthenticationMethod;
4+
import oracle.jdbc.provider.hashicorp.authentication.HashicorpCredentialsFactory;
5+
import oracle.jdbc.provider.hashicorp.secrets.HashiVaultSecretsManagerFactory;
6+
import oracle.jdbc.provider.parameter.ParameterSetParser;
7+
8+
/**
9+
* Defines how we parse common Vault parameters (similar to AWS approach).
10+
*/
11+
public final class HashicorpConfigurationParameters {
12+
13+
private HashicorpConfigurationParameters() {}
14+
15+
public static ParameterSetParser.Builder configureBuilder(ParameterSetParser.Builder builder) {
16+
return builder.addParameter(
17+
// The parameter name is "AUTHENTICATION"
18+
"AUTHENTICATION",
19+
// Tied to HashicorpCredentialsFactory.AUTHENTICATION_METHOD
20+
HashicorpCredentialsFactory.AUTHENTICATION_METHOD,
21+
// Default value if none is specified:
22+
HashicorpAuthenticationMethod.TOKEN,
23+
HashicorpConfigurationParameters::parseAuthentication)
24+
;
25+
}
26+
27+
private static HashicorpAuthenticationMethod parseAuthentication(String value) {
28+
// Map user-provided string to enum
29+
if ("TOKEN".equalsIgnoreCase(value) || "VAULT_TOKEN".equalsIgnoreCase(value)) {
30+
return HashicorpAuthenticationMethod.TOKEN;
31+
}
32+
throw new IllegalArgumentException(
33+
"Unrecognized Hashicorp authentication value: " + value);
34+
}
35+
}

0 commit comments

Comments
 (0)