diff --git a/google-auth-library-java/oauth2_http/java/com/google/auth/oauth2/GoogleCredentials.java b/google-auth-library-java/oauth2_http/java/com/google/auth/oauth2/GoogleCredentials.java index eeb69708dbc1..e423a68ac18b 100644 --- a/google-auth-library-java/oauth2_http/java/com/google/auth/oauth2/GoogleCredentials.java +++ b/google-auth-library-java/oauth2_http/java/com/google/auth/oauth2/GoogleCredentials.java @@ -373,9 +373,7 @@ final RegionalAccessBoundary getRegionalAccessBoundary() { */ void refreshRegionalAccessBoundaryIfExpired(@Nullable URI uri, @Nullable AccessToken token) throws IOException { - if (!(this instanceof RegionalAccessBoundaryProvider) - || !RegionalAccessBoundary.isEnabled() - || !isDefaultUniverseDomain()) { + if (!(this instanceof RegionalAccessBoundaryProvider) || !isDefaultUniverseDomain()) { return; } diff --git a/google-auth-library-java/oauth2_http/java/com/google/auth/oauth2/RegionalAccessBoundary.java b/google-auth-library-java/oauth2_http/java/com/google/auth/oauth2/RegionalAccessBoundary.java index b2a3f42942d7..dfcbe8491cd5 100644 --- a/google-auth-library-java/oauth2_http/java/com/google/auth/oauth2/RegionalAccessBoundary.java +++ b/google-auth-library-java/oauth2_http/java/com/google/auth/oauth2/RegionalAccessBoundary.java @@ -40,12 +40,11 @@ import com.google.api.client.http.HttpResponse; import com.google.api.client.http.HttpUnsuccessfulResponseHandler; import com.google.api.client.json.GenericJson; -import com.google.api.client.json.JsonParser; +import com.google.api.client.json.JsonObjectParser; import com.google.api.client.util.Clock; import com.google.api.client.util.ExponentialBackOff; import com.google.api.client.util.Key; import com.google.auth.http.HttpTransportFactory; -import com.google.common.annotations.VisibleForTesting; import com.google.common.base.MoreObjects; import com.google.common.base.Preconditions; import java.io.IOException; @@ -53,7 +52,6 @@ import java.io.Serializable; import java.util.Collections; import java.util.List; -import javax.annotation.Nullable; /** * Represents the regional access boundary configuration for a credential. This class holds the @@ -67,10 +65,6 @@ final class RegionalAccessBoundary implements Serializable { static final String X_ALLOWED_LOCATIONS_HEADER_KEY = "x-allowed-locations"; private static final long serialVersionUID = -2428522338274020302L; - // Note: this is for internal testing use use only. - // TODO: Fix unit test mocks so this can be removed - // Refer -> https://github.com/googleapis/google-auth-library-java/issues/1898 - static final String ENABLE_EXPERIMENT_ENV_VAR = "GOOGLE_AUTH_TRUST_BOUNDARY_ENABLE_EXPERIMENT"; static final long TTL_MILLIS = 6 * 60 * 60 * 1000L; // 6 hours static final long REFRESH_THRESHOLD_MILLIS = 1 * 60 * 60 * 1000L; // 1 hour @@ -79,8 +73,6 @@ final class RegionalAccessBoundary implements Serializable { private final long refreshTime; private transient Clock clock; - private static EnvironmentProvider environmentProvider = SystemEnvironmentProvider.getInstance(); - /** * Creates a new RegionalAccessBoundary instance. * @@ -172,30 +164,6 @@ public String toString() { } } - @VisibleForTesting - static void setEnvironmentProviderForTest(@Nullable EnvironmentProvider provider) { - environmentProvider = provider == null ? SystemEnvironmentProvider.getInstance() : provider; - } - - /** - * Checks if the regional access boundary feature is enabled. The feature is enabled if the - * environment variable or system property "GOOGLE_AUTH_TRUST_BOUNDARY_ENABLE_EXPERIMENT" is set - * to "true" or "1" (case-insensitive). - * - * @return True if the regional access boundary feature is enabled, false otherwise. - */ - static boolean isEnabled() { - String enabled = environmentProvider.getEnv(ENABLE_EXPERIMENT_ENV_VAR); - if (enabled == null) { - enabled = System.getProperty(ENABLE_EXPERIMENT_ENV_VAR); - } - if (enabled == null) { - return false; - } - String lowercased = enabled.toLowerCase(); - return "true".equals(lowercased) || "1".equals(enabled); - } - /** * Refreshes the regional access boundary by making a network call to the lookup endpoint. * @@ -223,6 +191,8 @@ static RegionalAccessBoundary refresh( HttpRequestFactory requestFactory = transportFactory.create().createRequestFactory(); HttpRequest request = requestFactory.buildGetRequest(new GenericUrl(url)); + // Disable automatic logging by google-http-java-client to prevent leakage of sensitive tokens. + request.setLoggingEnabled(false); request.getHeaders().setAuthorization("Bearer " + accessToken.getTokenValue()); // Add retry logic @@ -249,15 +219,20 @@ static RegionalAccessBoundary refresh( HttpIOExceptionHandler ioExceptionHandler = new HttpBackOffIOExceptionHandler(backoff); request.setIOExceptionHandler(ioExceptionHandler); + request.setParser(new JsonObjectParser(OAuth2Utils.JSON_FACTORY)); + RegionalAccessBoundaryResponse json; + HttpResponse response = null; try { - HttpResponse response = request.execute(); - String responseString = response.parseAsString(); - JsonParser parser = OAuth2Utils.JSON_FACTORY.createJsonParser(responseString); - json = parser.parseAndClose(RegionalAccessBoundaryResponse.class); + response = request.execute(); + json = response.parseAs(RegionalAccessBoundaryResponse.class); } catch (IOException e) { throw new IOException( "RegionalAccessBoundary: Failure while getting regional access boundaries:", e); + } finally { + if (response != null) { + response.disconnect(); + } } String encodedLocations = json.getEncodedLocations(); // The encodedLocations is the value attached to the x-allowed-locations header, and diff --git a/google-auth-library-java/oauth2_http/java/com/google/auth/oauth2/RegionalAccessBoundaryManager.java b/google-auth-library-java/oauth2_http/java/com/google/auth/oauth2/RegionalAccessBoundaryManager.java index eeea75bc2c86..e35efe86f7a0 100644 --- a/google-auth-library-java/oauth2_http/java/com/google/auth/oauth2/RegionalAccessBoundaryManager.java +++ b/google-auth-library-java/oauth2_http/java/com/google/auth/oauth2/RegionalAccessBoundaryManager.java @@ -31,11 +31,18 @@ package com.google.auth.oauth2; +import static com.google.auth.oauth2.LoggingUtils.log; + import com.google.api.client.util.Clock; import com.google.api.core.InternalApi; import com.google.auth.http.HttpTransportFactory; import com.google.common.annotations.VisibleForTesting; import com.google.common.util.concurrent.SettableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import java.util.logging.Level; import javax.annotation.Nullable; @@ -59,7 +66,7 @@ final class RegionalAccessBoundaryManager { * The default maximum elapsed time in milliseconds for retrying Regional Access Boundary lookup * requests. */ - private static final int DEFAULT_MAX_RETRY_ELAPSED_TIME_MILLIS = 60000; + static final int DEFAULT_MAX_RETRY_ELAPSED_TIME_MILLIS = 60000; /** * cachedRAB uses AtomicReference to provide thread-safe, lock-free access to the cached data for @@ -78,8 +85,41 @@ final class RegionalAccessBoundaryManager { private final AtomicReference cooldownState = new AtomicReference<>(new CooldownState(0, INITIAL_COOLDOWN_MILLIS)); + // Unbounded thread creation is discouraged in library code to avoid resource + // exhaustion. A shared, bounded executor service ensures a hard limit (5) + // on concurrent refresh tasks, while threadCount provides unique names + // for easier debugging. + private static final AtomicInteger threadCount = new AtomicInteger(0); + + // Bounded executor service ensures hard limits on concurrent refresh tasks and queued tasks + // to avoid resource exhaustion. + private static final int EXECUTOR_POOL_SIZE = 5; + private static final int EXECUTOR_QUEUE_CAPACITY = 100; + + private static final ExecutorService DEFAULT_SHARED_EXECUTOR; + + static { + ThreadPoolExecutor executor = + new ThreadPoolExecutor( + EXECUTOR_POOL_SIZE, // corePoolSize: threads to keep alive + EXECUTOR_POOL_SIZE, // maximumPoolSize: max threads allowed + 1, // keepAliveTime: time to wait before terminating idle threads + TimeUnit.HOURS, // unit for keepAliveTime + new LinkedBlockingQueue<>(EXECUTOR_QUEUE_CAPACITY), // work queue with bound + r -> { + Thread t = new Thread(r, "RAB-refresh-" + threadCount.getAndIncrement()); + t.setDaemon(true); + return t; + }); + // Allow core threads to time out so the executor can shrink to 0 when idle. + // Ensures threads are released when idle to avoid unnecessary resource usage. + executor.allowCoreThreadTimeOut(true); + DEFAULT_SHARED_EXECUTOR = executor; + } + private final transient Clock clock; private final int maxRetryElapsedTimeMillis; + private final ExecutorService executor; /** * Creates a new RegionalAccessBoundaryManager with the default retry timeout of 60 seconds. @@ -87,13 +127,20 @@ final class RegionalAccessBoundaryManager { * @param clock The clock to use for cooldown and expiration checks. */ RegionalAccessBoundaryManager(Clock clock) { - this(clock, DEFAULT_MAX_RETRY_ELAPSED_TIME_MILLIS); + this(clock, DEFAULT_MAX_RETRY_ELAPSED_TIME_MILLIS, DEFAULT_SHARED_EXECUTOR); } @VisibleForTesting RegionalAccessBoundaryManager(Clock clock, int maxRetryElapsedTimeMillis) { + this(clock, maxRetryElapsedTimeMillis, DEFAULT_SHARED_EXECUTOR); + } + + @VisibleForTesting + RegionalAccessBoundaryManager( + Clock clock, int maxRetryElapsedTimeMillis, ExecutorService executor) { this.clock = clock != null ? clock : Clock.SYSTEM; this.maxRetryElapsedTimeMillis = maxRetryElapsedTimeMillis; + this.executor = executor; } /** @@ -111,6 +158,11 @@ RegionalAccessBoundary getCachedRAB() { return null; } + @VisibleForTesting + void setCachedRAB(RegionalAccessBoundary rab) { + this.cachedRAB.set(rab); + } + /** * Triggers an asynchronous refresh of the RegionalAccessBoundary if it is not already being * refreshed and if the cooldown period is not active. @@ -161,19 +213,17 @@ void triggerAsyncRefresh( }; try { - // We use new Thread() here instead of - // CompletableFuture.runAsync() (which uses ForkJoinPool.commonPool()). - // This avoids consuming CPU resources since - // The common pool has a small, fixed number of threads designed for - // CPU-bound tasks. - Thread refreshThread = new Thread(refreshTask, "RAB-refresh-thread"); - refreshThread.setDaemon(true); - refreshThread.start(); + this.executor.submit(refreshTask); } catch (Exception | Error e) { // If scheduling fails (e.g., RejectedExecutionException, OutOfMemoryError for threads), // the task's finally block will never execute. We must release the lock here. - handleRefreshFailure( - new Exception("Regional Access Boundary background refresh failed to schedule", e)); + log( + LOGGER_PROVIDER, + Level.FINE, + null, + "Could not submit background refresh task for Regional Access Boundary. " + + "This is non-blocking and the library will attempt to refresh on the next access. Error: " + + e.getMessage()); future.setException(e); refreshFuture.set(null); } @@ -201,13 +251,13 @@ private void handleRefreshFailure(Exception e) { // concurrent failures from logging redundant messages or incorrectly calculating // the exponential backoff. if (cooldownState.compareAndSet(currentCooldownState, next)) { - LoggingUtils.log( + log( LOGGER_PROVIDER, Level.FINE, null, - "Regional Access Boundary lookup failed; entering cooldown for " + "Regional Access Boundary lookup was not successful; will retry after a cooldown of " + (next.durationMillis / 60000) - + "m. Error: " + + "m. This is handled automatically. Details: " + e.getMessage()); } } diff --git a/google-auth-library-java/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java b/google-auth-library-java/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java index a0930b796d04..26fe9151955b 100644 --- a/google-auth-library-java/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java +++ b/google-auth-library-java/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java @@ -32,6 +32,7 @@ package com.google.auth.oauth2; import static com.google.auth.Credentials.GOOGLE_DEFAULT_UNIVERSE; +import static com.google.auth.oauth2.TestUtils.createDummyRab; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -65,11 +66,6 @@ class AwsCredentialsTest extends BaseSerializationTest { @org.junit.jupiter.api.BeforeEach void setUp() {} - @org.junit.jupiter.api.AfterEach - void tearDown() { - RegionalAccessBoundary.setEnvironmentProviderForTest(null); - } - private static final String STS_URL = "https://sts.googleapis.com/v1/token"; private static final String AWS_CREDENTIALS_URL = "https://169.254.169.254"; private static final String AWS_CREDENTIALS_URL_WITH_ROLE = "https://169.254.169.254/roleName"; @@ -139,6 +135,7 @@ void refreshAccessToken_withoutServiceAccountImpersonation() throws IOException .setHttpTransportFactory(transportFactory) .setCredentialSource(buildAwsCredentialSource(transportFactory)) .build(); + awsCredential.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(awsCredential.clock)); AccessToken accessToken = awsCredential.refreshAccessToken(); @@ -168,6 +165,7 @@ void refreshAccessToken_withServiceAccountImpersonation() throws IOException { .setServiceAccountImpersonationUrl( transportFactory.transport.getServiceAccountImpersonationUrl()) .build(); + awsCredential.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(awsCredential.clock)); AccessToken accessToken = awsCredential.refreshAccessToken(); @@ -200,6 +198,7 @@ void refreshAccessToken_withServiceAccountImpersonationOptions() throws IOExcept .setServiceAccountImpersonationOptions( ExternalAccountCredentialsTest.buildServiceAccountImpersonationOptions()) .build(); + awsCredential.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(awsCredential.clock)); AccessToken accessToken = awsCredential.refreshAccessToken(); @@ -237,6 +236,7 @@ void refreshAccessTokenProgrammaticRefresh_withoutServiceAccountImpersonation() .setTokenUrl(STS_URL) .setSubjectTokenType("subjectTokenType") .build(); + awsCredential.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(awsCredential.clock)); AccessToken accessToken = awsCredential.refreshAccessToken(); @@ -268,6 +268,7 @@ void refreshAccessTokenProgrammaticRefresh_withServiceAccountImpersonation() thr .setServiceAccountImpersonationUrl( transportFactory.transport.getServiceAccountImpersonationUrl()) .build(); + awsCredential.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(awsCredential.clock)); AccessToken accessToken = awsCredential.refreshAccessToken(); @@ -291,6 +292,7 @@ void retrieveSubjectToken() throws IOException { .setHttpTransportFactory(transportFactory) .setCredentialSource(buildAwsCredentialSource(transportFactory)) .build(); + awsCredential.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(awsCredential.clock)); String subjectToken = URLDecoder.decode(awsCredential.retrieveSubjectToken(), "UTF-8"); @@ -335,6 +337,7 @@ void retrieveSubjectTokenWithSessionTokenUrl() throws IOException { .setHttpTransportFactory(transportFactory) .setCredentialSource(buildAwsImdsv2CredentialSource(transportFactory)) .build(); + awsCredential.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(awsCredential.clock)); String subjectToken = URLDecoder.decode(awsCredential.retrieveSubjectToken(), "UTF-8"); @@ -408,6 +411,7 @@ void retrieveSubjectToken_imdsv1EnvVariablesSet_metadataServerNotCalled() throws .setCredentialSource(buildAwsCredentialSource(transportFactory)) .setEnvironmentProvider(environmentProvider) .build(); + awsCredential.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(awsCredential.clock)); String subjectToken = URLDecoder.decode(awsCredential.retrieveSubjectToken(), "UTF-8"); @@ -453,6 +457,7 @@ void retrieveSubjectToken_imdsv2EnvVariablesSet_metadataServerNotCalled() throws .setCredentialSource(buildAwsImdsv2CredentialSource(transportFactory)) .setEnvironmentProvider(environmentProvider) .build(); + awsCredential.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(awsCredential.clock)); String subjectToken = URLDecoder.decode(awsCredential.retrieveSubjectToken(), "UTF-8"); @@ -492,6 +497,7 @@ void retrieveSubjectToken_noRegion_expectThrows() { .setHttpTransportFactory(transportFactory) .setCredentialSource(buildAwsCredentialSource(transportFactory)) .build(); + awsCredential.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(awsCredential.clock)); IOException exception = assertThrows(IOException.class, awsCredential::retrieveSubjectToken); assertEquals("Failed to retrieve AWS region.", exception.getMessage()); @@ -517,6 +523,7 @@ void retrieveSubjectToken_noRole_expectThrows() { .setHttpTransportFactory(transportFactory) .setCredentialSource(buildAwsCredentialSource(transportFactory)) .build(); + awsCredential.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(awsCredential.clock)); IOException exception = assertThrows(IOException.class, awsCredential::retrieveSubjectToken); assertEquals("Failed to retrieve AWS IAM role.", exception.getMessage()); @@ -545,6 +552,7 @@ void retrieveSubjectToken_noCredentials_expectThrows() { .setHttpTransportFactory(transportFactory) .setCredentialSource(buildAwsCredentialSource(transportFactory)) .build(); + awsCredential.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(awsCredential.clock)); IOException exception = assertThrows(IOException.class, awsCredential::retrieveSubjectToken); assertEquals("Failed to retrieve AWS credentials.", exception.getMessage()); @@ -576,6 +584,7 @@ void retrieveSubjectToken_noRegionUrlProvided() { .setHttpTransportFactory(transportFactory) .setCredentialSource(new AwsCredentialSource(credentialSource)) .build(); + awsCredential.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(awsCredential.clock)); IOException exception = assertThrows(IOException.class, awsCredential::retrieveSubjectToken); assertEquals( @@ -604,6 +613,7 @@ void retrieveSubjectToken_withProgrammaticRefresh() throws IOException { .setTokenUrl(STS_URL) .setSubjectTokenType("subjectTokenType") .build(); + awsCredential.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(awsCredential.clock)); String subjectToken = URLDecoder.decode(awsCredential.retrieveSubjectToken(), "UTF-8"); @@ -646,6 +656,7 @@ void retrieveSubjectToken_withProgrammaticRefreshSessionToken() throws IOExcepti .setTokenUrl(STS_URL) .setSubjectTokenType("subjectTokenType") .build(); + awsCredential.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(awsCredential.clock)); String subjectToken = URLDecoder.decode(awsCredential.retrieveSubjectToken(), "UTF-8"); @@ -696,6 +707,7 @@ void retrieveSubjectToken_passesContext() { .setTokenUrl(STS_URL) .setSubjectTokenType("subjectTokenType") .build(); + awsCredential.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(awsCredential.clock)); assertDoesNotThrow(awsCredential::retrieveSubjectToken); } @@ -718,6 +730,7 @@ void retrieveSubjectToken_withProgrammaticRefreshThrowsError() { .setTokenUrl(STS_URL) .setSubjectTokenType("subjectTokenType") .build(); + awsCredential.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(awsCredential.clock)); IOException exception = assertThrows(IOException.class, awsCredential::retrieveSubjectToken); assertEquals("test", exception.getMessage()); @@ -734,6 +747,8 @@ void getAwsSecurityCredentials_fromEnvironmentVariablesNoToken() throws IOExcept AwsCredentials.newBuilder(AWS_CREDENTIAL) .setEnvironmentProvider(environmentProvider) .build(); + testAwsCredentials.regionalAccessBoundaryManager.setCachedRAB( + createDummyRab(testAwsCredentials.clock)); AwsSecurityCredentials credentials = testAwsCredentials.getAwsSecurityCredentialsSupplier().getCredentials(emptyContext); @@ -767,6 +782,8 @@ void getAwsSecurityCredentials_fromEnvironmentVariablesWithToken() throws IOExce .setEnvironmentProvider(environmentProvider) .setCredentialSource(credSource) .build(); + testAwsCredentials.regionalAccessBoundaryManager.setCachedRAB( + createDummyRab(testAwsCredentials.clock)); AwsSecurityCredentials credentials = testAwsCredentials.getAwsSecurityCredentialsSupplier().getCredentials(emptyContext); @@ -789,6 +806,8 @@ void getAwsSecurityCredentials_fromEnvironmentVariables_noMetadataServerCall() AwsCredentials.newBuilder(AWS_CREDENTIAL) .setEnvironmentProvider(environmentProvider) .build(); + testAwsCredentials.regionalAccessBoundaryManager.setCachedRAB( + createDummyRab(testAwsCredentials.clock)); AwsSecurityCredentials credentials = testAwsCredentials.getAwsSecurityCredentialsSupplier().getCredentials(emptyContext); @@ -808,6 +827,7 @@ void getAwsSecurityCredentials_fromMetadataServer() throws IOException { .setHttpTransportFactory(transportFactory) .setCredentialSource(buildAwsCredentialSource(transportFactory)) .build(); + awsCredential.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(awsCredential.clock)); AwsSecurityCredentials credentials = awsCredential.getAwsSecurityCredentialsSupplier().getCredentials(emptyContext); @@ -840,6 +860,7 @@ void getAwsSecurityCredentials_fromMetadataServer_noUrlProvided() { .setHttpTransportFactory(transportFactory) .setCredentialSource(new AwsCredentialSource(credentialSource)) .build(); + awsCredential.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(awsCredential.clock)); IOException exception = assertThrows( @@ -868,6 +889,7 @@ void getAwsRegion_awsRegionEnvironmentVariable() throws IOException { .setCredentialSource(buildAwsCredentialSource(transportFactory)) .setEnvironmentProvider(environmentProvider) .build(); + awsCredentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(awsCredentials.clock)); String region = awsCredentials.getAwsSecurityCredentialsSupplier().getRegion(emptyContext); @@ -893,6 +915,7 @@ void getAwsRegion_awsDefaultRegionEnvironmentVariable() throws IOException { .setCredentialSource(buildAwsCredentialSource(transportFactory)) .setEnvironmentProvider(environmentProvider) .build(); + awsCredentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(awsCredentials.clock)); String region = awsCredentials.getAwsSecurityCredentialsSupplier().getRegion(emptyContext); @@ -914,6 +937,7 @@ void getAwsRegion_metadataServer() throws IOException { .setHttpTransportFactory(transportFactory) .setCredentialSource(buildAwsCredentialSource(transportFactory)) .build(); + awsCredentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(awsCredentials.clock)); String region = awsCredentials.getAwsSecurityCredentialsSupplier().getRegion(emptyContext); @@ -942,10 +966,12 @@ void createdScoped_clonedCredentialWithAddedScopes() { .setClientSecret("clientSecret") .setUniverseDomain("universeDomain") .build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); List newScopes = Arrays.asList("scope1", "scope2"); AwsCredentials newCredentials = (AwsCredentials) credentials.createScoped(newScopes); + newCredentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(newCredentials.clock)); assertEquals(credentials.getAudience(), newCredentials.getAudience()); assertEquals(credentials.getSubjectTokenType(), newCredentials.getSubjectTokenType()); @@ -1021,6 +1047,7 @@ void builder_allFields() { .setScopes(scopes) .setUniverseDomain("universeDomain") .build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); assertEquals("audience", credentials.getAudience()); assertEquals("subjectTokenType", credentials.getSubjectTokenType()); @@ -1057,6 +1084,7 @@ void builder_missingUniverseDomain_defaults() { .setClientSecret("clientSecret") .setScopes(scopes) .build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); assertEquals("https://test.com", credentials.getRegionalCredentialVerificationUrlOverride()); assertEquals("audience", credentials.getAudience()); @@ -1094,8 +1122,11 @@ void newBuilder_allFields() { .setScopes(scopes) .setUniverseDomain("universeDomain") .build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); AwsCredentials newBuilderCreds = AwsCredentials.newBuilder(credentials).build(); + newBuilderCreds.regionalAccessBoundaryManager.setCachedRAB( + createDummyRab(newBuilderCreds.clock)); assertEquals(credentials.getAudience(), newBuilderCreds.getAudience()); assertEquals(credentials.getSubjectTokenType(), newBuilderCreds.getSubjectTokenType()); assertEquals(credentials.getTokenUrl(), newBuilderCreds.getTokenUrl()); @@ -1131,8 +1162,11 @@ void newBuilder_noUniverseDomain_defaults() { .setClientSecret("clientSecret") .setScopes(scopes) .build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); AwsCredentials newBuilderCreds = AwsCredentials.newBuilder(credentials).build(); + newBuilderCreds.regionalAccessBoundaryManager.setCachedRAB( + createDummyRab(newBuilderCreds.clock)); assertEquals(credentials.getAudience(), newBuilderCreds.getAudience()); assertEquals(credentials.getSubjectTokenType(), newBuilderCreds.getSubjectTokenType()); assertEquals(credentials.getTokenUrl(), newBuilderCreds.getTokenUrl()); @@ -1170,6 +1204,7 @@ void builder_defaultRegionalCredentialVerificationUrlOverride() { .setClientSecret("clientSecret") .setScopes(scopes) .build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); assertNull(credentials.getRegionalCredentialVerificationUrlOverride()); assertEquals( @@ -1249,6 +1284,8 @@ void serialize() throws IOException, ClassNotFoundException { .setUniverseDomain("universeDomain") .setScopes(scopes) .build(); + testCredentials.regionalAccessBoundaryManager.setCachedRAB( + createDummyRab(testCredentials.clock)); AwsCredentials deserializedCredentials = serializeAndDeserialize(testCredentials); assertEquals(testCredentials, deserializedCredentials); @@ -1369,9 +1406,6 @@ public AwsSecurityCredentials getCredentials(ExternalAccountSupplierContext cont @Test public void testRefresh_regionalAccessBoundarySuccess() throws IOException, InterruptedException { - TestEnvironmentProvider environmentProvider = new TestEnvironmentProvider(); - RegionalAccessBoundary.setEnvironmentProviderForTest(environmentProvider); - environmentProvider.setEnv(RegionalAccessBoundary.ENABLE_EXPERIMENT_ENV_VAR, "1"); MockExternalAccountCredentialsTransportFactory transportFactory = new MockExternalAccountCredentialsTransportFactory(); diff --git a/google-auth-library-java/oauth2_http/javatests/com/google/auth/oauth2/ComputeEngineCredentialsTest.java b/google-auth-library-java/oauth2_http/javatests/com/google/auth/oauth2/ComputeEngineCredentialsTest.java index 8b20d0cc20f4..78bfd5ddaaa4 100644 --- a/google-auth-library-java/oauth2_http/javatests/com/google/auth/oauth2/ComputeEngineCredentialsTest.java +++ b/google-auth-library-java/oauth2_http/javatests/com/google/auth/oauth2/ComputeEngineCredentialsTest.java @@ -34,6 +34,7 @@ import static com.google.auth.oauth2.ComputeEngineCredentials.METADATA_RESPONSE_EMPTY_CONTENT_ERROR_MESSAGE; import static com.google.auth.oauth2.ImpersonatedCredentialsTest.SA_CLIENT_EMAIL; import static com.google.auth.oauth2.RegionalAccessBoundary.X_ALLOWED_LOCATIONS_HEADER_KEY; +import static com.google.auth.oauth2.TestUtils.createDummyRab; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -80,11 +81,6 @@ class ComputeEngineCredentialsTest extends BaseSerializationTest { @org.junit.jupiter.api.BeforeEach void setUp() {} - @org.junit.jupiter.api.AfterEach - void tearDown() { - RegionalAccessBoundary.setEnvironmentProviderForTest(null); - } - private static final URI CALL_URI = URI.create("http://googleapis.com/testapi/v1/foo"); private static final String TOKEN_URL = @@ -398,6 +394,7 @@ void getRequestMetadata_hasAccessToken() throws IOException { transportFactory.transport.setServiceAccountEmail(SA_CLIENT_EMAIL); ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); Map> metadata = credentials.getRequestMetadata(CALL_URI); TestUtils.assertContainsBearerToken(metadata, ACCESS_TOKEN); @@ -414,6 +411,7 @@ void getRequestMetadata_shouldInvalidateAccessTokenWhenScoped_newAccessTokenFrom transportFactory.transport.setServiceAccountEmail("SA_CLIENT_EMAIL"); ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); Map> metadata = credentials.getRequestMetadata(CALL_URI); TestUtils.assertContainsBearerToken(metadata, ACCESS_TOKEN); @@ -421,6 +419,8 @@ void getRequestMetadata_shouldInvalidateAccessTokenWhenScoped_newAccessTokenFrom assertNotNull(credentials.getAccessToken()); ComputeEngineCredentials scopedCredentialCopy = (ComputeEngineCredentials) credentials.createScoped(SCOPES); + scopedCredentialCopy.regionalAccessBoundaryManager.setCachedRAB( + createDummyRab(scopedCredentialCopy.clock)); assertNull(scopedCredentialCopy.getAccessToken()); Map> metadataForCopiedCredentials = scopedCredentialCopy.getRequestMetadata(CALL_URI); @@ -435,6 +435,7 @@ void getRequestMetadata_missingServiceAccount_throws() { transportFactory.transport.setServiceAccountEmail("SA_CLIENT_EMAIL"); ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); IOException exception = assertThrows(IOException.class, () -> credentials.getRequestMetadata(CALL_URI)); String message = exception.getMessage(); @@ -450,6 +451,7 @@ void getRequestMetadata_serverError_throws() { transportFactory.transport.setServiceAccountEmail("SA_CLIENT_EMAIL"); ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); IOException exception = assertThrows(IOException.class, () -> credentials.getRequestMetadata(CALL_URI)); String message = exception.getMessage(); @@ -573,6 +575,7 @@ void getAccount_sameAs() { transportFactory.transport.setServiceAccountEmail(defaultAccountEmail); ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); assertEquals(defaultAccountEmail, credentials.getAccount()); @@ -606,6 +609,7 @@ public LowLevelHttpResponse execute() throws IOException { transportFactory.transport.setServiceAccountEmail(defaultAccountEmail); ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); RuntimeException exception = assertThrows(RuntimeException.class, credentials::getAccount); assertEquals("Failed to get service account", exception.getMessage()); @@ -637,6 +641,7 @@ public LowLevelHttpResponse execute() throws IOException { transportFactory.transport.setServiceAccountEmail(defaultAccountEmail); ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); RuntimeException exception = assertThrows(RuntimeException.class, credentials::getAccount); assertEquals("Failed to get service account", exception.getMessage()); @@ -654,6 +659,7 @@ void sign_sameAs() { transportFactory.transport.setSignature(expectedSignature); ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); assertArrayEquals(expectedSignature, credentials.sign(expectedSignature)); } @@ -666,6 +672,7 @@ void sign_getUniverseException() { transportFactory.transport.setServiceAccountEmail(defaultAccountEmail); ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); transportFactory.transport.setStatusCode(501); assertThrows(IOException.class, credentials::getUniverseDomain); @@ -684,6 +691,7 @@ void sign_getAccountFails() { transportFactory.transport.setSignature(expectedSignature); ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); SigningException exception = assertThrows(SigningException.class, () -> credentials.sign(expectedSignature)); @@ -719,6 +727,7 @@ public LowLevelHttpResponse execute() throws IOException { ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); byte[] bytes = {0xD, 0xE, 0xA, 0xD}; @@ -757,6 +766,7 @@ public LowLevelHttpResponse execute() throws IOException { ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); byte[] bytes = {0xD, 0xE, 0xA, 0xD}; @@ -788,6 +798,7 @@ public LowLevelHttpResponse execute() throws IOException { ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); IOException exception = assertThrows(IOException.class, credentials::refreshAccessToken); assertTrue(exception.getCause().getMessage().contains("503")); @@ -851,6 +862,7 @@ public LowLevelHttpResponse execute() throws IOException { ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); String universeDomain = credentials.getUniverseDomain(); assertEquals("some-universe.xyz", universeDomain); @@ -878,6 +890,7 @@ public LowLevelHttpResponse execute() throws IOException { ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); String universeDomain = credentials.getUniverseDomain(); assertEquals(Credentials.GOOGLE_DEFAULT_UNIVERSE, universeDomain); @@ -905,6 +918,7 @@ public LowLevelHttpResponse execute() throws IOException { ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); String universeDomain = credentials.getUniverseDomain(); assertEquals(Credentials.GOOGLE_DEFAULT_UNIVERSE, universeDomain); @@ -951,6 +965,7 @@ void getUniverseDomain_fromMetadata_non404error_throws() { ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); for (int status = 400; status < 600; status++) { // 404 should not throw and tested separately @@ -991,6 +1006,7 @@ public LowLevelHttpResponse execute() { ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); byte[] bytes = {0xD, 0xE, 0xA, 0xD}; @@ -1007,6 +1023,7 @@ void idTokenWithAudience_sameAs() throws IOException { transportFactory.transport.setIdToken(STANDARD_ID_TOKEN); ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); String targetAudience = "https://foo.bar"; IdTokenCredentials tokenCredential = @@ -1027,6 +1044,7 @@ void idTokenWithAudience_standard() throws IOException { MockMetadataServerTransportFactory transportFactory = new MockMetadataServerTransportFactory(); ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); String targetAudience = "https://foo.bar"; IdTokenCredentials tokenCredential = @@ -1046,6 +1064,7 @@ void idTokenWithAudience_full() throws IOException { MockMetadataServerTransportFactory transportFactory = new MockMetadataServerTransportFactory(); ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); String targetAudience = "https://foo.bar"; IdTokenCredentials tokenCredential = @@ -1072,6 +1091,7 @@ void idTokenWithAudience_licenses() throws IOException { MockMetadataServerTransportFactory transportFactory = new MockMetadataServerTransportFactory(); ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); String targetAudience = "https://foo.bar"; IdTokenCredentials tokenCredential = @@ -1100,6 +1120,7 @@ void idTokenWithAudience_404StatusCode() { transportFactory.transport.setStatusCode(HttpStatusCodes.STATUS_CODE_NOT_FOUND); ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); IOException exception = assertThrows(IOException.class, () -> credentials.idTokenWithAudience("Audience", null)); assertEquals( @@ -1117,6 +1138,7 @@ void idTokenWithAudience_emptyContent() { transportFactory.transport.setEmptyContent(true); ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); IOException exception = assertThrows(IOException.class, () -> credentials.idTokenWithAudience("Audience", null)); assertEquals(METADATA_RESPONSE_EMPTY_CONTENT_ERROR_MESSAGE, exception.getMessage()); @@ -1128,6 +1150,7 @@ void idTokenWithAudience_503StatusCode() { transportFactory.transport.setStatusCode(HttpStatusCodes.STATUS_CODE_SERVICE_UNAVAILABLE); ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); assertThrows( GoogleAuthException.class, () -> credentials.idTokenWithAudience("Audience", null)); } @@ -1152,6 +1175,7 @@ public LowLevelHttpResponse execute() throws IOException { ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); String projectId = credentials.getProjectId(); assertEquals("some-project-id", projectId); } @@ -1162,6 +1186,7 @@ void getProjectId_metadataServerFailure_404StatusCode() { transportFactory.transport.setStatusCode(HttpStatusCodes.STATUS_CODE_SERVICE_UNAVAILABLE); ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); assertNull(credentials.getProjectId()); } @@ -1171,6 +1196,7 @@ void getProjectId_metadataServerFailure_otherStatusCode() { transportFactory.transport.setStatusCode(HttpStatusCodes.STATUS_CODE_UNAUTHORIZED); ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); assertNull(credentials.getProjectId()); } @@ -1180,6 +1206,7 @@ void getProjectId_explicitSet_noMDsCall() { new MockRequestCountingTransportFactory(); ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); credentials.setProjectId("explicit.project_id"); assertEquals("explicit.project_id", credentials.getProjectId()); @@ -1188,9 +1215,6 @@ void getProjectId_explicitSet_noMDsCall() { @org.junit.jupiter.api.Test void refresh_regionalAccessBoundarySuccess() throws IOException, InterruptedException { - TestEnvironmentProvider environmentProvider = new TestEnvironmentProvider(); - RegionalAccessBoundary.setEnvironmentProviderForTest(environmentProvider); - environmentProvider.setEnv(RegionalAccessBoundary.ENABLE_EXPERIMENT_ENV_VAR, "1"); String defaultAccountEmail = "default@email.com"; MockMetadataServerTransportFactory transportFactory = new MockMetadataServerTransportFactory(); diff --git a/google-auth-library-java/oauth2_http/javatests/com/google/auth/oauth2/ExternalAccountAuthorizedUserCredentialsTest.java b/google-auth-library-java/oauth2_http/javatests/com/google/auth/oauth2/ExternalAccountAuthorizedUserCredentialsTest.java index fbf3f79dbe65..b374e08111ff 100644 --- a/google-auth-library-java/oauth2_http/javatests/com/google/auth/oauth2/ExternalAccountAuthorizedUserCredentialsTest.java +++ b/google-auth-library-java/oauth2_http/javatests/com/google/auth/oauth2/ExternalAccountAuthorizedUserCredentialsTest.java @@ -32,6 +32,7 @@ package com.google.auth.oauth2; import static com.google.auth.Credentials.GOOGLE_DEFAULT_UNIVERSE; +import static com.google.auth.oauth2.TestUtils.createDummyRab; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; @@ -130,9 +131,7 @@ void setup() { } @org.junit.jupiter.api.AfterEach - void tearDown() { - RegionalAccessBoundary.setEnvironmentProviderForTest(null); - } + void tearDown() {} @Test void builder_allFields() throws IOException { @@ -708,6 +707,7 @@ void createScopedRequired_false() { void getRequestMetadata() throws IOException { GoogleCredentials credentials = ExternalAccountAuthorizedUserCredentials.fromJson(buildJsonCredentials(), transportFactory); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); Map> metadata = credentials.getRequestMetadata(CALL_URI); @@ -719,6 +719,7 @@ void getRequestMetadata() throws IOException { void getRequestMetadata_withQuotaProjectId() throws IOException { GoogleCredentials credentials = ExternalAccountAuthorizedUserCredentials.fromJson(buildJsonCredentials(), transportFactory); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); Map> metadata = credentials.getRequestMetadata(CALL_URI); @@ -737,6 +738,7 @@ void getRequestMetadata_withAccessToken() throws IOException { .setHttpTransportFactory(transportFactory) .setAccessToken(new AccessToken(ACCESS_TOKEN, /* expirationTime= */ null)) .build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); Map> metadata = credentials.getRequestMetadata(CALL_URI); @@ -1243,9 +1245,6 @@ void serialize() throws IOException, ClassNotFoundException { @org.junit.jupiter.api.Test void testRefresh_regionalAccessBoundarySuccess() throws IOException, InterruptedException { - TestEnvironmentProvider environmentProvider = new TestEnvironmentProvider(); - RegionalAccessBoundary.setEnvironmentProviderForTest(environmentProvider); - environmentProvider.setEnv(RegionalAccessBoundary.ENABLE_EXPERIMENT_ENV_VAR, "1"); ExternalAccountAuthorizedUserCredentials credentials = ExternalAccountAuthorizedUserCredentials.newBuilder() diff --git a/google-auth-library-java/oauth2_http/javatests/com/google/auth/oauth2/ExternalAccountCredentialsTest.java b/google-auth-library-java/oauth2_http/javatests/com/google/auth/oauth2/ExternalAccountCredentialsTest.java index 5b20f33db983..ae4fbb1aac8b 100644 --- a/google-auth-library-java/oauth2_http/javatests/com/google/auth/oauth2/ExternalAccountCredentialsTest.java +++ b/google-auth-library-java/oauth2_http/javatests/com/google/auth/oauth2/ExternalAccountCredentialsTest.java @@ -35,6 +35,7 @@ import static com.google.auth.oauth2.OAuth2Utils.IAM_CREDENTIALS_ALLOWED_LOCATIONS_URL_FORMAT_SERVICE_ACCOUNT; import static com.google.auth.oauth2.OAuth2Utils.IAM_CREDENTIALS_ALLOWED_LOCATIONS_URL_FORMAT_WORKFORCE_POOL; import static com.google.auth.oauth2.OAuth2Utils.IAM_CREDENTIALS_ALLOWED_LOCATIONS_URL_FORMAT_WORKLOAD_POOL; +import static com.google.auth.oauth2.TestUtils.createDummyRab; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertInstanceOf; @@ -92,11 +93,6 @@ void setup() { transportFactory = new MockExternalAccountCredentialsTransportFactory(); } - @org.junit.jupiter.api.AfterEach - void tearDown() { - RegionalAccessBoundary.setEnvironmentProviderForTest(null); - } - @Test void fromStream_identityPoolCredentials() throws IOException { GenericJson json = buildJsonIdentityPoolCredential(); @@ -1113,6 +1109,8 @@ void getRequestMetadata_withQuotaProjectId() throws IOException { .setCredentialSource(new TestCredentialSource(FILE_CREDENTIAL_SOURCE_MAP)) .setQuotaProjectId("quotaProjectId") .build(); + testCredentials.regionalAccessBoundaryManager.setCachedRAB( + createDummyRab(testCredentials.clock)); Map> requestMetadata = testCredentials.getRequestMetadata(URI.create("http://googleapis.com/foo/bar")); @@ -1302,9 +1300,7 @@ public void getRegionalAccessBoundaryUrl_invalidAudience_throws() { @Test public void refresh_workload_regionalAccessBoundarySuccess() throws IOException, InterruptedException { - TestEnvironmentProvider environmentProvider = new TestEnvironmentProvider(); - RegionalAccessBoundary.setEnvironmentProviderForTest(environmentProvider); - environmentProvider.setEnv(RegionalAccessBoundary.ENABLE_EXPERIMENT_ENV_VAR, "1"); + String audience = "//iam.googleapis.com/projects/12345/locations/global/workloadIdentityPools/my-pool/providers/my-provider"; @@ -1339,9 +1335,7 @@ public String retrieveSubjectToken() throws IOException { @Test public void refresh_workforce_regionalAccessBoundarySuccess() throws IOException, InterruptedException { - TestEnvironmentProvider environmentProvider = new TestEnvironmentProvider(); - RegionalAccessBoundary.setEnvironmentProviderForTest(environmentProvider); - environmentProvider.setEnv(RegionalAccessBoundary.ENABLE_EXPERIMENT_ENV_VAR, "1"); + String audience = "//iam.googleapis.com/locations/global/workforcePools/my-pool/providers/my-provider"; @@ -1376,9 +1370,7 @@ public String retrieveSubjectToken() throws IOException { @Test public void refresh_impersonated_workload_regionalAccessBoundarySuccess() throws IOException, InterruptedException { - TestEnvironmentProvider environmentProvider = new TestEnvironmentProvider(); - RegionalAccessBoundary.setEnvironmentProviderForTest(environmentProvider); - environmentProvider.setEnv(RegionalAccessBoundary.ENABLE_EXPERIMENT_ENV_VAR, "1"); + String projectNumber = "12345"; String poolId = "my-pool"; String providerId = "my-provider"; @@ -1440,9 +1432,7 @@ public void refresh_impersonated_workload_regionalAccessBoundarySuccess() @Test public void refresh_impersonated_workforce_regionalAccessBoundarySuccess() throws IOException, InterruptedException { - TestEnvironmentProvider environmentProvider = new TestEnvironmentProvider(); - RegionalAccessBoundary.setEnvironmentProviderForTest(environmentProvider); - environmentProvider.setEnv(RegionalAccessBoundary.ENABLE_EXPERIMENT_ENV_VAR, "1"); + String poolId = "my-pool"; String providerId = "my-provider"; String audience = diff --git a/google-auth-library-java/oauth2_http/javatests/com/google/auth/oauth2/GoogleCredentialsTest.java b/google-auth-library-java/oauth2_http/javatests/com/google/auth/oauth2/GoogleCredentialsTest.java index dd64a07d4a1f..18e5c4585eef 100644 --- a/google-auth-library-java/oauth2_http/javatests/com/google/auth/oauth2/GoogleCredentialsTest.java +++ b/google-auth-library-java/oauth2_http/javatests/com/google/auth/oauth2/GoogleCredentialsTest.java @@ -109,9 +109,7 @@ class GoogleCredentialsTest extends BaseSerializationTest { void setUp() {} @org.junit.jupiter.api.AfterEach - void tearDown() { - RegionalAccessBoundary.setEnvironmentProviderForTest(null); - } + void tearDown() {} @Test void getApplicationDefault_nullTransport_throws() { @@ -858,9 +856,6 @@ void serialize() throws IOException, ClassNotFoundException { @Test public void serialize_removesStaleRabHeaders() throws Exception { - TestEnvironmentProvider environmentProvider = new TestEnvironmentProvider(); - RegionalAccessBoundary.setEnvironmentProviderForTest(environmentProvider); - environmentProvider.setEnv(RegionalAccessBoundary.ENABLE_EXPERIMENT_ENV_VAR, "1"); MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); RegionalAccessBoundary rab = @@ -1046,9 +1041,7 @@ void getCredentialInfo_impersonatedServiceAccount() throws IOException { @Test public void regionalAccessBoundary_shouldFetchAndReturnRegionalAccessBoundaryDataSuccessfully() throws IOException, InterruptedException { - TestEnvironmentProvider environmentProvider = new TestEnvironmentProvider(); - RegionalAccessBoundary.setEnvironmentProviderForTest(environmentProvider); - environmentProvider.setEnv(RegionalAccessBoundary.ENABLE_EXPERIMENT_ENV_VAR, "1"); + MockTokenServerTransport transport = new MockTokenServerTransport(); transport.addServiceAccount(SA_CLIENT_EMAIL, ACCESS_TOKEN); RegionalAccessBoundary regionalAccessBoundary = @@ -1083,9 +1076,6 @@ public void regionalAccessBoundary_shouldFetchAndReturnRegionalAccessBoundaryDat @Test public void regionalAccessBoundary_shouldRetryRegionalAccessBoundaryLookupOnFailure() throws IOException, InterruptedException { - TestEnvironmentProvider environmentProvider = new TestEnvironmentProvider(); - RegionalAccessBoundary.setEnvironmentProviderForTest(environmentProvider); - environmentProvider.setEnv(RegionalAccessBoundary.ENABLE_EXPERIMENT_ENV_VAR, "1"); // This transport will be used for the regional access boundary lookup. // We will configure it to fail on the first attempt. @@ -1137,9 +1127,7 @@ public com.google.api.client.http.LowLevelHttpRequest buildRequest( @Test public void regionalAccessBoundary_refreshShouldNotThrowWhenNoValidAccessTokenIsPassed() throws IOException { - TestEnvironmentProvider environmentProvider = new TestEnvironmentProvider(); - RegionalAccessBoundary.setEnvironmentProviderForTest(environmentProvider); - environmentProvider.setEnv(RegionalAccessBoundary.ENABLE_EXPERIMENT_ENV_VAR, "1"); + MockTokenServerTransport transport = new MockTokenServerTransport(); // Return an expired access token. transport.addServiceAccount(SA_CLIENT_EMAIL, "expired-token"); @@ -1162,9 +1150,7 @@ public void regionalAccessBoundary_refreshShouldNotThrowWhenNoValidAccessTokenIs @Test public void regionalAccessBoundary_cooldownDoublingAndRefresh() throws IOException, InterruptedException { - TestEnvironmentProvider environmentProvider = new TestEnvironmentProvider(); - RegionalAccessBoundary.setEnvironmentProviderForTest(environmentProvider); - environmentProvider.setEnv(RegionalAccessBoundary.ENABLE_EXPERIMENT_ENV_VAR, "1"); + MockTokenServerTransport transport = new MockTokenServerTransport(); transport.addServiceAccount(SA_CLIENT_EMAIL, ACCESS_TOKEN); // Always fail lookup for now. @@ -1224,9 +1210,7 @@ public void regionalAccessBoundary_cooldownDoublingAndRefresh() @Test public void regionalAccessBoundary_shouldFailOpenWhenRefreshCannotBeStarted() throws IOException { - TestEnvironmentProvider environmentProvider = new TestEnvironmentProvider(); - RegionalAccessBoundary.setEnvironmentProviderForTest(environmentProvider); - environmentProvider.setEnv(RegionalAccessBoundary.ENABLE_EXPERIMENT_ENV_VAR, "1"); + // Use a simple AccessToken-based credential that won't try to refresh. GoogleCredentials credentials = GoogleCredentials.create(new AccessToken("some-token", null)); @@ -1238,9 +1222,7 @@ public void regionalAccessBoundary_shouldFailOpenWhenRefreshCannotBeStarted() th @Test public void regionalAccessBoundary_deduplicationOfConcurrentRefreshes() throws IOException, InterruptedException { - TestEnvironmentProvider environmentProvider = new TestEnvironmentProvider(); - RegionalAccessBoundary.setEnvironmentProviderForTest(environmentProvider); - environmentProvider.setEnv(RegionalAccessBoundary.ENABLE_EXPERIMENT_ENV_VAR, "1"); + MockTokenServerTransport transport = new MockTokenServerTransport(); transport.setRegionalAccessBoundary( new RegionalAccessBoundary("valid", Collections.singletonList("us-central1"), null)); @@ -1269,9 +1251,7 @@ public void regionalAccessBoundary_deduplicationOfConcurrentRefreshes() @Test public void regionalAccessBoundary_shouldSkipRefreshForRegionalEndpoints() throws IOException { - TestEnvironmentProvider environmentProvider = new TestEnvironmentProvider(); - RegionalAccessBoundary.setEnvironmentProviderForTest(environmentProvider); - environmentProvider.setEnv(RegionalAccessBoundary.ENABLE_EXPERIMENT_ENV_VAR, "1"); + MockTokenServerTransport transport = new MockTokenServerTransport(); GoogleCredentials credentials = createTestCredentials(transport); diff --git a/google-auth-library-java/oauth2_http/javatests/com/google/auth/oauth2/IdTokenCredentialsTest.java b/google-auth-library-java/oauth2_http/javatests/com/google/auth/oauth2/IdTokenCredentialsTest.java index e3dcec4b520c..56ebcc3f273e 100644 --- a/google-auth-library-java/oauth2_http/javatests/com/google/auth/oauth2/IdTokenCredentialsTest.java +++ b/google-auth-library-java/oauth2_http/javatests/com/google/auth/oauth2/IdTokenCredentialsTest.java @@ -31,6 +31,7 @@ package com.google.auth.oauth2; +import static com.google.auth.oauth2.TestUtils.createDummyRab; import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.IOException; @@ -46,6 +47,7 @@ void hashCode_equals() throws IOException { transportFactory.transport.setIdToken(ComputeEngineCredentialsTest.STANDARD_ID_TOKEN); ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); String targetAudience = "https://foo.bar"; IdTokenCredentials tokenCredential = @@ -72,6 +74,7 @@ void toString_equals() throws IOException { transportFactory.transport.setIdToken(ComputeEngineCredentialsTest.STANDARD_ID_TOKEN); ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); String targetAudience = "https://foo.bar"; IdTokenCredentials tokenCredential = @@ -99,6 +102,7 @@ void serialize() throws IOException, ClassNotFoundException { transportFactory.transport.setIdToken(ComputeEngineCredentialsTest.STANDARD_ID_TOKEN); ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); String targetAudience = "https://foo.bar"; IdTokenCredentials tokenCredential = diff --git a/google-auth-library-java/oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java b/google-auth-library-java/oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java index 399bf7246c9a..3a5dcd8720e7 100644 --- a/google-auth-library-java/oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java +++ b/google-auth-library-java/oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java @@ -34,6 +34,7 @@ import static com.google.auth.Credentials.GOOGLE_DEFAULT_UNIVERSE; import static com.google.auth.oauth2.MockExternalAccountCredentialsTransport.SERVICE_ACCOUNT_IMPERSONATION_URL; import static com.google.auth.oauth2.OAuth2Utils.JSON_FACTORY; +import static com.google.auth.oauth2.TestUtils.createDummyRab; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -81,9 +82,7 @@ class IdentityPoolCredentialsTest extends BaseSerializationTest { void setUp() {} @org.junit.jupiter.api.AfterEach - void tearDown() { - RegionalAccessBoundary.setEnvironmentProviderForTest(null); - } + void tearDown() {} @Test void createdScoped_clonedCredentialWithAddedScopes() { @@ -95,10 +94,12 @@ void createdScoped_clonedCredentialWithAddedScopes() { .setClientSecret("clientSecret") .setUniverseDomain("universeDomain") .build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); List newScopes = Arrays.asList("scope1", "scope2"); IdentityPoolCredentials newCredentials = credentials.createScoped(newScopes); + newCredentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(newCredentials.clock)); assertEquals(credentials.getAudience(), newCredentials.getAudience()); assertEquals(credentials.getSubjectTokenType(), newCredentials.getSubjectTokenType()); @@ -136,6 +137,7 @@ void retrieveSubjectToken_fileSourced() throws IOException { IdentityPoolCredentials.newBuilder(createBaseFileSourcedCredentials()) .setCredentialSource(credentialSource) .build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); String subjectToken = credentials.retrieveSubjectToken(); @@ -177,6 +179,7 @@ void retrieveSubjectToken_fileSourcedWithJsonFormat() throws IOException { .setHttpTransportFactory(transportFactory) .setCredentialSource(credentialSource) .build(); + credential.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credential.clock)); String subjectToken = credential.retrieveSubjectToken(); @@ -215,6 +218,7 @@ void retrieveSubjectToken_noFile_throws() { IdentityPoolCredentials.newBuilder(createBaseFileSourcedCredentials()) .setCredentialSource(credentialSource) .build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); IOException e = assertThrows(IOException.class, credentials::retrieveSubjectToken); assertEquals( @@ -233,6 +237,7 @@ void retrieveSubjectToken_urlSourced() throws IOException { .setCredentialSource( buildUrlBasedCredentialSource(transportFactory.transport.getMetadataUrl())) .build(); + credential.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credential.clock)); String subjectToken = credential.retrieveSubjectToken(); @@ -258,6 +263,7 @@ void retrieveSubjectToken_urlSourcedWithJsonFormat() throws IOException { .setHttpTransportFactory(transportFactory) .setCredentialSource(credentialSource) .build(); + credential.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credential.clock)); String subjectToken = credential.retrieveSubjectToken(); @@ -278,6 +284,7 @@ void retrieveSubjectToken_urlSourcedCredential_throws() { .setCredentialSource( buildUrlBasedCredentialSource(transportFactory.transport.getMetadataUrl())) .build(); + credential.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credential.clock)); IOException e = assertThrows(IOException.class, credential::retrieveSubjectToken); assertEquals( @@ -295,6 +302,7 @@ void retrieveSubjectToken_provider() throws IOException { .setCredentialSource(null) .setSubjectTokenSupplier(testProvider) .build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); String subjectToken = credentials.retrieveSubjectToken(); @@ -314,6 +322,7 @@ void retrieveSubjectToken_providerThrowsError() { .setCredentialSource(null) .setSubjectTokenSupplier(errorProvider) .build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); IOException e = assertThrows(IOException.class, credentials::retrieveSubjectToken); assertEquals("test", e.getMessage()); @@ -338,6 +347,7 @@ void retrieveSubjectToken_supplierPassesContext() throws IOException { .setCredentialSource(null) .setSubjectTokenSupplier(testSupplier) .build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); credentials.retrieveSubjectToken(); } @@ -359,6 +369,7 @@ void refreshAccessToken_withoutServiceAccountImpersonation() throws IOException .setCredentialSource( buildUrlBasedCredentialSource(transportFactory.transport.getMetadataUrl())) .build(); + credential.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credential.clock)); AccessToken accessToken = credential.refreshAccessToken(); @@ -385,6 +396,7 @@ void refreshAccessToken_internalOptionsSet() throws IOException { .setCredentialSource( buildUrlBasedCredentialSource(transportFactory.transport.getMetadataUrl())) .build(); + credential.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credential.clock)); AccessToken accessToken = credential.refreshAccessToken(); @@ -422,6 +434,7 @@ void refreshAccessToken_withServiceAccountImpersonation() throws IOException { .setCredentialSource( buildUrlBasedCredentialSource(transportFactory.transport.getMetadataUrl())) .build(); + credential.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credential.clock)); AccessToken accessToken = credential.refreshAccessToken(); @@ -455,6 +468,7 @@ void refreshAccessToken_withServiceAccountImpersonationOptions() throws IOExcept .setServiceAccountImpersonationOptions( ExternalAccountCredentialsTest.buildServiceAccountImpersonationOptions()) .build(); + credential.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credential.clock)); AccessToken accessToken = credential.refreshAccessToken(); @@ -491,6 +505,7 @@ void refreshAccessToken_Provider() throws IOException { .setTokenUrl(transportFactory.transport.getStsUrl()) .setHttpTransportFactory(transportFactory) .build(); + credential.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credential.clock)); AccessToken accessToken = credential.refreshAccessToken(); @@ -520,6 +535,7 @@ void refreshAccessToken_providerWithServiceAccountImpersonation() throws IOExcep .setTokenUrl(transportFactory.transport.getStsUrl()) .setHttpTransportFactory(transportFactory) .build(); + credential.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credential.clock)); AccessToken accessToken = credential.refreshAccessToken(); @@ -550,6 +566,7 @@ void refreshAccessToken_workforceWithServiceAccountImpersonation() throws IOExce buildUrlBasedCredentialSource(transportFactory.transport.getMetadataUrl())) .setWorkforcePoolUserProject("userProject") .build(); + credential.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credential.clock)); AccessToken accessToken = credential.refreshAccessToken(); @@ -587,6 +604,7 @@ void refreshAccessToken_workforceWithServiceAccountImpersonationOptions() throws .setServiceAccountImpersonationOptions( ExternalAccountCredentialsTest.buildServiceAccountImpersonationOptions()) .build(); + credential.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credential.clock)); AccessToken accessToken = credential.refreshAccessToken(); @@ -770,6 +788,7 @@ void builder_allFields() { .setScopes(scopes) .setUniverseDomain("universeDomain") .build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); assertEquals("audience", credentials.getAudience()); assertEquals("subjectTokenType", credentials.getSubjectTokenType()); @@ -804,6 +823,7 @@ void builder_subjectTokenSupplier() { .setClientSecret("clientSecret") .setScopes(scopes) .build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); assertEquals(testProvider, credentials.getIdentityPoolSubjectTokenSupplier()); } @@ -855,6 +875,7 @@ void builder_emptyWorkforceUserProjectWithWorkforceAudience() { .setCredentialSource(createFileCredentialSource()) .setQuotaProjectId("quotaProjectId") .build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); assertTrue(credentials.isWorkforcePoolConfiguration()); } @@ -909,6 +930,7 @@ void builder_missingUniverseDomain_defaults() { .setClientSecret("clientSecret") .setScopes(scopes) .build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); assertEquals("audience", credentials.getAudience()); assertEquals("subjectTokenType", credentials.getSubjectTokenType()); @@ -946,9 +968,13 @@ void newBuilder_allFields() { .setWorkforcePoolUserProject("workforcePoolUserProject") .setUniverseDomain("universeDomain") .build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); IdentityPoolCredentials newBuilderCreds = IdentityPoolCredentials.newBuilder(credentials).build(); + newBuilderCreds.regionalAccessBoundaryManager.setCachedRAB( + new RegionalAccessBoundary( + "dummy-locations", Arrays.asList("dummy-loc"), newBuilderCreds.clock)); assertEquals(credentials.getAudience(), newBuilderCreds.getAudience()); assertEquals(credentials.getSubjectTokenType(), newBuilderCreds.getSubjectTokenType()); assertEquals(credentials.getTokenUrl(), newBuilderCreds.getTokenUrl()); @@ -987,9 +1013,13 @@ void newBuilder_noUniverseDomain_defaults() { .setScopes(scopes) .setWorkforcePoolUserProject("workforcePoolUserProject") .build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); IdentityPoolCredentials newBuilderCreds = IdentityPoolCredentials.newBuilder(credentials).build(); + newBuilderCreds.regionalAccessBoundaryManager.setCachedRAB( + new RegionalAccessBoundary( + "dummy-locations", Arrays.asList("dummy-loc"), newBuilderCreds.clock)); assertEquals(credentials.getAudience(), newBuilderCreds.getAudience()); assertEquals(credentials.getSubjectTokenType(), newBuilderCreds.getSubjectTokenType()); assertEquals(credentials.getTokenUrl(), newBuilderCreds.getTokenUrl()); @@ -1018,6 +1048,9 @@ void serialize() throws IOException, ClassNotFoundException { .setClientSecret("clientSecret") .setUniverseDomain("universeDomain") .build(); + testCredentials.regionalAccessBoundaryManager.setCachedRAB( + new RegionalAccessBoundary( + "dummy-locations", Arrays.asList("dummy-loc"), testCredentials.clock)); IdentityPoolCredentials deserializedCredentials = serializeAndDeserialize(testCredentials); assertEquals(testCredentials, deserializedCredentials); @@ -1047,6 +1080,7 @@ void build_withCertificateSource_succeeds() throws Exception { .setSubjectTokenType("test-token-type") .setCredentialSource(credentialSource) .build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); // Verify successful creation and correct internal setup. assertNotNull(credentials, "Credentials should be successfully created"); @@ -1089,6 +1123,7 @@ void build_withDefaultCertificateConfig_success() .setSubjectTokenType("test-token-type") .setCredentialSource(credentialSource) .build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); // Verify successful creation and correct internal setup. assertNotNull(credentials, "Credentials should be successfully created"); @@ -1258,15 +1293,18 @@ private IdentityPoolCredentials createBaseFileSourcedCredentials() { IdentityPoolCredentialSource identityPoolCredentialSource = new IdentityPoolCredentialSource(fileCredentialSourceMap); - return IdentityPoolCredentials.newBuilder() - .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) - .setAudience( - "//iam.googleapis.com/projects/123/locations/global/workloadIdentityPools/pool/providers/provider") - .setSubjectTokenType("subjectTokenType") - .setTokenUrl(STS_URL) - .setTokenInfoUrl("tokenInfoUrl") - .setCredentialSource(identityPoolCredentialSource) - .build(); + IdentityPoolCredentials credentials = + IdentityPoolCredentials.newBuilder() + .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) + .setAudience( + "//iam.googleapis.com/projects/123/locations/global/workloadIdentityPools/pool/providers/provider") + .setSubjectTokenType("subjectTokenType") + .setTokenUrl(STS_URL) + .setTokenInfoUrl("tokenInfoUrl") + .setCredentialSource(identityPoolCredentialSource) + .build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); + return credentials; } private IdentityPoolCredentialSource createFileCredentialSource() { @@ -1312,9 +1350,6 @@ void setShouldThrowOnGetKeyStore(boolean shouldThrow) { @Test public void testRefresh_regionalAccessBoundarySuccess() throws IOException, InterruptedException { - TestEnvironmentProvider environmentProvider = new TestEnvironmentProvider(); - RegionalAccessBoundary.setEnvironmentProviderForTest(environmentProvider); - environmentProvider.setEnv(RegionalAccessBoundary.ENABLE_EXPERIMENT_ENV_VAR, "1"); MockExternalAccountCredentialsTransportFactory transportFactory = new MockExternalAccountCredentialsTransportFactory(); diff --git a/google-auth-library-java/oauth2_http/javatests/com/google/auth/oauth2/ImpersonatedCredentialsTest.java b/google-auth-library-java/oauth2_http/javatests/com/google/auth/oauth2/ImpersonatedCredentialsTest.java index fc3c2e9c783e..3664fb22c2ff 100644 --- a/google-auth-library-java/oauth2_http/javatests/com/google/auth/oauth2/ImpersonatedCredentialsTest.java +++ b/google-auth-library-java/oauth2_http/javatests/com/google/auth/oauth2/ImpersonatedCredentialsTest.java @@ -32,6 +32,7 @@ package com.google.auth.oauth2; import static com.google.auth.oauth2.RegionalAccessBoundary.X_ALLOWED_LOCATIONS_HEADER_KEY; +import static com.google.auth.oauth2.TestUtils.createDummyRab; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -163,11 +164,6 @@ void setup() throws IOException { mockTransportFactory = new MockIAMCredentialsServiceTransportFactory(); } - @org.junit.After - public void tearDown() { - RegionalAccessBoundary.setEnvironmentProviderForTest(null); - } - static GoogleCredentials getSourceCredentials() throws IOException { MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); PrivateKey privateKey = OAuth2Utils.privateKeyFromPkcs8(SA_PRIVATE_KEY_PKCS8); @@ -180,6 +176,8 @@ static GoogleCredentials getSourceCredentials() throws IOException { .setProjectId(PROJECT_ID) .setHttpTransportFactory(transportFactory) .build(); + sourceCredentials.regionalAccessBoundaryManager.setCachedRAB( + createDummyRab(sourceCredentials.clock)); transportFactory.transport.addServiceAccount(SA_CLIENT_EMAIL, ACCESS_TOKEN); transportFactory.transport.setRegionalAccessBoundary(REGIONAL_ACCESS_BOUNDARY); @@ -597,6 +595,8 @@ void getRequestMetadata_withQuotaProjectId() throws IOException, IllegalStateExc VALID_LIFETIME, mockTransportFactory, QUOTA_PROJECT_ID); + targetCredentials.regionalAccessBoundaryManager.setCachedRAB( + createDummyRab(targetCredentials.clock)); Map> metadata = targetCredentials.getRequestMetadata(); assertTrue(metadata.containsKey("x-goog-user-project")); @@ -619,6 +619,8 @@ void getRequestMetadata_withoutQuotaProjectId() throws IOException, IllegalState IMMUTABLE_SCOPES_LIST, VALID_LIFETIME, mockTransportFactory); + targetCredentials.regionalAccessBoundaryManager.setCachedRAB( + createDummyRab(targetCredentials.clock)); Map> metadata = targetCredentials.getRequestMetadata(); assertFalse(metadata.containsKey("x-goog-user-project")); @@ -1276,9 +1278,7 @@ void refreshAccessToken_afterSerialization_success() throws IOException, ClassNo @Test void refresh_regionalAccessBoundarySuccess() throws IOException, InterruptedException { - TestEnvironmentProvider environmentProvider = new TestEnvironmentProvider(); - RegionalAccessBoundary.setEnvironmentProviderForTest(environmentProvider); - environmentProvider.setEnv(RegionalAccessBoundary.ENABLE_EXPERIMENT_ENV_VAR, "1"); + // Mock regional access boundary response RegionalAccessBoundary regionalAccessBoundary = REGIONAL_ACCESS_BOUNDARY; diff --git a/google-auth-library-java/oauth2_http/javatests/com/google/auth/oauth2/LoggingTest.java b/google-auth-library-java/oauth2_http/javatests/com/google/auth/oauth2/LoggingTest.java index 68e9c8edf393..92ea38cbcf7d 100644 --- a/google-auth-library-java/oauth2_http/javatests/com/google/auth/oauth2/LoggingTest.java +++ b/google-auth-library-java/oauth2_http/javatests/com/google/auth/oauth2/LoggingTest.java @@ -43,6 +43,7 @@ import static com.google.auth.oauth2.ServiceAccountCredentialsTest.DEFAULT_ID_TOKEN; import static com.google.auth.oauth2.ServiceAccountCredentialsTest.SCOPES; import static com.google.auth.oauth2.ServiceAccountCredentialsTest.createDefaultBuilder; +import static com.google.auth.oauth2.TestUtils.createDummyRab; import static com.google.auth.oauth2.UserCredentialsTest.CLIENT_ID; import static com.google.auth.oauth2.UserCredentialsTest.CLIENT_SECRET; import static com.google.auth.oauth2.UserCredentialsTest.REFRESH_TOKEN; @@ -97,11 +98,6 @@ static void setup() { @org.junit.jupiter.api.BeforeEach void setUp() {} - @org.junit.jupiter.api.AfterEach - void tearDown() { - RegionalAccessBoundary.setEnvironmentProviderForTest(null); - } - @Test void userCredentials_getRequestMetadata_fromRefreshToken_hasAccessToken() throws IOException { TestAppender testAppender = setupTestLogger(UserCredentials.class); @@ -172,6 +168,7 @@ void serviceAccountCredentials_getRequestMetadata_hasAccessToken() throws IOExce ServiceAccountCredentialsTest.createDefaultBuilderWithToken(ACCESS_TOKEN) .setScopes(SCOPES) .build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); Map> metadata = credentials.getRequestMetadata(CALL_URI); TestUtils.assertContainsBearerToken(metadata, ACCESS_TOKEN); @@ -228,6 +225,7 @@ void serviceAccountCredentials_idTokenWithAudience_iamFlow_targetAudienceMatches .setHttpTransportFactory(transportFactory) .setUniverseDomain(nonGDU) .build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); String targetAudience = "https://foo.bar"; IdTokenCredentials tokenCredential = @@ -449,11 +447,12 @@ void getRequestMetadata_hasAccessToken() throws IOException { transportFactory.transport.setServiceAccountEmail("SA_CLIENT_EMAIL"); ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); Map> metadata = credentials.getRequestMetadata(CALL_URI); TestUtils.assertContainsBearerToken(metadata, ACCESS_TOKEN); - assertEquals(3, testAppender.events.size()); + assertEquals(5, testAppender.events.size()); ILoggingEvent accessTokenRequest = testAppender.events.get(0); assertEquals("Sending request to refresh access token", accessTokenRequest.getMessage()); @@ -490,6 +489,7 @@ void idTokenWithAudience_full() throws IOException { MockMetadataServerTransportFactory transportFactory = new MockMetadataServerTransportFactory(); ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); String targetAudience = "https://foo.bar"; IdTokenCredentials tokenCredential = @@ -544,6 +544,7 @@ void serviceAccountCredentials_exchangeToken_masksSensitiveTokens() throws IOExc ServiceAccountCredentialsTest.createDefaultBuilderWithToken(ACCESS_TOKEN) .setScopes(SCOPES) .build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); Map> metadata = credentials.getRequestMetadata(CALL_URI); TestUtils.assertContainsBearerToken(metadata, ACCESS_TOKEN); diff --git a/google-auth-library-java/oauth2_http/javatests/com/google/auth/oauth2/PluggableAuthCredentialsTest.java b/google-auth-library-java/oauth2_http/javatests/com/google/auth/oauth2/PluggableAuthCredentialsTest.java index adc945dd72ea..8576ffe38e3a 100644 --- a/google-auth-library-java/oauth2_http/javatests/com/google/auth/oauth2/PluggableAuthCredentialsTest.java +++ b/google-auth-library-java/oauth2_http/javatests/com/google/auth/oauth2/PluggableAuthCredentialsTest.java @@ -59,9 +59,7 @@ class PluggableAuthCredentialsTest extends BaseSerializationTest { @org.junit.jupiter.api.AfterEach - void tearDown() { - RegionalAccessBoundary.setEnvironmentProviderForTest(null); - } + void tearDown() {} // The default timeout for waiting for the executable to finish (30 seconds). private static final int DEFAULT_EXECUTABLE_TIMEOUT_MS = 30 * 1000; @@ -610,9 +608,6 @@ void serialize() { @Test public void testRefresh_regionalAccessBoundarySuccess() throws IOException, InterruptedException { - TestEnvironmentProvider environmentProvider = new TestEnvironmentProvider(); - RegionalAccessBoundary.setEnvironmentProviderForTest(environmentProvider); - environmentProvider.setEnv(RegionalAccessBoundary.ENABLE_EXPERIMENT_ENV_VAR, "1"); MockExternalAccountCredentialsTransportFactory transportFactory = new MockExternalAccountCredentialsTransportFactory(); diff --git a/google-auth-library-java/oauth2_http/javatests/com/google/auth/oauth2/RegionalAccessBoundaryTest.java b/google-auth-library-java/oauth2_http/javatests/com/google/auth/oauth2/RegionalAccessBoundaryTest.java index 7c7ccd690ce2..5664582ef059 100644 --- a/google-auth-library-java/oauth2_http/javatests/com/google/auth/oauth2/RegionalAccessBoundaryTest.java +++ b/google-auth-library-java/oauth2_http/javatests/com/google/auth/oauth2/RegionalAccessBoundaryTest.java @@ -31,9 +31,9 @@ package com.google.auth.oauth2; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import com.google.api.client.testing.http.MockHttpTransport; import com.google.api.client.testing.http.MockLowLevelHttpResponse; @@ -41,17 +41,16 @@ import com.google.auth.http.HttpTransportFactory; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.Collections; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicLong; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; -@RunWith(JUnit4.class) public class RegionalAccessBoundaryTest { private static final long TTL = RegionalAccessBoundary.TTL_MILLIS; @@ -59,12 +58,12 @@ public class RegionalAccessBoundaryTest { private TestClock testClock; - @Before + @BeforeEach public void setUp() { testClock = new TestClock(); } - @After + @AfterEach public void tearDown() {} @Test @@ -127,6 +126,27 @@ public void testSerialization() throws Exception { assertFalse(deserializedRab.isExpired()); } + @Test + public void testRefreshClosesResponse() throws Exception { + final String url = "https://example.com/rab"; + final AccessToken token = + new AccessToken("token", new java.util.Date(System.currentTimeMillis() + 3600000L)); + + TrackingMockLowLevelHttpResponse mockResponse = new TrackingMockLowLevelHttpResponse(); + mockResponse.setContentType("application/json"); + mockResponse.setContent("{\"encodedLocations\": \"encoded\", \"locations\": [\"loc\"]}"); + + MockHttpTransport transport = + new MockHttpTransport.Builder().setLowLevelHttpResponse(mockResponse).build(); + HttpTransportFactory transportFactory = () -> transport; + + RegionalAccessBoundary rab = + RegionalAccessBoundary.refresh(transportFactory, url, token, testClock, 1000); + + assertEquals("encoded", rab.getEncodedLocations()); + assertTrue(mockResponse.isDisconnected(), "Response should have been disconnected"); + } + @Test public void testManagerTriggersRefreshInGracePeriod() throws InterruptedException { final String url = @@ -200,11 +220,94 @@ public void testManagerTriggersRefreshInGracePeriod() throws InterruptedExceptio } assertTrue( - "Refresh should have completed and updated the cache within 5 seconds", - resultRab != null && newerEncoded.equals(resultRab.getEncodedLocations())); + resultRab != null && newerEncoded.equals(resultRab.getEncodedLocations()), + "Refresh should have completed and updated the cache within 5 seconds"); assertEquals(newerEncoded, resultRab.getEncodedLocations()); } + @Test + public void testExecutorQueueCapacityLimit() throws Exception { + final String url = "https://example.com/rab"; + final AccessToken token = + new AccessToken("token", new java.util.Date(System.currentTimeMillis() + 3600000L)); + RegionalAccessBoundaryProvider provider = () -> url; + + int poolSize = 5; + int queueCapacity = 100; + int totalCapacity = poolSize + queueCapacity; + + java.util.concurrent.ThreadPoolExecutor testExecutor = + new java.util.concurrent.ThreadPoolExecutor( + poolSize, + poolSize, + 1, + java.util.concurrent.TimeUnit.HOURS, + new java.util.concurrent.LinkedBlockingQueue<>(queueCapacity), + r -> { + Thread t = new Thread(r, "test-RAB-refresh"); + t.setDaemon(true); + return t; + }); + + CountDownLatch latch = new CountDownLatch(1); + + java.io.InputStream blockingStream = + new java.io.InputStream() { + private final java.io.InputStream delegate = + new ByteArrayInputStream( + "{\"encodedLocations\": \"encoded\", \"locations\": [\"loc\"]}".getBytes()); + private boolean blocked = false; + + @Override + public int read() throws java.io.IOException { + if (!blocked) { + try { + latch.await(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + blocked = true; + } + return delegate.read(); + } + }; + + MockHttpTransport transport = + new MockHttpTransport.Builder() + .setLowLevelHttpResponse( + new MockLowLevelHttpResponse() + .setContent(blockingStream) + .setContentType("application/json")) + .build(); + HttpTransportFactory transportFactory = () -> transport; + + RegionalAccessBoundaryManager[] managers = new RegionalAccessBoundaryManager[totalCapacity]; + for (int i = 0; i < totalCapacity; i++) { + managers[i] = + new RegionalAccessBoundaryManager( + testClock, + RegionalAccessBoundaryManager.DEFAULT_MAX_RETRY_ELAPSED_TIME_MILLIS, + testExecutor); + managers[i].triggerAsyncRefresh(transportFactory, provider, token); + } + + RegionalAccessBoundaryManager extraManager = + new RegionalAccessBoundaryManager( + testClock, + RegionalAccessBoundaryManager.DEFAULT_MAX_RETRY_ELAPSED_TIME_MILLIS, + testExecutor); + assertFalse(extraManager.isCooldownActive()); + + extraManager.triggerAsyncRefresh(transportFactory, provider, token); + + assertFalse( + extraManager.isCooldownActive(), + "106th task should NOT have entered cooldown on scheduling failure"); + + latch.countDown(); + testExecutor.shutdownNow(); + } + private static class TestClock implements Clock { private final AtomicLong currentTime = new AtomicLong(System.currentTimeMillis()); @@ -217,4 +320,18 @@ public void set(long millis) { currentTime.set(millis); } } + + private static class TrackingMockLowLevelHttpResponse extends MockLowLevelHttpResponse { + private boolean disconnected = false; + + @Override + public void disconnect() throws IOException { + super.disconnect(); + disconnected = true; + } + + public boolean isDisconnected() { + return disconnected; + } + } } diff --git a/google-auth-library-java/oauth2_http/javatests/com/google/auth/oauth2/ServiceAccountCredentialsTest.java b/google-auth-library-java/oauth2_http/javatests/com/google/auth/oauth2/ServiceAccountCredentialsTest.java index 1ac38f957c6e..e9bf7c0e7d6a 100644 --- a/google-auth-library-java/oauth2_http/javatests/com/google/auth/oauth2/ServiceAccountCredentialsTest.java +++ b/google-auth-library-java/oauth2_http/javatests/com/google/auth/oauth2/ServiceAccountCredentialsTest.java @@ -32,6 +32,7 @@ package com.google.auth.oauth2; import static com.google.auth.oauth2.RegionalAccessBoundary.X_ALLOWED_LOCATIONS_HEADER_KEY; +import static com.google.auth.oauth2.TestUtils.createDummyRab; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -160,9 +161,7 @@ static ServiceAccountCredentials.Builder createDefaultBuilder() throws IOExcepti void setUp() {} @org.junit.jupiter.api.AfterEach - void tearDown() { - RegionalAccessBoundary.setEnvironmentProviderForTest(null); - } + void tearDown() {} @Test void setLifetime() throws IOException { @@ -368,6 +367,7 @@ void createAssertionForIdToken_incorrect() throws IOException { @Test void createdScoped_withAud_noUniverse_jwtWithScopesDisabled_accessToken() throws IOException { GoogleCredentials credentials = createDefaultBuilderWithToken(ACCESS_TOKEN).build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); // No aud, no scopes gives an exception. IOException exception = @@ -377,6 +377,8 @@ void createdScoped_withAud_noUniverse_jwtWithScopesDisabled_accessToken() throws "expected to fail with exception"); GoogleCredentials scopedCredentials = credentials.createScoped(SCOPES); + scopedCredentials.regionalAccessBoundaryManager.setCachedRAB( + createDummyRab(scopedCredentials.clock)); assertEquals(false, credentials.isExplicitUniverseDomain()); assertEquals(Credentials.GOOGLE_DEFAULT_UNIVERSE, credentials.getUniverseDomain()); Map> metadata = scopedCredentials.getRequestMetadata(CALL_URI); @@ -387,17 +389,22 @@ void createdScoped_withAud_noUniverse_jwtWithScopesDisabled_accessToken() throws void createdScoped_withUniverse_selfSignedJwt() throws IOException { ServiceAccountCredentials credentials = createDefaultBuilder().setUniverseDomain("foo.bar").build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); IOException exception = assertThrows(IOException.class, () -> credentials.getRequestMetadata(null)); assertTrue( exception.getMessage().contains("Scopes and uri are not configured for service account")); GoogleCredentials scopedCredentials = credentials.createScoped("dummy.scope"); + scopedCredentials.regionalAccessBoundaryManager.setCachedRAB( + createDummyRab(scopedCredentials.clock)); Map> metadata = scopedCredentials.getRequestMetadata(null); verifyJwtAccess(metadata, "dummy.scope"); // Recreate to avoid jwt caching. scopedCredentials = credentials.createScoped("dummy.scope2"); + scopedCredentials.regionalAccessBoundaryManager.setCachedRAB( + createDummyRab(scopedCredentials.clock)); assertEquals(true, scopedCredentials.isExplicitUniverseDomain()); assertEquals("foo.bar", scopedCredentials.getUniverseDomain()); metadata = scopedCredentials.getRequestMetadata(CALL_URI); @@ -407,6 +414,8 @@ void createdScoped_withUniverse_selfSignedJwt() throws IOException { scopedCredentials = credentials.createScoped( Collections.emptyList(), Arrays.asList("dummy.default.scope")); + scopedCredentials.regionalAccessBoundaryManager.setCachedRAB( + createDummyRab(scopedCredentials.clock)); metadata = scopedCredentials.getRequestMetadata(null); verifyJwtAccess(metadata, "dummy.default.scope"); @@ -414,6 +423,8 @@ void createdScoped_withUniverse_selfSignedJwt() throws IOException { scopedCredentials = credentials.createScoped( Collections.emptyList(), Arrays.asList("dummy.default.scope2")); + scopedCredentials.regionalAccessBoundaryManager.setCachedRAB( + createDummyRab(scopedCredentials.clock)); metadata = scopedCredentials.getRequestMetadata(CALL_URI); verifyJwtAccess(metadata, "dummy.default.scope2"); } @@ -534,6 +545,7 @@ void fromJSON_hasAccessToken() throws IOException { GenericJson json = writeServiceAccountJson(PROJECT_ID, null, null); GoogleCredentials credentials = ServiceAccountCredentials.fromJson(json, transportFactory); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); credentials = credentials.createScoped(SCOPES); Map> metadata = credentials.getRequestMetadata(CALL_URI); @@ -547,6 +559,7 @@ void fromJSON_withUniverse_selfSignedJwt() throws IOException { GenericJson json = writeServiceAccountJson(PROJECT_ID, null, "foo.bar"); GoogleCredentials credentials = ServiceAccountCredentials.fromJson(json, transportFactory); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); credentials = credentials.createScoped(SCOPES); Map> metadata = credentials.getRequestMetadata(null); @@ -571,6 +584,7 @@ void fromJson_hasQuotaProjectId() throws IOException { transportFactory.transport.addServiceAccount(CLIENT_EMAIL, ACCESS_TOKEN); GenericJson json = writeServiceAccountJson(PROJECT_ID, QUOTA_PROJECT, null); GoogleCredentials credentials = ServiceAccountCredentials.fromJson(json, transportFactory); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); credentials = credentials.createScoped(SCOPES); Map> metadata = credentials.getRequestMetadata(CALL_URI); @@ -585,6 +599,7 @@ void fromJson_hasQuotaProjectId() throws IOException { void getRequestMetadata_hasAccessToken() throws IOException { GoogleCredentials credentials = createDefaultBuilderWithToken(ACCESS_TOKEN).setScopes(SCOPES).build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); Map> metadata = credentials.getRequestMetadata(CALL_URI); TestUtils.assertContainsBearerToken(metadata, ACCESS_TOKEN); } @@ -595,12 +610,13 @@ void getRequestMetadata_customTokenServer_hasAccessToken() throws IOException { MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); transportFactory.transport.addServiceAccount(CLIENT_EMAIL, ACCESS_TOKEN); transportFactory.transport.setTokenServerUri(tokenServerUri); - OAuth2Credentials credentials = + ServiceAccountCredentials credentials = createDefaultBuilder() .setScopes(SCOPES) .setHttpTransportFactory(transportFactory) .setTokenServerUri(tokenServerUri) .build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); Map> metadata = credentials.getRequestMetadata(CALL_URI); TestUtils.assertContainsBearerToken(metadata, ACCESS_TOKEN); @@ -625,6 +641,7 @@ void refreshAccessToken_refreshesToken() throws IOException { MockTokenServerTransport transport = transportFactory.transport; ServiceAccountCredentials credentials = createDefaultBuilder().setScopes(SCOPES).setHttpTransportFactory(transportFactory).build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); transport.addServiceAccount(CLIENT_EMAIL, accessToken1); TestUtils.assertContainsBearerToken(credentials.getRequestMetadata(CALL_URI), accessToken1); @@ -640,6 +657,7 @@ void refreshAccessToken_tokenExpiry() throws IOException { transport.addServiceAccount(CLIENT_EMAIL, ACCESS_TOKEN); ServiceAccountCredentials credentials = createDefaultBuilder().setScopes(SCOPES).setHttpTransportFactory(transportFactory).build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); credentials.clock = new FixedClock(0L); AccessToken accessToken = credentials.refreshAccessToken(); @@ -661,6 +679,7 @@ void refreshAccessToken_IOException_Retry() throws IOException { MockTokenServerTransport transport = transportFactory.transport; ServiceAccountCredentials credentials = createDefaultBuilder().setScopes(SCOPES).setHttpTransportFactory(transportFactory).build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); transport.addServiceAccount(CLIENT_EMAIL, accessToken1); TestUtils.assertContainsBearerToken(credentials.getRequestMetadata(CALL_URI), accessToken1); @@ -679,6 +698,7 @@ void refreshAccessToken_retriesServerErrors() throws IOException { MockTokenServerTransport transport = transportFactory.transport; ServiceAccountCredentials credentials = createDefaultBuilder().setScopes(SCOPES).setHttpTransportFactory(transportFactory).build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); transport.addServiceAccount(CLIENT_EMAIL, accessToken1); TestUtils.assertContainsBearerToken(credentials.getRequestMetadata(CALL_URI), accessToken1); @@ -699,6 +719,7 @@ void refreshAccessToken_retriesTimeoutAndThrottled() throws IOException { MockTokenServerTransport transport = transportFactory.transport; ServiceAccountCredentials credentials = createDefaultBuilder().setScopes(SCOPES).setHttpTransportFactory(transportFactory).build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); transport.addServiceAccount(CLIENT_EMAIL, accessToken1); TestUtils.assertContainsBearerToken(credentials.getRequestMetadata(CALL_URI), accessToken1); @@ -723,6 +744,7 @@ void refreshAccessToken_defaultRetriesDisabled() throws IOException { .setHttpTransportFactory(transportFactory) .build() .createWithCustomRetryStrategy(false); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); transport.addServiceAccount(CLIENT_EMAIL, accessToken1); TestUtils.assertContainsBearerToken(credentials.getRequestMetadata(CALL_URI), accessToken1); @@ -744,6 +766,7 @@ void refreshAccessToken_maxRetries_maxDelay() throws IOException { MockTokenServerTransport transport = transportFactory.transport; ServiceAccountCredentials credentials = createDefaultBuilder().setScopes(SCOPES).setHttpTransportFactory(transportFactory).build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); transport.addServiceAccount(CLIENT_EMAIL, ACCESS_TOKEN); TestUtils.assertContainsBearerToken(credentials.getRequestMetadata(CALL_URI), ACCESS_TOKEN); @@ -773,6 +796,7 @@ void refreshAccessToken_RequestFailure_retried() throws IOException { MockTokenServerTransport transport = transportFactory.transport; ServiceAccountCredentials credentials = createDefaultBuilder().setScopes(SCOPES).setHttpTransportFactory(transportFactory).build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); transport.addServiceAccount(CLIENT_EMAIL, ACCESS_TOKEN); TestUtils.assertContainsBearerToken(credentials.getRequestMetadata(CALL_URI), ACCESS_TOKEN); @@ -804,6 +828,7 @@ void refreshAccessToken_4xx_5xx_NonRetryableFails() throws IOException { MockTokenServerTransport transport = transportFactory.transport; ServiceAccountCredentials credentials = createDefaultBuilder().setScopes(SCOPES).setHttpTransportFactory(transportFactory).build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); transport.addServiceAccount(CLIENT_EMAIL, accessToken1); TestUtils.assertContainsBearerToken(credentials.getRequestMetadata(CALL_URI), accessToken1); @@ -829,6 +854,7 @@ void idTokenWithAudience_oauthFlow_targetAudienceMatchesAudClaim() throws IOExce MockTokenServerTransport transport = transportFactory.transport; ServiceAccountCredentials credentials = createDefaultBuilder().setScopes(SCOPES).setHttpTransportFactory(transportFactory).build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); transport.addServiceAccount(CLIENT_EMAIL, accessToken1); TestUtils.assertContainsBearerToken(credentials.getRequestMetadata(CALL_URI), accessToken1); @@ -866,6 +892,7 @@ void idTokenWithAudience_oauthFlow_targetAudienceDoesNotMatchAudClaim() throws I MockTokenServerTransport transport = transportFactory.transport; ServiceAccountCredentials credentials = createDefaultBuilder().setScopes(SCOPES).setHttpTransportFactory(transportFactory).build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); transport.addServiceAccount(CLIENT_EMAIL, accessToken1); TestUtils.assertContainsBearerToken(credentials.getRequestMetadata(CALL_URI), accessToken1); @@ -898,6 +925,7 @@ void idTokenWithAudience_iamFlow_targetAudienceMatchesAudClaim() throws IOExcept .setHttpTransportFactory(transportFactory) .setUniverseDomain(nonGDU) .build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); String targetAudience = "https://foo.bar"; IdTokenCredentials tokenCredential = @@ -929,6 +957,7 @@ void idTokenWithAudience_iamFlow_targetAudienceDoesNotMatchAudClaim() throws IOE .setHttpTransportFactory(transportFactory) .setUniverseDomain(nonGDU) .build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); String targetAudience = "differentAudience"; IdTokenCredentials tokenCredential = @@ -950,6 +979,7 @@ void idTokenWithAudience_oauthEndpoint_non2XXStatusCode() throws IOException { transportFactory.transport.setError(new IOException("404 Not Found")); ServiceAccountCredentials credentials = createDefaultBuilder().setScopes(SCOPES).setHttpTransportFactory(transportFactory).build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); String targetAudience = "audience"; IdTokenCredentials tokenCredential = @@ -978,6 +1008,7 @@ void idTokenWithAudience_iamEndpoint_non2XXStatusCode() throws IOException { .setHttpTransportFactory(transportFactory) .setUniverseDomain(universeDomain) .build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); String targetAudience = "audience"; IdTokenCredentials tokenCredential = @@ -1379,6 +1410,7 @@ void fromStream_providesToken() throws IOException { GoogleCredentials credentials = ServiceAccountCredentials.fromStream(serviceAccountStream, transportFactory); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); assertNotNull(credentials); credentials = credentials.createScoped(SCOPES); @@ -1421,6 +1453,7 @@ void getIdTokenWithAudience_badEmailError_issClaimTraced() throws IOException { transport.setError(new IOException("Invalid grant: Account not found")); ServiceAccountCredentials credentials = createDefaultBuilder().setScopes(SCOPES).setHttpTransportFactory(transportFactory).build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); String targetAudience = "https://bar"; IdTokenCredentials tokenCredential = @@ -1505,6 +1538,7 @@ void getRequestMetadata_setsQuotaProjectId() throws IOException { .setQuotaProjectId("my-quota-project-id") .setHttpTransportFactory(transportFactory) .build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); Map> metadata = credentials.getRequestMetadata(CALL_URI); assertTrue(metadata.containsKey("x-goog-user-project")); @@ -1531,6 +1565,7 @@ void getRequestMetadata_noQuotaProjectId() throws IOException { .setProjectId(PROJECT_ID) .setHttpTransportFactory(transportFactory) .build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); Map> metadata = credentials.getRequestMetadata(CALL_URI); assertFalse(metadata.containsKey("x-goog-user-project")); @@ -1554,6 +1589,7 @@ void getRequestMetadata_withCallback() throws IOException { .setQuotaProjectId("my-quota-project-id") .setHttpTransportFactory(transportFactory) .build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); final Map> plainMetadata = credentials.getRequestMetadata(); final AtomicBoolean success = new AtomicBoolean(false); @@ -1594,6 +1630,7 @@ void getRequestMetadata_withScopes_withUniverseDomain_SelfSignedJwt() throws IOE .setHttpTransportFactory(transportFactory) .setUniverseDomain("foo.bar") .build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); final Map> plainMetadata = credentials.getRequestMetadata(); final AtomicBoolean success = new AtomicBoolean(false); @@ -1630,6 +1667,7 @@ void getRequestMetadata_withScopes_selfSignedJWT() throws IOException { .setHttpTransportFactory(new MockTokenServerTransportFactory()) .setUseJwtAccessWithScope(true) .build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); Map> metadata = credentials.getRequestMetadata(CALL_URI); assertNotNull(((ServiceAccountCredentials) credentials).getSelfSignedJwtCredentialsWithScope()); @@ -1661,6 +1699,7 @@ void refreshAccessToken_withDomainDelegation_selfSignedJWT_disabled() throws IOE .setHttpTransportFactory(transportFactory) .setUseJwtAccessWithScope(true) .build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); transport.addServiceAccount(CLIENT_EMAIL, accessToken1); Map> metadata = credentials.getRequestMetadata(CALL_URI); @@ -1685,6 +1724,7 @@ void getRequestMetadata_withAudience_selfSignedJWT() throws IOException { .setProjectId(PROJECT_ID) .setHttpTransportFactory(new MockTokenServerTransportFactory()) .build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); Map> metadata = credentials.getRequestMetadata(CALL_URI); assertNull(((ServiceAccountCredentials) credentials).getSelfSignedJwtCredentialsWithScope()); @@ -1705,6 +1745,7 @@ void getRequestMetadata_withDefaultScopes_selfSignedJWT() throws IOException { .setHttpTransportFactory(new MockTokenServerTransportFactory()) .setUseJwtAccessWithScope(true) .build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); Map> metadata = credentials.getRequestMetadata(null); verifyJwtAccess(metadata, "dummy.scope"); @@ -1725,6 +1766,7 @@ void getRequestMetadataWithCallback_selfSignedJWT() throws IOException { .setUseJwtAccessWithScope(true) .setScopes(SCOPES) .build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); final AtomicBoolean success = new AtomicBoolean(false); credentials.getRequestMetadata( @@ -1762,6 +1804,7 @@ void createScopes_existingAccessTokenInvalidated() throws IOException { .setHttpTransportFactory(transportFactory) .setScopes(SCOPES) .build(); + credentials.regionalAccessBoundaryManager.setCachedRAB(createDummyRab(credentials.clock)); TestUtils.assertContainsBearerToken(credentials.getRequestMetadata(CALL_URI), ACCESS_TOKEN); // Calling createScoped() again will invalidate the existing access token and calling @@ -1773,9 +1816,7 @@ void createScopes_existingAccessTokenInvalidated() throws IOException { @Test public void refresh_regionalAccessBoundarySuccess() throws IOException, InterruptedException { - TestEnvironmentProvider environmentProvider = new TestEnvironmentProvider(); - RegionalAccessBoundary.setEnvironmentProviderForTest(environmentProvider); - environmentProvider.setEnv(RegionalAccessBoundary.ENABLE_EXPERIMENT_ENV_VAR, "1"); + // Mock regional access boundary response RegionalAccessBoundary regionalAccessBoundary = new RegionalAccessBoundary( @@ -1813,9 +1854,7 @@ public void refresh_regionalAccessBoundarySuccess() throws IOException, Interrup @Test public void refresh_regionalAccessBoundary_selfSignedJWT() throws IOException, InterruptedException { - TestEnvironmentProvider environmentProvider = new TestEnvironmentProvider(); - RegionalAccessBoundary.setEnvironmentProviderForTest(environmentProvider); - environmentProvider.setEnv(RegionalAccessBoundary.ENABLE_EXPERIMENT_ENV_VAR, "1"); + RegionalAccessBoundary regionalAccessBoundary = new RegionalAccessBoundary( TestUtils.REGIONAL_ACCESS_BOUNDARY_ENCODED_LOCATION, diff --git a/google-auth-library-java/oauth2_http/javatests/com/google/auth/oauth2/TestUtils.java b/google-auth-library-java/oauth2_http/javatests/com/google/auth/oauth2/TestUtils.java index 4efc138bbfa8..52652a71c458 100644 --- a/google-auth-library-java/oauth2_http/javatests/com/google/auth/oauth2/TestUtils.java +++ b/google-auth-library-java/oauth2_http/javatests/com/google/auth/oauth2/TestUtils.java @@ -69,4 +69,9 @@ static void validateMetricsHeader( } assertEquals(expectedMetricsValue, actualMetricsValue); } + + static RegionalAccessBoundary createDummyRab(com.google.api.client.util.Clock clock) { + return new RegionalAccessBoundary( + "dummy-locations", java.util.Arrays.asList("dummy-loc"), clock); + } }