Skip to content

Commit cd3e1a0

Browse files
OCI Connection String Provider (#124)
* Add VaultConnectionStringProvider for tnsnames.ora support * update properties files * Update Readme file * Add Connection String Provider sample * Update VaultConnectionStringProvider for early parameter validation and clarify required parameters in documentation
1 parent 257111f commit cd3e1a0

File tree

8 files changed

+444
-2
lines changed

8 files changed

+444
-2
lines changed

ojdbc-provider-oci/README.md

+42
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ Provider</a></dt>
3838
<dd>Provides TCPS/TLS wallets for secure connections to an Autonomous Database</dd>
3939
<dt><a href="#seps-wallet-provider">SEPS Wallet Provider</a></dt>
4040
<dd>Provides SEPS (Secure External Password Store) wallets for secure username and password retrieval</dd>
41+
<dt><a href="#vault-connection-string-provider">Vault Connection String Provider</a></dt>
42+
<dd>Provides connection strings for secure database connectivity based on aliases, retrieved from the tnsnames.ora file stored in OCI Vault.</dd>
4143
<dt><a href="#common-parameters-for-resource-providers">Common Parameters for Resource Providers</a></dt>
4244
<dd>Common parameters supported by the resource providers</dd>
4345
</dl>
@@ -512,6 +514,46 @@ Optional parameter to specify the index of the connection string to use when ret
512514

513515
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-wallet.properties](example-vault-wallet.properties).
514516

517+
## Vault Connection String Provider
518+
519+
The OCI Vault Connection String Provider provides Oracle JDBC with a connection string managed by the OCI Vault service.
520+
This is a Resource Provider identified by the name `ojdbc-provider-oci-vault-tnsnames`.
521+
522+
This provider retrieves and decodes a `tnsnames.ora` file stored as a secret in OCI Vault, allowing selection of
523+
connection strings based on specified aliases.
524+
It supports both plain text and Base64-encoded `tnsnames.ora` files.
525+
526+
This enables flexible and secure configuration for database connections using the alias names defined in your `tnsnames.ora` file.
527+
528+
In addition to the set of common parameters, this provider also requires the parameters listed below:
529+
530+
<table>
531+
<thead>
532+
<tr>
533+
<th>Parameter Name</th>
534+
<th>Description</th>
535+
<th>Accepted Values</th>
536+
<th>Default Value</th>
537+
</tr>
538+
</thead>
539+
<tbody>
540+
<tr>
541+
<td><code>ocid</code></td>
542+
<td>The OCID of the OCI Vault secret containing the <code>tnsnames.ora</code> file.</td>
543+
<td>The <a href="https://docs.oracle.com/en-us/iaas/Content/General/Concepts/identifiers.htm">OCID</a> of the secret</td>
544+
<td><i>No default value. A value must be configured for this parameter.</i></td>
545+
</tr>
546+
<tr>
547+
<td><code>tnsAlias</code></td>
548+
<td>Specifies the alias to retrieve the appropriate connection string from the <code>tnsnames.ora</code> file.</td>
549+
<td>Any valid alias present in your <code>tnsnames.ora</code> file.</td>
550+
<td><i>No default value. A value must be configured for this parameter.</i></td>
551+
</tr>
552+
</tbody>
553+
</table>
554+
555+
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).
556+
515557

516558
## Access Token Provider
517559
The Access Token Provider provides Oracle JDBC with an access token that authorizes logins to an Autonomous Database. This is a Resource Provider identified by

ojdbc-provider-oci/example-test.properties

+6
Original file line numberDiff line numberDiff line change
@@ -146,3 +146,9 @@ OCI_SEPS_WALLET_PASSWORD=*****
146146

147147
# Optional index to select specific credentials from SEPS Wallet
148148
OCI_SEPS_CONNECTION_STRING_INDEX=1
149+
150+
# The OCID of a tnsnames.ora file stored in OCI Vault
151+
OCI_TNS_NAMES_OCID=ocid1.vaultsecret.oc1.phx.bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
152+
153+
# The alias in the tnsnames.ora file to use for the connection string
154+
OCI_TNS_NAMES_ALIAS=your_tns_alias

ojdbc-provider-oci/example-vault.properties

+7
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,10 @@ oracle.jdbc.provider.username.ocid=${USERNAME_OCID}
5454
# "PASSWORD_OCID":
5555
oracle.jdbc.provider.password=ojdbc-provider-oci-vault-password
5656
oracle.jdbc.provider.password.ocid=${PASSWORD_OCID}
57+
58+
# Configures the OCI Vault Connection String Provider. The OCID of the tnsnames secret
59+
# and tns alias are configured as environment variables or JVM system
60+
# properties named "TNS_NAMES_OCID" and "TNS_ALIAS".
61+
oracle.jdbc.provider.connectionString=ojdbc-provider-oci-vault-tnsnames
62+
oracle.jdbc.provider.connectionString.ocid=${TNS_NAMES_OCID}
63+
oracle.jdbc.provider.connectionString.tnsAlias=${TNS_ALIAS}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
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.oci.resource;
40+
41+
import oracle.jdbc.provider.resource.ResourceParameter;
42+
import oracle.jdbc.provider.util.TNSNames;
43+
import oracle.jdbc.spi.ConnectionStringProvider;
44+
45+
import java.io.ByteArrayInputStream;
46+
import java.io.IOException;
47+
import java.io.InputStream;
48+
import java.util.Base64;
49+
import java.util.Map;
50+
51+
import static oracle.jdbc.provider.oci.vault.SecretFactory.OCID;
52+
import static oracle.jdbc.provider.util.CommonParameters.TNS_ALIAS;
53+
54+
/**
55+
* <p>
56+
* A provider for securely retrieving the connection string from a tnsnames.ora
57+
* file stored in OCI Vault for use with an Oracle Autonomous Database.
58+
* The tnsnames.ora file can be stored as plain text or base64-encoded content
59+
* in OCI Vault. This class decodes and parses the content to select
60+
* connection strings based on specified aliases.
61+
* </p>
62+
* <p>
63+
* This class implements the {@link ConnectionStringProvider} SPI defined by
64+
* Oracle JDBC.
65+
* It is designed to be instantiated via {@link java.util.ServiceLoader}.
66+
* </p>
67+
*/
68+
public class VaultConnectionStringProvider
69+
extends OciResourceProvider
70+
implements ConnectionStringProvider {
71+
72+
private static final ResourceParameter[] PARAMETERS = {
73+
new ResourceParameter("ocid", OCID),
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 VaultConnectionStringProvider() {
82+
super("vault-tnsnames", PARAMETERS);
83+
}
84+
85+
/**
86+
* Retrieves a database connection string from the tnsnames.ora file stored
87+
* in OCI Vault.
88+
* <p>
89+
* This method accesses the file in OCI Vault, decodes it if it is
90+
* base64-encoded, and parses the `tnsnames.ora` content. The method
91+
* returns the connection string associated with the specified alias from
92+
* the `tnsnames.ora` file.
93+
* The file can either be stored as plain text or as base64-encoded content
94+
* in the vault.
95+
* </p>
96+
*
97+
* @param parameterValues The parameters required to access the `tnsnames.ora`
98+
* file in OCI Vault, including the vault OCID and the tns-alias.
99+
* @return The connection string associated with the specified alias
100+
* in the `tnsnames.ora` file.
101+
* @throws IllegalStateException If there is an error reading the `tnsnames.ora`
102+
* file, decoding its content, or accessing the OCI Vault.
103+
* @throws IllegalArgumentException If the specified alias is invalid or
104+
* does not exist in the `tnsnames.ora` file.
105+
*/
106+
@Override
107+
public String getConnectionString(Map<Parameter, CharSequence> parameterValues) {
108+
109+
String alias;
110+
try {
111+
alias = parseParameterValues(parameterValues).getRequired(TNS_ALIAS);
112+
} catch (IllegalStateException e) {
113+
throw new IllegalArgumentException(
114+
"Required parameter 'tnAlias' is missing", e
115+
);
116+
}
117+
118+
// Retrieve the secret containing tnsnames.ora content from OCI Vault
119+
String secretValue = getVaultSecret(parameterValues)
120+
.getBase64Secret();
121+
122+
byte[] fileBytes = Base64.getDecoder().decode(secretValue);
123+
124+
TNSNames tnsNames;
125+
try (InputStream inputStream = new ByteArrayInputStream(fileBytes)) {
126+
tnsNames = TNSNames.read(inputStream);
127+
} catch (IOException e) {
128+
throw new IllegalStateException("Failed to read tnsnames.ora content", e);
129+
}
130+
131+
String connectionString = tnsNames.getConnectionStringByAlias(alias);
132+
if (connectionString == null) {
133+
throw new IllegalArgumentException(
134+
"Alias specified does not exist in tnsnames.ora: " + alias
135+
);
136+
}
137+
return connectionString;
138+
}
139+
}
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
oracle.jdbc.provider.oci.resource.DatabaseConnectionStringProvider
1+
oracle.jdbc.provider.oci.resource.DatabaseConnectionStringProvider
2+
oracle.jdbc.provider.oci.resource.VaultConnectionStringProvider

ojdbc-provider-oci/src/test/java/oracle/jdbc/provider/oci/OciTestProperty.java

+7-1
Original file line numberDiff line numberDiff line change
@@ -104,5 +104,11 @@ public enum OciTestProperty {
104104

105105
OCI_PASSWORD_PAYLOAD_OCID_MULTIPLE_KEYS,
106106

107-
OCI_PASSWORD_PAYLOAD_OCID_KEY;
107+
OCI_PASSWORD_PAYLOAD_OCID_KEY,
108+
109+
OCI_TNS_NAMES_OCID,
110+
111+
OCI_TNS_NAMES_ALIAS,
112+
113+
OCI_NON_BASE64_TNS_NAMES_OCID;
108114
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
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.oci.resource;
40+
41+
import oracle.jdbc.provider.TestProperties;
42+
import oracle.jdbc.provider.oci.OciTestProperty;
43+
import oracle.jdbc.spi.ConnectionStringProvider;
44+
import oracle.jdbc.spi.OracleResourceProvider.Parameter;
45+
import org.junit.jupiter.api.Test;
46+
47+
import java.util.HashMap;
48+
import java.util.Map;
49+
50+
import static oracle.jdbc.provider.resource.ResourceProviderTestUtil.createParameterValues;
51+
import static oracle.jdbc.provider.resource.ResourceProviderTestUtil.findProvider;
52+
import static org.junit.jupiter.api.Assertions.assertNotNull;
53+
import static org.junit.jupiter.api.Assertions.assertThrows;
54+
55+
/** Verifies {@link DatabaseConnectionStringProvider} */
56+
public class VaultConnectionStringProviderTest {
57+
58+
private static final ConnectionStringProvider PROVIDER =
59+
findProvider(
60+
ConnectionStringProvider.class,
61+
"ojdbc-provider-oci-vault-tnsnames");
62+
63+
@Test
64+
public void testValidAlias() {
65+
Map<String, String> testParameters = new HashMap<>();
66+
testParameters.put("authenticationMethod", "config-file");
67+
testParameters.put("configFile",
68+
TestProperties.getOrAbort(OciTestProperty.OCI_CONFIG_FILE));
69+
testParameters.put("profile",
70+
TestProperties.getOrAbort(OciTestProperty.OCI_CONFIG_PROFILE));
71+
testParameters.put("ocid",
72+
TestProperties.getOrAbort(OciTestProperty.OCI_TNS_NAMES_OCID));
73+
testParameters.put("tnsAlias",
74+
TestProperties.getOrAbort(OciTestProperty.OCI_TNS_NAMES_ALIAS));
75+
76+
Map<Parameter, CharSequence> parameterValues =
77+
createParameterValues(PROVIDER, testParameters);
78+
79+
String connectionString = PROVIDER.getConnectionString(parameterValues);
80+
assertNotNull(connectionString);
81+
}
82+
83+
@Test
84+
public void testInvalidOrNonExistentAlias() {
85+
Map<String, String> testParameters = new HashMap<>();
86+
testParameters.put("authenticationMethod", "config-file");
87+
testParameters.put("configFile",
88+
TestProperties.getOrAbort(OciTestProperty.OCI_CONFIG_FILE));
89+
testParameters.put("profile",
90+
TestProperties.getOrAbort(OciTestProperty.OCI_CONFIG_PROFILE));
91+
testParameters.put("ocid",
92+
TestProperties.getOrAbort(OciTestProperty.OCI_TNS_NAMES_OCID));
93+
testParameters.put("tnsAlias", "INVALID_ALIAS");
94+
95+
Map<Parameter, CharSequence> parameterValues =
96+
createParameterValues(PROVIDER, testParameters);
97+
98+
assertThrows(IllegalArgumentException.class,
99+
() -> PROVIDER.getConnectionString(parameterValues),
100+
"Expected IllegalArgumentException for invalid alias"
101+
);
102+
}
103+
104+
@Test
105+
public void testMissingAliasParameter() {
106+
Map<String, String> testParameters = new HashMap<>();
107+
testParameters.put("authenticationMethod", "config-file");
108+
testParameters.put("configFile",
109+
TestProperties.getOrAbort(OciTestProperty.OCI_CONFIG_FILE));
110+
testParameters.put("profile",
111+
TestProperties.getOrAbort(OciTestProperty.OCI_CONFIG_PROFILE));
112+
testParameters.put("ocid",
113+
TestProperties.getOrAbort(OciTestProperty.OCI_TNS_NAMES_OCID));
114+
115+
Map<Parameter, CharSequence> parameterValues =
116+
createParameterValues(PROVIDER, testParameters);
117+
118+
assertThrows(IllegalArgumentException.class,
119+
() -> PROVIDER.getConnectionString(parameterValues),
120+
"Expected IllegalArgumentException when tnsAlias parameter is missing"
121+
);
122+
}
123+
124+
@Test
125+
public void testPlainTextEncodedTnsnamesContent() {
126+
Map<String, String> testParameters = new HashMap<>();
127+
testParameters.put("authenticationMethod", "config-file");
128+
testParameters.put("configFile",
129+
TestProperties.getOrAbort(OciTestProperty.OCI_CONFIG_FILE));
130+
testParameters.put("profile",
131+
TestProperties.getOrAbort(OciTestProperty.OCI_CONFIG_PROFILE));
132+
testParameters.put("ocid",
133+
TestProperties.getOrAbort(OciTestProperty.OCI_NON_BASE64_TNS_NAMES_OCID));
134+
testParameters.put("tnsAlias",
135+
TestProperties.getOrAbort(OciTestProperty.OCI_TNS_NAMES_ALIAS));
136+
137+
Map<Parameter, CharSequence> parameterValues =
138+
createParameterValues(PROVIDER, testParameters);
139+
140+
String connectionString = PROVIDER.getConnectionString(parameterValues);
141+
assertNotNull(connectionString);
142+
}
143+
}

0 commit comments

Comments
 (0)