Skip to content

Commit 0f0a5ef

Browse files
committed
Add remaining tests, refactor, increase GCP test machine
1 parent ec07732 commit 0f0a5ef

File tree

3 files changed

+90
-54
lines changed

3 files changed

+90
-54
lines changed

.evergreen/.evg.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2134,6 +2134,7 @@ task_groups:
21342134
binary: bash
21352135
env:
21362136
GCPOIDC_VMNAME_PREFIX: "JAVA_DRIVER"
2137+
GCPKMS_MACHINETYPE: "e2-medium" # comparable elapsed time to Azure; default was starved, caused timeouts
21372138
args:
21382139
- ${DRIVERS_TOOLS}/.evergreen/auth_oidc/gcp/setup.sh
21392140
teardown_task:

driver-core/src/main/com/mongodb/internal/authentication/GcpCredentialHelper.java

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -33,31 +33,32 @@
3333
*/
3434
public final class GcpCredentialHelper {
3535

36-
public static CredentialInfo fetchGcpCredentialInfo(final String resource) {
37-
String endpoint = "http://metadata/computeMetadata/v1/instance/service-accounts/default/identity?" + resource;
38-
return new CredentialInfo(
39-
getBsonDocument(endpoint).getValue(),
40-
Duration.ZERO);
41-
}
42-
4336
public static BsonDocument obtainFromEnvironment() {
4437
String endpoint = "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token";
45-
return new BsonDocument("accessToken", getBsonDocument(endpoint));
46-
}
4738

48-
private static BsonString getBsonDocument(final String endpoint) {
4939
Map<String, String> header = new HashMap<>();
5040
header.put("Metadata-Flavor", "Google");
51-
header.put("Accept", "application/json");
5241
String response = getHttpContents("GET", endpoint, header);
5342
BsonDocument responseDocument = BsonDocument.parse(response);
5443
if (responseDocument.containsKey("access_token")) {
55-
return responseDocument.get("access_token").asString();
44+
return new BsonDocument("accessToken", responseDocument.get("access_token"));
5645
} else {
57-
throw new MongoClientException("access_token is missing from GCE metadata response. Full response is ''" + response);
46+
throw new MongoClientException("access_token is missing from GCE metadata response. Full response is ''"
47+
+ response);
5848
}
5949
}
6050

51+
public static CredentialInfo fetchGcpCredentialInfo(final String resource) {
52+
String endpoint = "http://metadata/computeMetadata/v1/instance/service-accounts/default/identity?audience="
53+
+ resource;
54+
Map<String, String> header = new HashMap<>();
55+
header.put("Metadata-Flavor", "Google");;
56+
String response = getHttpContents("GET", endpoint, header);
57+
return new CredentialInfo(
58+
new BsonString(response).getValue(),
59+
Duration.ZERO);
60+
}
61+
6162
private GcpCredentialHelper() {
6263
}
6364
}

driver-sync/src/test/functional/com/mongodb/internal/connection/OidcAuthenticationProseTests.java

Lines changed: 75 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import org.bson.BsonDocument;
3434
import org.bson.BsonInt32;
3535
import org.bson.BsonString;
36+
import org.bson.Document;
3637
import org.junit.jupiter.api.AfterEach;
3738
import org.junit.jupiter.api.BeforeEach;
3839
import org.junit.jupiter.api.Test;
@@ -71,7 +72,6 @@
7172
import static org.junit.jupiter.api.Assertions.assertFalse;
7273
import static org.junit.jupiter.api.Assertions.assertThrows;
7374
import static org.junit.jupiter.api.Assertions.assertTrue;
74-
import static org.junit.jupiter.api.Assertions.fail;
7575
import static org.junit.jupiter.api.Assumptions.assumeTrue;
7676
import static util.ThreadTestHelpers.executeAll;
7777

@@ -201,7 +201,7 @@ public void test2p2RequestCallbackReturnsNull() {
201201
//noinspection ConstantConditions
202202
OidcCallback callback = (context) -> null;
203203
MongoClientSettings clientSettings = this.createSettings(callback);
204-
performFind(clientSettings, MongoConfigurationException.class,
204+
assertFindFails(clientSettings, MongoConfigurationException.class,
205205
"Result of callback must not be null");
206206
}
207207

@@ -216,12 +216,9 @@ public void test2p3CallbackReturnsMissingData() {
216216
// we ensure that the error is propagated
217217
MongoClientSettings clientSettings = createSettings(callback);
218218
try (MongoClient mongoClient = createMongoClient(clientSettings)) {
219-
try {
220-
performFind(mongoClient);
221-
fail();
222-
} catch (Exception e) {
223-
assertCause(IllegalArgumentException.class, "accessToken can not be null", e);
224-
}
219+
assertCause(IllegalArgumentException.class,
220+
"accessToken can not be null",
221+
() -> performFind(mongoClient));
225222
}
226223
}
227224

@@ -230,13 +227,9 @@ public void test2p4InvalidClientConfigurationWithCallback() {
230227
String uri = getOidcUri() + "&authMechanismProperties=ENVIRONMENT:" + getOidcEnv();
231228
MongoClientSettings settings = createSettings(
232229
uri, createCallback(), null, OIDC_CALLBACK_KEY);
233-
try {
234-
performFind(settings);
235-
fail();
236-
} catch (Exception e) {
237-
assertCause(IllegalArgumentException.class,
238-
"OIDC_CALLBACK must not be specified when ENVIRONMENT is specified", e);
239-
}
230+
assertCause(IllegalArgumentException.class,
231+
"OIDC_CALLBACK must not be specified when ENVIRONMENT is specified",
232+
() -> performFind(settings));
240233
}
241234

242235
@Test
@@ -282,13 +275,9 @@ public void test3p2AuthFailsWithoutCachedToken() {
282275
(x) -> new OidcCallbackResult("invalid_token", Duration.ZERO);
283276
MongoClientSettings clientSettings = createSettings(callback);
284277
try (MongoClient mongoClient = createMongoClient(clientSettings)) {
285-
try {
286-
performFind(mongoClient);
287-
fail();
288-
} catch (Exception e) {
289-
assertCause(MongoCommandException.class,
290-
"Command failed with error 18 (AuthenticationFailed):", e);
291-
}
278+
assertCause(MongoCommandException.class,
279+
"Command failed with error 18 (AuthenticationFailed):",
280+
() -> performFind(mongoClient));
292281
}
293282
}
294283

@@ -321,8 +310,6 @@ public void test3p3UnexpectedErrorDoesNotClearCache() {
321310
}
322311
}
323312

324-
// TODO-OIDC reinstate 2 broken(?) tests in mongodb-oidc-no-retry.json
325-
326313
@Test
327314
public void test4p1Reauthentication() {
328315
TestCallback callback = createCallback();
@@ -335,6 +322,59 @@ public void test4p1Reauthentication() {
335322
assertEquals(2, callback.invocations.get());
336323
}
337324

325+
@Test
326+
public void test4p2ReadCommandsFailIfReauthenticationFails() {
327+
// Create a `MongoClient` whose OIDC callback returns one good token
328+
// and then bad tokens after the first call.
329+
TestCallback wrappedCallback = createCallback();
330+
OidcCallback callback = (context) -> {
331+
OidcCallbackResult result1 = wrappedCallback.callback(context);
332+
return new OidcCallbackResult(
333+
wrappedCallback.getInvocations() > 1 ? "bad" : result1.getAccessToken(),
334+
Duration.ZERO,
335+
null);
336+
};
337+
MongoClientSettings clientSettings = createSettings(callback);
338+
try (MongoClient mongoClient = createMongoClient(clientSettings)) {
339+
performFind(mongoClient);
340+
failCommand(391, 1, "find");
341+
assertCause(MongoCommandException.class,
342+
"Command failed with error 18",
343+
() -> performFind(mongoClient));
344+
}
345+
assertEquals(2, wrappedCallback.invocations.get());
346+
}
347+
348+
@Test
349+
public void test4p3WriteCommandsFailIfReauthenticationFails() {
350+
// Create a `MongoClient` whose OIDC callback returns one good token
351+
// and then bad tokens after the first call.
352+
TestCallback wrappedCallback = createCallback();
353+
OidcCallback callback = (context) -> {
354+
OidcCallbackResult result1 = wrappedCallback.callback(context);
355+
return new OidcCallbackResult(
356+
wrappedCallback.getInvocations() > 1 ? "bad" : result1.getAccessToken(),
357+
Duration.ZERO,
358+
null);
359+
};
360+
MongoClientSettings clientSettings = createSettings(callback);
361+
try (MongoClient mongoClient = createMongoClient(clientSettings)) {
362+
performInsert(mongoClient);
363+
failCommand(391, 1, "insert");
364+
assertCause(MongoCommandException.class,
365+
"Command failed with error 18",
366+
() -> performInsert(mongoClient));
367+
}
368+
assertEquals(2, wrappedCallback.invocations.get());
369+
}
370+
371+
private static void performInsert(final MongoClient mongoClient) {
372+
mongoClient
373+
.getDatabase("test")
374+
.getCollection("test")
375+
.insertOne(Document.parse("{ x: 1 }"));
376+
}
377+
338378
@Test
339379
public void test5p1Azure() {
340380
assumeTrue(getOidcEnv().equals("azure"));
@@ -410,7 +450,7 @@ public void testh1p5MultiplePrincipalNoUser() {
410450
// Create an OIDC configured client with `MONGODB_URI_MULTI` and no username.
411451
MongoClientSettings clientSettings = createSettingsMulti(null, createHumanCallback());
412452
// Assert that a `find` operation fails.
413-
performFind(clientSettings, MongoCommandException.class, "Authentication failed");
453+
assertFindFails(clientSettings, MongoCommandException.class, "Authentication failed");
414454
}
415455

416456
@Test
@@ -420,15 +460,15 @@ public void testh1p6AllowedHostsBlocked() {
420460
//- Assert that a ``find`` operation fails with a client-side error.
421461
MongoClientSettings clientSettings1 = createSettings(getOidcUri(),
422462
createHumanCallback(), null, OIDC_HUMAN_CALLBACK_KEY, Collections.emptyList());
423-
performFind(clientSettings1, MongoSecurityException.class, "not permitted by ALLOWED_HOSTS");
463+
assertFindFails(clientSettings1, MongoSecurityException.class, "not permitted by ALLOWED_HOSTS");
424464

425465
//- Create a client that uses the URL
426466
// ``mongodb://localhost/?authMechanism=MONGODB-OIDC&ignored=example.com``, a
427467
// human callback, and an ``ALLOWED_HOSTS`` that contains ``["example.com"]``.
428468
//- Assert that a ``find`` operation fails with a client-side error.
429469
MongoClientSettings clientSettings2 = createSettings(getOidcUri() + "&ignored=example.com",
430470
createHumanCallback(), null, OIDC_HUMAN_CALLBACK_KEY, Arrays.asList("example.com"));
431-
performFind(clientSettings2, MongoSecurityException.class, "not permitted by ALLOWED_HOSTS");
471+
assertFindFails(clientSettings2, MongoSecurityException.class, "not permitted by ALLOWED_HOSTS");
432472
}
433473

434474
// Not a prose test
@@ -485,14 +525,14 @@ public void testh2p2HumanCallbackReturnsMissingData() {
485525
assumeTestEnvironment();
486526
//noinspection ConstantConditions
487527
OidcCallback callbackNull = (context) -> null;
488-
performFind(createHumanSettings(callbackNull, null),
528+
assertFindFails(createHumanSettings(callbackNull, null),
489529
MongoConfigurationException.class,
490530
"Result of callback must not be null");
491531

492532
//noinspection ConstantConditions
493533
OidcCallback callback =
494534
(context) -> new OidcCallbackResult(null, Duration.ZERO);
495-
performFind(createHumanSettings(callback, null),
535+
assertFindFails(createHumanSettings(callback, null),
496536
IllegalArgumentException.class,
497537
"accessToken can not be null");
498538
}
@@ -503,7 +543,7 @@ public void testRefreshTokenAbsent() {
503543
// additionally, check validation for refresh in machine workflow:
504544
OidcCallback callbackMachineRefresh =
505545
(context) -> new OidcCallbackResult("access", Duration.ZERO, "exists");
506-
performFind(createSettings(callbackMachineRefresh),
546+
assertFindFails(createSettings(callbackMachineRefresh),
507547
MongoConfigurationException.class,
508548
"Refresh token must only be provided in human workflow");
509549
}
@@ -549,7 +589,7 @@ public void testh3p2NoSpecAuthIfNoCachedToken() {
549589
failCommand(18, 1, "saslStart");
550590
TestListener listener = new TestListener();
551591
TestCommandListener commandListener = new TestCommandListener(listener);
552-
performFind(createHumanSettings(createHumanCallback(), commandListener),
592+
assertFindFails(createHumanSettings(createHumanCallback(), commandListener),
553593
MongoCommandException.class,
554594
"Command failed with error 18");
555595
assertEquals(Arrays.asList(
@@ -833,7 +873,7 @@ private void performFind(final MongoClientSettings settings) {
833873
}
834874
}
835875

836-
private <T extends Throwable> void performFind(
876+
private <T extends Throwable> void assertFindFails(
837877
final MongoClientSettings settings,
838878
final Class<T> expectedExceptionOrCause,
839879
final String expectedMessage) {
@@ -852,21 +892,15 @@ private void performFind(final MongoClient mongoClient) {
852892

853893
private static <T extends Throwable> void assertCause(
854894
final Class<T> expectedCause, final String expectedMessageFragment, final Executable e) {
855-
Throwable actualException = assertThrows(Throwable.class, e);
856-
assertCause(expectedCause, expectedMessageFragment, actualException);
857-
}
858-
859-
private static <T extends Throwable> void assertCause(
860-
final Class<T> expectedCause, final String expectedMessageFragment, final Throwable actualException) {
861-
Throwable cause = actualException;
895+
Throwable cause = assertThrows(Throwable.class, e);
862896
while (cause.getCause() != null) {
863897
cause = cause.getCause();
864898
}
865899
if (!expectedCause.isInstance(cause)) {
866-
throw new AssertionFailedError("Unexpected cause: " + actualException.getClass(), actualException);
900+
throw new AssertionFailedError("Unexpected cause: " + assertThrows(Throwable.class, e).getClass(), assertThrows(Throwable.class, e));
867901
}
868902
if (!cause.getMessage().contains(expectedMessageFragment)) {
869-
throw new AssertionFailedError("Unexpected message", actualException);
903+
throw new AssertionFailedError("Unexpected message", assertThrows(Throwable.class, e));
870904
}
871905
}
872906

0 commit comments

Comments
 (0)