|
35 | 35 | import static org.junit.jupiter.api.Assertions.assertEquals; |
36 | 36 | import static org.junit.jupiter.api.Assertions.assertFalse; |
37 | 37 | import static org.junit.jupiter.api.Assertions.assertNull; |
| 38 | +import static org.junit.jupiter.api.Assertions.assertThrows; |
38 | 39 | import static org.junit.jupiter.api.Assertions.assertTrue; |
39 | 40 |
|
40 | 41 | import com.google.api.client.testing.http.MockHttpTransport; |
@@ -156,6 +157,23 @@ public void testRefreshClosesResponse() throws Exception { |
156 | 157 | assertTrue(mockResponse.isDisconnected(), "Response should have been disconnected"); |
157 | 158 | } |
158 | 159 |
|
| 160 | + @Test |
| 161 | + public void testRefreshThrowsIOExceptionOnExpiringToken() { |
| 162 | + final String url = "https://example.com/rab"; |
| 163 | + final AccessToken token = |
| 164 | + new AccessToken( |
| 165 | + "token", new java.util.Date(testClock.currentTimeMillis() + 120_000L)); // 2 minutes, within 3 min skew |
| 166 | + |
| 167 | + MockHttpTransport transport = new MockHttpTransport.Builder().build(); |
| 168 | + HttpTransportFactory transportFactory = () -> transport; |
| 169 | + |
| 170 | + assertThrows( |
| 171 | + IOException.class, |
| 172 | + () -> { |
| 173 | + RegionalAccessBoundary.refresh(transportFactory, url, token, testClock, 1000); |
| 174 | + }); |
| 175 | + } |
| 176 | + |
159 | 177 | @Test |
160 | 178 | public void testManagerTriggersRefreshInGracePeriod() throws InterruptedException { |
161 | 179 | final String url = |
@@ -410,4 +428,91 @@ public boolean isDisconnected() { |
410 | 428 | // Verify that MtlsHttpTransportFactory.create() was called to retrieve the mTLS transport |
411 | 429 | Mockito.verify(mockMtlsFactory, Mockito.times(2)).create(); |
412 | 430 | } |
| 431 | + |
| 432 | + @Test |
| 433 | + public void |
| 434 | + regionalAccessBoundary_withMtlsEnabledButInitializationFailed_shouldFallbackToNonMtlsEndpoint() |
| 435 | + throws IOException, InterruptedException { |
| 436 | + |
| 437 | + MockExternalAccountCredentialsTransport transport = |
| 438 | + new MockExternalAccountCredentialsTransport(); |
| 439 | + |
| 440 | + // Configure the environment provider to enable mTLS. |
| 441 | + // X509Provider will use the invalid certificate config. |
| 442 | + TestEnvironmentProvider testEnvProvider = new TestEnvironmentProvider(); |
| 443 | + testEnvProvider.setEnv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "true"); |
| 444 | + testEnvProvider.setEnv( |
| 445 | + "GOOGLE_API_CERTIFICATE_CONFIG", |
| 446 | + new File("testresources/mtls/invalid_certificate_config.json").getAbsolutePath()); |
| 447 | + |
| 448 | + final String url = |
| 449 | + "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/default:allowedLocations"; |
| 450 | + final AccessToken token = |
| 451 | + new AccessToken( |
| 452 | + "token", new java.util.Date(System.currentTimeMillis() + 10 * 3600000L)); |
| 453 | + |
| 454 | + RegionalAccessBoundaryProvider provider = () -> url; |
| 455 | + |
| 456 | + // Use default OAuth2Utils.HTTP_TRANSPORT_FACTORY |
| 457 | + HttpTransportFactory transportFactory = OAuth2Utils.HTTP_TRANSPORT_FACTORY; |
| 458 | + |
| 459 | + RegionalAccessBoundaryManager manager = |
| 460 | + new RegionalAccessBoundaryManager( |
| 461 | + testClock, |
| 462 | + RegionalAccessBoundaryManager.DEFAULT_MAX_RETRY_ELAPSED_TIME_MILLIS, |
| 463 | + com.google.common.util.concurrent.MoreExecutors.newDirectExecutorService()); |
| 464 | + |
| 465 | + // Mock static RegionalAccessBoundary.refresh method to intercept the call |
| 466 | + try (org.mockito.MockedStatic<RegionalAccessBoundary> rabMock = |
| 467 | + org.mockito.Mockito.mockStatic(RegionalAccessBoundary.class)) { |
| 468 | + |
| 469 | + RegionalAccessBoundary mockRab = |
| 470 | + new RegionalAccessBoundary( |
| 471 | + "fallback-encoded", Arrays.asList("fallback-loc"), testClock.currentTimeMillis(), testClock); |
| 472 | + |
| 473 | + rabMock |
| 474 | + .when( |
| 475 | + () -> |
| 476 | + RegionalAccessBoundary.refresh( |
| 477 | + org.mockito.ArgumentMatchers.any(), |
| 478 | + org.mockito.ArgumentMatchers.any(), |
| 479 | + org.mockito.ArgumentMatchers.any(), |
| 480 | + org.mockito.ArgumentMatchers.any(), |
| 481 | + org.mockito.ArgumentMatchers.anyInt())) |
| 482 | + .thenReturn(mockRab); |
| 483 | + |
| 484 | + // Trigger refresh |
| 485 | + manager.triggerAsyncRefresh( |
| 486 | + transportFactory, |
| 487 | + provider, |
| 488 | + token, |
| 489 | + testEnvProvider, |
| 490 | + SystemPropertyProvider.getInstance()); |
| 491 | + |
| 492 | + // Verify it was cached |
| 493 | + assertEquals("fallback-encoded", manager.getCachedRAB().getEncodedLocations()); |
| 494 | + |
| 495 | + final org.mockito.ArgumentCaptor<String> urlCaptor = |
| 496 | + org.mockito.ArgumentCaptor.forClass(String.class); |
| 497 | + final org.mockito.ArgumentCaptor<HttpTransportFactory> factoryCaptor = |
| 498 | + org.mockito.ArgumentCaptor.forClass(HttpTransportFactory.class); |
| 499 | + |
| 500 | + // Verify static call and capture arguments |
| 501 | + rabMock.verify( |
| 502 | + () -> |
| 503 | + RegionalAccessBoundary.refresh( |
| 504 | + factoryCaptor.capture(), |
| 505 | + urlCaptor.capture(), |
| 506 | + org.mockito.ArgumentMatchers.any(), |
| 507 | + org.mockito.ArgumentMatchers.any(), |
| 508 | + org.mockito.ArgumentMatchers.anyInt()), |
| 509 | + org.mockito.Mockito.times(1)); |
| 510 | + |
| 511 | + // Verify the URL passed to refresh did NOT get changed to the mTLS endpoint |
| 512 | + assertEquals(url, urlCaptor.getValue()); |
| 513 | + // Verify that the transport factory used is the default one |
| 514 | + assertEquals(OAuth2Utils.HTTP_TRANSPORT_FACTORY, factoryCaptor.getValue()); |
| 515 | + } |
| 516 | + } |
413 | 517 | } |
| 518 | + |
0 commit comments