Skip to content

Commit a2946e7

Browse files
Add GCP Secret Manager Connection String Provider for Oracle JDBC (#119)
* Add GCP Secret Manager Connection String Provider * Add configuration properties files * Refactor Common Utilities and Parameters * fix keyVaultConnectionStringProvider Tests in Azure && refactor TCPS and SEPS wallets providers * update Readme.md file * Add example for Connection String Provider in GCP * Rename 'tns-alias' parameter to 'tnsAlias' for consistency
1 parent aa76cc1 commit a2946e7

File tree

14 files changed

+527
-21
lines changed

14 files changed

+527
-21
lines changed

ojdbc-provider-azure/src/test/java/oracle/jdbc/provider/azure/resource/KeyVaultConnectionStringProviderTest.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,8 @@ public void testGetParameters() {
9494
.filter(parameter -> "tns-alias".equals(parameter.name()))
9595
.findFirst()
9696
.orElseThrow(AssertionError::new);
97-
assertFalse(aliasParameter.isSensitive());
98-
assertFalse(aliasParameter.isRequired());
97+
assertTrue(aliasParameter.isSensitive());
98+
assertTrue(aliasParameter.isRequired());
9999
assertNull(aliasParameter.defaultValue());
100100
}
101101

ojdbc-provider-common/src/main/java/oracle/jdbc/provider/util/CommonParameters.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,9 @@ private CommonParameters() {}
146146
* <p><b>Note:</b> The alias should exactly match one of the aliases defined
147147
* in your tnsnames.ora file.</p>
148148
*/
149-
public static final Parameter<String> TNS_ALIAS = Parameter.create();
149+
public static final Parameter<String> TNS_ALIAS = Parameter.create(
150+
REQUIRED, SENSITIVE
151+
);
150152

151153

152154
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
** Copyright (c) 2024 Oracle and/or its affiliates.
3+
**
4+
** The Universal Permissive License (UPL), Version 1.0
5+
**
6+
** Subject to the condition set forth below, permission is hereby granted to any
7+
** person obtaining a copy of this software, associated documentation and/or data
8+
** (collectively the "Software"), free of charge and under any and all copyright
9+
** rights in the Software, and any and all patent rights owned or freely
10+
** licensable by each licensor hereunder covering either (i) the unmodified
11+
** Software as contributed to or provided by such licensor, or (ii) the Larger
12+
** Works (as defined below), to deal in both
13+
**
14+
** (a) the Software, and
15+
** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
16+
** one is included with the Software (each a "Larger Work" to which the Software
17+
** is contributed by such licensors),
18+
**
19+
** without restriction, including without limitation the rights to copy, create
20+
** derivative works of, display, perform, and distribute the Software and make,
21+
** use, sell, offer for sale, import, export, have made, and have sold the
22+
** Software and the Larger Work(s), and to sublicense the foregoing rights on
23+
** either these or other terms.
24+
**
25+
** This license is subject to the following condition:
26+
** The above copyright notice and either this complete permission notice or at
27+
** a minimum a reference to the UPL must be included in all copies or
28+
** substantial portions of the Software.
29+
**
30+
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
31+
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
32+
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
33+
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
34+
** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
35+
** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
36+
** SOFTWARE.
37+
*/
38+
39+
package oracle.jdbc.provider.util;
40+
41+
import java.util.Base64;
42+
43+
/**
44+
* Utility class for common file handling operations.
45+
*/
46+
public final class FileUtils {
47+
48+
/**
49+
* Checks if the given byte array is Base64-encoded.
50+
*/
51+
public static boolean isBase64Encoded(byte[] secretBytes) {
52+
try {
53+
Base64.getDecoder().decode(secretBytes);
54+
return true;
55+
} catch (IllegalArgumentException e) {
56+
return false;
57+
}
58+
}
59+
}

ojdbc-provider-common/src/main/java/oracle/jdbc/provider/util/WalletUtils.java

-14
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@
55
import oracle.security.pki.OracleWallet;
66

77
import java.io.IOException;
8-
import java.util.Arrays;
9-
import java.util.Base64;
108
import java.util.Enumeration;
119

1210
/**
@@ -250,18 +248,6 @@ public static Credentials getCredentials(
250248
}
251249
}
252250

253-
/**
254-
* Checks if the given byte array is Base64-encoded.
255-
*/
256-
public static boolean isBase64Encoded(byte[] secretBytes) {
257-
try {
258-
Base64.getDecoder().decode(secretBytes);
259-
return true;
260-
} catch (IllegalArgumentException e) {
261-
return false;
262-
}
263-
}
264-
265251
/**
266252
* A username and password.
267253
*/

ojdbc-provider-gcp/README.md

+40
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ Provider</a></dt>
2727
<dd>Provides TCPS/TLS wallet for secure connections to an Autonomous Database from the Secret Manager service</dd>
2828
<dt><a href="#secret-manager-seps-wallet-provider">Secret Manager SEPS Wallet Provider</a></dt>
2929
<dd>Provides SEPS (Secure External Password Store) wallets for secure username and password retrieval from the Secret Manager service</dd>
30+
<dt><a href="#secret-manager-connection-string-provider">Secret Manager Connection String Provider</a></dt>
31+
<dd>Provides connection strings for secure database connectivity based on aliases, retrieved from the `tnsnames.ora`
32+
file stored in GCP Secret Manager.</dd>
3033
</dl>
3134

3235
Visit any of the links above to find information and usage examples for a
@@ -329,3 +332,40 @@ An example of a [connection properties file](https://docs.oracle.com/en/database
329332

330333
This provider supports wallets stored in GCP Secret Manager as both base64-encoded strings and imported files. It automatically detects the storage format and processes the wallet accordingly, ensuring flexibility in managing your SEPS credentials.
331334

335+
## Secret Manager Connection String Provider
336+
337+
The Connection String Provider provides Oracle JDBC with a connection string managed by the GCP Secret Manager service.
338+
This is a Resource Provider identified by the name `ojdbc-provider-gcp-secretmanager-tnsnames`.
339+
340+
This provider retrieves a `tnsnames.ora` file stored in GCP Secret Manager, allowing selection of connection strings
341+
based on specified aliases. The `tnsnames.ora` file can be stored as a base64-encoded string or as a raw file,
342+
and the provider automatically detects the format.
343+
344+
This enables flexible configuration for secure database connections using the alias names defined in your `tnsnames.ora` file.
345+
346+
<table>
347+
<thead>
348+
<tr>
349+
<th>Parameter Name</th>
350+
<th>Description</th>
351+
<th>Accepted Values</th>
352+
<th>Default Value</th>
353+
</tr>
354+
</thead>
355+
<tbody>
356+
<tr>
357+
<td><code>secretVersionName</code></td>
358+
<td>The version name of the secret in GCP Secret Manager that contains the <code>tnsnames.ora</code> file.</td>
359+
<td>The <a href="https://cloud.google.com/secret-manager/docs/creating-and-accessing-secrets">GCP Secret Manager Secret Version</a>, typically in the form:<br><pre>projects/{project-id}/secrets/{secret-id}/versions/{version-id}</pre></td>
360+
<td><i>No default value. A value must be configured for this parameter.</i></td>
361+
</tr>
362+
<tr>
363+
<td><code>tnsAlias</code></td>
364+
<td>Specifies the alias to retrieve the appropriate connection string from the <code>tnsnames.ora</code> file.</td>
365+
<td>Any valid alias present in your <code>tnsnames.ora</code> file.</td>
366+
<td><i>No default value. A value must be configured for this parameter.</i></td>
367+
</tr>
368+
</tbody>
369+
</table>
370+
371+
An example of a [connection properties file](https://docs.oracle.com/en/database/oracle/oracle-database/23/jajdb/oracle/jdbc/OracleConnection.html#CONNECTION_PROPERTY_CONFIG_FILE) that configures this provider can be found in [example-vault.properties](example-vault.properties).

ojdbc-provider-gcp/example-test.properties

+6
Original file line numberDiff line numberDiff line change
@@ -125,3 +125,9 @@ GCP_SSO_SEPS_SECRET_VERSION_NAME=projects/project-id/secrets/sso-seps-secret/ver
125125

126126
# Corrupted SEPS Wallet Secret stored in GCP Secret Manager for negative test cases
127127
GCP_CORRUPTED_SEPS_WALLET_SECRET_VERSION_NAME=projects/project-id/secrets/corrupted-seps-secret/versions/1
128+
129+
# Secret version name for tnsnames.ora file in GCP Secret Manager
130+
GCP_SECRET_MANAGER_TNS_NAMES_SECRET_VERSION=projects/project-id/secrets/tnsnames-ora-secret/versions/1
131+
132+
# Alias to retrieve specific connection string from tnsnames.ora in GCP Secret Manager
133+
GCP_SECRET_MANAGER_TNS_ALIAS_SECRET_NAME=your_tns_alias

ojdbc-provider-gcp/example-vault.properties

+9
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,12 @@ oracle.jdbc.provider.username.secretVersionName=${USERNAME_SECRET_VERSION_NAME}
5454
# property named "PASSWORD_SECRET_VERSION_NAME":
5555
oracle.jdbc.provider.password=ojdbc-provider-gcp-secretmanager-password
5656
oracle.jdbc.provider.password.secretVersionName=${PASSWORD_SECRET_VERSION_NAME}
57+
58+
# Configures the GCP Secret Manager Connection String Provider. The secret version name and
59+
# tns alias are configured as environment variables or JVM system properties
60+
# named "GCP_SECRET_VERSION_NAME" and "TNS_ALIAS".
61+
oracle.jdbc.provider.connectionString=ojdbc-provider-gcp-secretmanager-tnsnames
62+
oracle.jdbc.provider.connectionString.secretVersionName=${GCP_SECRET_VERSION_NAME}
63+
oracle.jdbc.provider.connectionString.tnsAlias=${TNS_ALIAS}
64+
65+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
/*
2+
** Copyright (c) 2024 Oracle and/or its affiliates.
3+
**
4+
** The Universal Permissive License (UPL), Version 1.0
5+
**
6+
** Subject to the condition set forth below, permission is hereby granted to any
7+
** person obtaining a copy of this software, associated documentation and/or data
8+
** (collectively the "Software"), free of charge and under any and all copyright
9+
** rights in the Software, and any and all patent rights owned or freely
10+
** licensable by each licensor hereunder covering either (i) the unmodified
11+
** Software as contributed to or provided by such licensor, or (ii) the Larger
12+
** Works (as defined below), to deal in both
13+
**
14+
** (a) the Software, and
15+
** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
16+
** one is included with the Software (each a "Larger Work" to which the Software
17+
** is contributed by such licensors),
18+
**
19+
** without restriction, including without limitation the rights to copy, create
20+
** derivative works of, display, perform, and distribute the Software and make,
21+
** use, sell, offer for sale, import, export, have made, and have sold the
22+
** Software and the Larger Work(s), and to sublicense the foregoing rights on
23+
** either these or other terms.
24+
**
25+
** This license is subject to the following condition:
26+
** The above copyright notice and either this complete permission notice or at
27+
** a minimum a reference to the UPL must be included in all copies or
28+
** substantial portions of the Software.
29+
**
30+
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
31+
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
32+
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
33+
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
34+
** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
35+
** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
36+
** SOFTWARE.
37+
*/
38+
39+
package oracle.jdbc.provider.gcp.resource;
40+
41+
import oracle.jdbc.provider.resource.ResourceParameter;
42+
import oracle.jdbc.provider.util.FileUtils;
43+
import oracle.jdbc.provider.util.TNSNames;
44+
import oracle.jdbc.spi.ConnectionStringProvider;
45+
46+
import java.io.ByteArrayInputStream;
47+
import java.io.IOException;
48+
import java.io.InputStream;
49+
import java.util.Base64;
50+
import java.util.Map;
51+
52+
import static oracle.jdbc.provider.util.CommonParameters.TNS_ALIAS;
53+
54+
55+
/**
56+
* <p>
57+
* A provider for securely retrieving the connection string from a tnsnames.ora
58+
* file stored in GCP Secret Manager for use with an Oracle Autonomous Database.
59+
* The tnsnames.ora file can be stored either as a base64-encoded secret or in
60+
* raw binary format in GCP Secret Manager. This provider automatically
61+
* detects and decodes base64-encoded content as needed before parsing the
62+
* file to retrieve connection strings based on specified aliases.
63+
* </p>
64+
* <p>
65+
* This class implements the {@link ConnectionStringProvider} SPI defined by
66+
* Oracle JDBC. It is designed to be instantiated via {@link java.util.ServiceLoader}.
67+
* </p>
68+
*/
69+
public class GcpSecretManagerConnectionStringProvider
70+
extends GcpSecretManagerProvider
71+
implements ConnectionStringProvider {
72+
73+
private static final ResourceParameter[] TNS_NAMES_PARAMETERS = {
74+
new ResourceParameter("tnsAlias", TNS_ALIAS)
75+
};
76+
77+
/**
78+
* A public no-arg constructor used by {@link java.util.ServiceLoader} to
79+
* construct an instance of this provider.
80+
*/
81+
public GcpSecretManagerConnectionStringProvider() {
82+
super("secretmanager-tnsnames", TNS_NAMES_PARAMETERS);
83+
}
84+
85+
/**
86+
* Retrieves a database connection string from the tnsnames.ora file stored
87+
* in GCP Secret Manager.
88+
* <p>
89+
* This method accesses the file in GCP Secret Manager, detects if the content
90+
* is base64-encoded, and decodes it if necessary. The tnsnames.ora content is
91+
* then parsed to locate the connection string associated with the specified
92+
* alias.
93+
* </p>
94+
*
95+
* @param parameterValues The parameters required to access the tnsnames.ora
96+
* file in GCP Secret Manager, including the secret name and the tns-alias.
97+
* @return The connection string associated with the specified alias
98+
* in the tnsnames.ora file.
99+
* @throws IllegalStateException If there is an error reading the tnsnames.ora
100+
* file or accessing the GCP Secret Manager.
101+
* @throws IllegalArgumentException If the specified alias is invalid or
102+
* does not exist in the tnsnames.ora file.
103+
*/
104+
@Override
105+
public String getConnectionString(Map<Parameter, CharSequence> parameterValues) {
106+
107+
byte[] fileBytes = getSecret(parameterValues).toByteArray();
108+
109+
if (FileUtils.isBase64Encoded(fileBytes)) {
110+
fileBytes = Base64.getDecoder().decode(fileBytes);
111+
}
112+
113+
TNSNames tnsNames;
114+
try (InputStream inputStream = new ByteArrayInputStream(fileBytes)) {
115+
tnsNames = TNSNames.read(inputStream);
116+
} catch (IOException e) {
117+
throw new IllegalStateException("Failed to read tnsnames.ora content", e);
118+
}
119+
120+
String alias;
121+
try {
122+
alias = parseParameterValues(parameterValues).getRequired(TNS_ALIAS);
123+
} catch (IllegalStateException e) {
124+
throw new IllegalArgumentException(
125+
"Required parameter 'tnsAlias' is missing", e
126+
);
127+
}
128+
129+
String connectionString = tnsNames.getConnectionStringByAlias(alias);
130+
if (connectionString == null) {
131+
throw new IllegalArgumentException(
132+
"Alias specified does not exist in tnsnames.ora: " + alias
133+
);
134+
}
135+
return connectionString;
136+
}
137+
}

ojdbc-provider-gcp/src/main/java/oracle/jdbc/provider/gcp/resource/GcpSecretManagerSEPSProvider.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040

4141
import oracle.jdbc.provider.parameter.ParameterSet;
4242
import oracle.jdbc.provider.resource.ResourceParameter;
43+
import oracle.jdbc.provider.util.FileUtils;
4344
import oracle.jdbc.provider.util.WalletUtils;
4445
import oracle.jdbc.spi.PasswordProvider;
4546
import oracle.jdbc.spi.UsernameProvider;
@@ -114,7 +115,7 @@ private WalletUtils.Credentials getWalletCredentials(
114115

115116
byte[] walletBytes = getSecret(parameterValues).toByteArray();
116117

117-
if (WalletUtils.isBase64Encoded(walletBytes)) {
118+
if (FileUtils.isBase64Encoded(walletBytes)) {
118119
walletBytes = Base64.getDecoder().decode(walletBytes);
119120
}
120121

ojdbc-provider-gcp/src/main/java/oracle/jdbc/provider/gcp/resource/GcpSecretManagerTCPSProvider.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@
4040

4141
import oracle.jdbc.provider.parameter.ParameterSet;
4242
import oracle.jdbc.provider.resource.ResourceParameter;
43+
import oracle.jdbc.provider.util.FileUtils;
4344
import oracle.jdbc.provider.util.TlsUtils;
44-
import oracle.jdbc.provider.util.WalletUtils;
4545
import oracle.jdbc.spi.TlsConfigurationProvider;
4646

4747
import javax.net.ssl.SSLContext;
@@ -113,7 +113,7 @@ public SSLContext getSSLContext(Map<Parameter, CharSequence> parameterValues) {
113113

114114
byte[] fileBytes = getSecret(parameterValues).toByteArray();
115115

116-
if (WalletUtils.isBase64Encoded(fileBytes)) {
116+
if (FileUtils.isBase64Encoded(fileBytes)) {
117117
fileBytes = Base64.getDecoder().decode(fileBytes);
118118
}
119119

Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
oracle.jdbc.provider.gcp.resource.GcpSecretManagerConnectionStringProvider

ojdbc-provider-gcp/src/test/java/oracle/provider/gcp/GcpTestProperty.java

+7-1
Original file line numberDiff line numberDiff line change
@@ -73,5 +73,11 @@ public enum GcpTestProperty {
7373

7474
GCP_SSO_SEPS_SECRET_VERSION_NAME,
7575

76-
GCP_CORRUPTED_SEPS_WALLET_SECRET_VERSION_NAME;
76+
GCP_CORRUPTED_SEPS_WALLET_SECRET_VERSION_NAME,
77+
78+
GCP_SECRET_MANAGER_TNS_NAMES_SECRET_VERSION,
79+
80+
GCP_SECRET_MANAGER_TNS_ALIAS_SECRET_NAME,
81+
82+
GCP_NON_BASE64_TNS_NAMES_SECRET_VERSION;
7783
}

0 commit comments

Comments
 (0)