Skip to content

Commit 2576ae9

Browse files
committed
Added back accidentally removed tests.
1 parent 2519404 commit 2576ae9

2 files changed

Lines changed: 194 additions & 18 deletions

File tree

oauth2_http/java/com/google/auth/oauth2/IdentityPoolCredentials.java

Lines changed: 39 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ public class IdentityPoolCredentials extends ExternalAccountCredentials {
9696
== IdentityPoolCredentialSourceType.CERTIFICATE) {
9797
try {
9898
this.subjectTokenSupplier =
99-
createCertificateSubjectTokenSupplier(credentialSource);
99+
createCertificateSubjectTokenSupplier(builder, credentialSource);
100100
} catch (IOException e) {
101101
throw new RuntimeException(
102102
// Wrap IOException in RuntimeException because constructors cannot throw checked
@@ -160,31 +160,40 @@ public Builder toBuilder() {
160160
}
161161

162162
private IdentityPoolSubjectTokenSupplier createCertificateSubjectTokenSupplier(
163-
IdentityPoolCredentialSource credentialSource) throws IOException {
164-
final IdentityPoolCredentialSource.CertificateConfig certConfig =
165-
credentialSource.getCertificateConfig();
166-
String explicitCertConfigPath =
167-
certConfig.useDefaultCertificateConfig()
168-
? null
169-
: certConfig.getCertificateConfigLocation();
170-
163+
Builder builder, IdentityPoolCredentialSource credentialSource) throws IOException {
171164
// Configure the mTLS transport with the x509 keystore.
172-
X509Provider x509Provider =
173-
new X509Provider(getEnvironmentProvider(), getPropertyProvider(), explicitCertConfigPath);
165+
X509Provider x509Provider = getX509Provider(builder, credentialSource);
174166
KeyStore mtlsKeyStore = x509Provider.getKeyStore();
175167
this.transportFactory = new MtlsHttpTransportFactory(mtlsKeyStore);
176168

177169
// Initialize the subject token supplier with the certificate path.
178-
String configCertPath =
179-
MtlsUtils.getCertificatePath(
180-
getEnvironmentProvider(), getPropertyProvider(), explicitCertConfigPath);
181-
credentialSource.setCredentialLocation(configCertPath);
170+
credentialSource.setCredentialLocation(x509Provider.getCertificatePath());
182171
return new CertificateIdentityPoolSubjectTokenSupplier(credentialSource);
183172
}
184173

174+
private X509Provider getX509Provider(
175+
Builder builder, IdentityPoolCredentialSource credentialSource) {
176+
final IdentityPoolCredentialSource.CertificateConfig certConfig =
177+
credentialSource.getCertificateConfig();
178+
179+
// Use the provided X509Provider if available, otherwise initialize a default one.
180+
X509Provider x509Provider = builder.x509Provider;
181+
if (x509Provider == null) {
182+
// Determine the certificate path based on the configuration.
183+
String explicitCertConfigPath =
184+
certConfig.useDefaultCertificateConfig()
185+
? null
186+
: certConfig.getCertificateConfigLocation();
187+
x509Provider =
188+
new X509Provider(getEnvironmentProvider(), getPropertyProvider(), explicitCertConfigPath);
189+
}
190+
return x509Provider;
191+
}
192+
185193
public static class Builder extends ExternalAccountCredentials.Builder {
186194

187195
private IdentityPoolSubjectTokenSupplier subjectTokenSupplier;
196+
private X509Provider x509Provider;
188197

189198
Builder() {}
190199

@@ -195,9 +204,21 @@ public static class Builder extends ExternalAccountCredentials.Builder {
195204
}
196205
}
197206

198-
199-
200-
207+
/**
208+
* Sets a custom {@link X509Provider} to manage the client certificate and private key for mTLS.
209+
* If set, this provider will be used instead of the default behavior which initializes an
210+
* {@code X509Provider} based on the {@code certificateConfigLocation} or default paths found in
211+
* the {@code credentialSource}. This is primarily used for testing.
212+
*
213+
* @param x509Provider the custom X509 provider to use.
214+
* @return this {@code Builder} object
215+
*/
216+
@CanIgnoreReturnValue
217+
@VisibleForTesting
218+
Builder setX509Provider(X509Provider x509Provider) {
219+
this.x509Provider = x509Provider;
220+
return this;
221+
}
201222
/**
202223
* Sets the subject token supplier. The supplier should return a valid subject token string.
203224
*

oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1078,6 +1078,51 @@ void build_withCertificateSource_succeeds() throws Exception {
10781078
"Metrics header should indicate certificate source");
10791079
}
10801080

1081+
@Test
1082+
void build_withCertificateSourceAndCustomX509Provider_success()
1083+
throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException {
1084+
// Create an empty KeyStore and a spy on a custom X509Provider.
1085+
KeyStore keyStore = KeyStore.getInstance("JKS");
1086+
keyStore.load(null, null);
1087+
TestX509Provider x509Provider =
1088+
spy(new TestX509Provider(keyStore, "/path/to/certificate.json"));
1089+
1090+
// Set up credential source for certificate type.
1091+
Map<String, Object> certificateMap = new HashMap<>();
1092+
certificateMap.put("use_default_certificate_config", true);
1093+
Map<String, Object> credentialSourceMap = new HashMap<>();
1094+
credentialSourceMap.put("certificate", certificateMap);
1095+
IdentityPoolCredentialSource credentialSource =
1096+
new IdentityPoolCredentialSource(credentialSourceMap);
1097+
MockExternalAccountCredentialsTransportFactory mockTransportFactory =
1098+
new MockExternalAccountCredentialsTransportFactory();
1099+
1100+
// Build credentials with the custom provider.
1101+
IdentityPoolCredentials credentials =
1102+
IdentityPoolCredentials.newBuilder()
1103+
.setX509Provider(x509Provider)
1104+
.setHttpTransportFactory(mockTransportFactory)
1105+
.setAudience("test-audience")
1106+
.setSubjectTokenType("test-token-type")
1107+
.setCredentialSource(credentialSource)
1108+
.build();
1109+
1110+
// Verify successful creation and correct internal setup.
1111+
assertNotNull(credentials, "Credentials should be successfully created");
1112+
assertTrue(
1113+
credentials.getIdentityPoolSubjectTokenSupplier()
1114+
instanceof CertificateIdentityPoolSubjectTokenSupplier,
1115+
"Subject token supplier should be for certificates");
1116+
assertEquals(
1117+
IdentityPoolCredentials.CERTIFICATE_METRICS_HEADER_VALUE,
1118+
credentials.getCredentialSourceType(),
1119+
"Metrics header should indicate certificate source");
1120+
1121+
// Verify the custom provider methods were called during build.
1122+
verify(x509Provider).getKeyStore();
1123+
verify(x509Provider).getCertificatePath();
1124+
}
1125+
10811126
@Test
10821127
void build_withDefaultCertificate_throwsOnTransportInitFailure() {
10831128
// Setup credential source to use default certificate config.
@@ -1104,6 +1149,81 @@ void build_withDefaultCertificate_throwsOnTransportInitFailure() {
11041149
exception.getMessage());
11051150
}
11061151

1152+
@Test
1153+
void build_withCustomProvider_throwsOnGetKeyStore()
1154+
throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException {
1155+
// Simulate a scenario where the X509Provider fails to load the KeyStore, typically due to an
1156+
// IOException when reading the certificate or private key files.
1157+
KeyStore keyStore = KeyStore.getInstance("JKS");
1158+
keyStore.load(null, null);
1159+
TestX509Provider x509Provider = new TestX509Provider(keyStore, "/path/to/certificate.json");
1160+
x509Provider.setShouldThrowOnGetKeyStore(true); // Configure to throw
1161+
1162+
Map<String, Object> certificateMap = new HashMap<>();
1163+
certificateMap.put("certificate_config_location", "/path/to/certificate.json");
1164+
1165+
// Expect RuntimeException because the constructor wraps the IOException.
1166+
RuntimeException exception =
1167+
assertThrows(
1168+
RuntimeException.class,
1169+
() -> createCredentialsWithCertificate(x509Provider, certificateMap));
1170+
1171+
// Verify the cause is the expected IOException from the mock.
1172+
assertNotNull(exception.getCause());
1173+
assertTrue(exception.getCause() instanceof IOException);
1174+
assertEquals("Simulated IOException on get keystore", exception.getCause().getMessage());
1175+
1176+
// Verify the wrapper exception message
1177+
assertEquals(
1178+
"Failed to initialize IdentityPoolCredentials from certificate source due to an I/O error.",
1179+
exception.getMessage());
1180+
}
1181+
1182+
@Test
1183+
void build_withCustomProvider_throwsOnGetCertificatePath()
1184+
throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException {
1185+
// Simulate a scenario where the X509Provider cannot access or read the certificate
1186+
// configuration file needed to determine the certificate path, resulting in an IOException.
1187+
KeyStore keyStore = KeyStore.getInstance("JKS");
1188+
keyStore.load(null, null);
1189+
TestX509Provider x509Provider = new TestX509Provider(keyStore, "/path/to/certificate.json");
1190+
x509Provider.setShouldThrowOnGetCertificatePath(true); // Configure to throw
1191+
1192+
Map<String, Object> certificateMap = new HashMap<>();
1193+
certificateMap.put("certificate_config_location", "/path/to/certificate.json");
1194+
1195+
// Expect RuntimeException because the constructor wraps the IOException.
1196+
RuntimeException exception =
1197+
assertThrows(
1198+
RuntimeException.class,
1199+
() -> createCredentialsWithCertificate(x509Provider, certificateMap));
1200+
1201+
// Verify the cause is the expected IOException from the mock.
1202+
assertNotNull(exception.getCause());
1203+
assertTrue(exception.getCause() instanceof IOException);
1204+
assertEquals("Simulated IOException on certificate path", exception.getCause().getMessage());
1205+
1206+
// Verify the wrapper exception message
1207+
assertEquals(
1208+
"Failed to initialize IdentityPoolCredentials from certificate source due to an I/O error.",
1209+
exception.getMessage());
1210+
}
1211+
1212+
private void createCredentialsWithCertificate(
1213+
X509Provider x509Provider, Map<String, Object> certificateMap) {
1214+
Map<String, Object> credentialSourceMap = new HashMap<>();
1215+
credentialSourceMap.put("certificate", certificateMap);
1216+
IdentityPoolCredentialSource credentialSource =
1217+
new IdentityPoolCredentialSource(credentialSourceMap);
1218+
1219+
IdentityPoolCredentials.newBuilder()
1220+
.setX509Provider(x509Provider)
1221+
.setHttpTransportFactory(new MockExternalAccountCredentialsTransportFactory())
1222+
.setAudience("")
1223+
.setSubjectTokenType("")
1224+
.setCredentialSource(credentialSource)
1225+
.build();
1226+
}
11071227

11081228
static InputStream writeIdentityPoolCredentialsStream(
11091229
String tokenUrl,
@@ -1186,5 +1306,40 @@ public HttpTransport create() {
11861306
}
11871307
}
11881308

1309+
static class TestX509Provider extends X509Provider {
1310+
private final KeyStore keyStore;
1311+
private final String certPath;
1312+
private boolean throwOnGetKeyStore;
1313+
private boolean throwOnGetCertificatePath;
1314+
1315+
public TestX509Provider(KeyStore keyStore, String certPath) {
1316+
super();
1317+
this.keyStore = keyStore;
1318+
this.certPath = certPath;
1319+
}
1320+
1321+
public void setShouldThrowOnGetKeyStore(boolean throwOnGetKeyStore) {
1322+
this.throwOnGetKeyStore = throwOnGetKeyStore;
1323+
}
1324+
1325+
public void setShouldThrowOnGetCertificatePath(boolean throwOnGetCertificatePath) {
1326+
this.throwOnGetCertificatePath = throwOnGetCertificatePath;
1327+
}
1328+
1329+
@Override
1330+
public KeyStore getKeyStore() throws IOException {
1331+
if (throwOnGetKeyStore) {
1332+
throw new IOException("Simulated IOException on get keystore");
1333+
}
1334+
return keyStore;
1335+
}
11891336

1337+
@Override
1338+
public String getCertificatePath() throws IOException {
1339+
if (throwOnGetCertificatePath) {
1340+
throw new IOException("Simulated IOException on certificate path");
1341+
}
1342+
return certPath;
1343+
}
1344+
}
11901345
}

0 commit comments

Comments
 (0)