diff --git a/oauth2_http/java/com/google/auth/oauth2/ExternalAccountAuthorizedUserCredentials.java b/oauth2_http/java/com/google/auth/oauth2/ExternalAccountAuthorizedUserCredentials.java index e67ddb89d..abb1da71c 100644 --- a/oauth2_http/java/com/google/auth/oauth2/ExternalAccountAuthorizedUserCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/ExternalAccountAuthorizedUserCredentials.java @@ -31,7 +31,9 @@ package com.google.auth.oauth2; +import static com.google.auth.oauth2.OAuth2Utils.IAM_CREDENTIALS_ALLOWED_LOCATIONS_URL_FORMAT_WORKFORCE_POOL; import static com.google.auth.oauth2.OAuth2Utils.JSON_FACTORY; +import static com.google.auth.oauth2.OAuth2Utils.WORKFORCE_AUDIENCE_PATTERN; import static com.google.common.base.Preconditions.checkNotNull; import com.google.api.client.http.GenericUrl; @@ -44,6 +46,7 @@ import com.google.api.client.json.JsonObjectParser; import com.google.api.client.util.GenericData; import com.google.api.client.util.Preconditions; +import com.google.api.core.InternalApi; import com.google.auth.http.HttpTransportFactory; import com.google.common.base.MoreObjects; import com.google.common.io.BaseEncoding; @@ -55,6 +58,7 @@ import java.util.Date; import java.util.Map; import java.util.Objects; +import java.util.regex.Matcher; import javax.annotation.Nullable; /** @@ -75,12 +79,12 @@ * } * */ -public class ExternalAccountAuthorizedUserCredentials extends GoogleCredentials { +public class ExternalAccountAuthorizedUserCredentials extends GoogleCredentials + implements TrustBoundaryProvider { private static final String PARSE_ERROR_PREFIX = "Error parsing token refresh response. "; private static final long serialVersionUID = -2181779590486283287L; - private final String transportFactoryClassName; private final String audience; private final String tokenUrl; @@ -210,10 +214,27 @@ public AccessToken refreshAccessToken() throws IOException { this.refreshToken = refreshToken; } - return AccessToken.newBuilder() - .setExpirationTime(expiresAtMilliseconds) - .setTokenValue(accessToken) - .build(); + AccessToken newAccessToken = + AccessToken.newBuilder() + .setExpirationTime(expiresAtMilliseconds) + .setTokenValue(accessToken) + .build(); + + refreshTrustBoundary(newAccessToken, transportFactory); + return newAccessToken; + } + + @InternalApi + @Override + public String getTrustBoundaryUrl() throws IOException { + Matcher matcher = WORKFORCE_AUDIENCE_PATTERN.matcher(getAudience()); + if (!matcher.matches()) { + throw new IllegalStateException( + "The provided audience is not in the correct format for a workforce pool."); + } + String poolId = matcher.group("pool"); + return String.format( + IAM_CREDENTIALS_ALLOWED_LOCATIONS_URL_FORMAT_WORKFORCE_POOL, getUniverseDomain(), poolId); } @Nullable diff --git a/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java b/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java index c4268d167..33cbb592c 100644 --- a/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java @@ -31,12 +31,15 @@ package com.google.auth.oauth2; +import static com.google.auth.oauth2.OAuth2Utils.WORKFORCE_AUDIENCE_PATTERN; +import static com.google.auth.oauth2.OAuth2Utils.WORKLOAD_AUDIENCE_PATTERN; import static com.google.common.base.Preconditions.checkNotNull; import com.google.api.client.http.HttpHeaders; import com.google.api.client.json.GenericJson; import com.google.api.client.json.JsonObjectParser; import com.google.api.client.util.Data; +import com.google.api.core.InternalApi; import com.google.auth.RequestMetadataCallback; import com.google.auth.http.HttpTransportFactory; import com.google.common.base.MoreObjects; @@ -55,6 +58,7 @@ import java.util.Locale; import java.util.Map; import java.util.concurrent.Executor; +import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.annotation.Nullable; @@ -64,7 +68,8 @@ *

Handles initializing external credentials, calls to the Security Token Service, and service * account impersonation. */ -public abstract class ExternalAccountCredentials extends GoogleCredentials { +public abstract class ExternalAccountCredentials extends GoogleCredentials + implements TrustBoundaryProvider { private static final long serialVersionUID = 8049126194174465023L; @@ -527,7 +532,11 @@ protected AccessToken exchangeExternalCredentialForAccessToken( this.impersonatedCredentials = this.buildImpersonatedCredentials(); } if (this.impersonatedCredentials != null) { - return this.impersonatedCredentials.refreshAccessToken(); + AccessToken accessToken = this.impersonatedCredentials.refreshAccessToken(); + // After the impersonated credential refreshes, its trust boundary is + // also refreshed. That is the trust boundary we will use. + this.trustBoundary = this.impersonatedCredentials.getTrustBoundary(); + return accessToken; } StsRequestHandler.Builder requestHandler = @@ -556,7 +565,9 @@ protected AccessToken exchangeExternalCredentialForAccessToken( } StsTokenExchangeResponse response = requestHandler.build().exchangeToken(); - return response.getAccessToken(); + AccessToken accessToken = response.getAccessToken(); + refreshTrustBoundary(accessToken, transportFactory); + return accessToken; } /** @@ -613,6 +624,33 @@ public String getServiceAccountEmail() { return ImpersonatedCredentials.extractTargetPrincipal(serviceAccountImpersonationUrl); } + @InternalApi + @Override + public String getTrustBoundaryUrl() { + Matcher workforceMatcher = WORKFORCE_AUDIENCE_PATTERN.matcher(getAudience()); + if (workforceMatcher.matches()) { + String poolId = workforceMatcher.group("pool"); + return String.format( + OAuth2Utils.IAM_CREDENTIALS_ALLOWED_LOCATIONS_URL_FORMAT_WORKFORCE_POOL, + getUniverseDomain(), + poolId); + } + + Matcher workloadMatcher = WORKLOAD_AUDIENCE_PATTERN.matcher(getAudience()); + if (workloadMatcher.matches()) { + String projectNumber = workloadMatcher.group("project"); + String poolId = workloadMatcher.group("pool"); + return String.format( + OAuth2Utils.IAM_CREDENTIALS_ALLOWED_LOCATIONS_URL_FORMAT_WORKLOAD_POOL, + getUniverseDomain(), + projectNumber, + poolId); + } + + throw new IllegalStateException( + "The provided audience is not in a valid format for either a workload identity pool or a workforce pool."); + } + @Nullable public String getClientId() { return clientId; diff --git a/oauth2_http/java/com/google/auth/oauth2/GoogleCredentials.java b/oauth2_http/java/com/google/auth/oauth2/GoogleCredentials.java index 59afc00d5..4f3118bc8 100644 --- a/oauth2_http/java/com/google/auth/oauth2/GoogleCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/GoogleCredentials.java @@ -108,7 +108,7 @@ String getFileType() { private final String universeDomain; private final boolean isExplicitUniverseDomain; - private TrustBoundary trustBoundary; + TrustBoundary trustBoundary; protected final String quotaProjectId; @@ -339,6 +339,10 @@ TrustBoundary getTrustBoundary() { return trustBoundary; } + protected void setTrustBoundary(TrustBoundary trustBoundary) { + this.trustBoundary = trustBoundary; + } + /** * Refreshes the trust boundary by making a call to the trust boundary URL. * diff --git a/oauth2_http/java/com/google/auth/oauth2/OAuth2Utils.java b/oauth2_http/java/com/google/auth/oauth2/OAuth2Utils.java index b69d636c8..72cab4f01 100644 --- a/oauth2_http/java/com/google/auth/oauth2/OAuth2Utils.java +++ b/oauth2_http/java/com/google/auth/oauth2/OAuth2Utils.java @@ -68,6 +68,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.regex.Pattern; /** * Internal utilities for the com.google.auth.oauth2 namespace. @@ -93,9 +94,6 @@ public class OAuth2Utils { static final URI TOKEN_SERVER_URI = URI.create("https://oauth2.googleapis.com/token"); static final URI TOKEN_REVOKE_URI = URI.create("https://oauth2.googleapis.com/revoke"); - - static final String IAM_CREDENTIALS_ALLOWED_LOCATIONS_URL_FORMAT_SERVICE_ACCOUNT = - "https://iamcredentials.%s/v1/projects/-/serviceAccounts/%s/allowedLocations"; static final URI USER_AUTH_URI = URI.create("https://accounts.google.com/o/oauth2/auth"); public static final String CLOUD_PLATFORM_SCOPE = @@ -120,6 +118,22 @@ public class OAuth2Utils { static final double RETRY_MULTIPLIER = 2; static final int DEFAULT_NUMBER_OF_RETRIES = 3; + static final Pattern WORKFORCE_AUDIENCE_PATTERN = + Pattern.compile( + "^//iam.googleapis.com/locations/(?[^/]+)/workforcePools/(?[^/]+)/providers/(?[^/]+)$"); + static final Pattern WORKLOAD_AUDIENCE_PATTERN = + Pattern.compile( + "^//iam.googleapis.com/projects/(?[^/]+)/locations/(?[^/]+)/workloadIdentityPools/(?[^/]+)/providers/(?[^/]+)$"); + + static final String IAM_CREDENTIALS_ALLOWED_LOCATIONS_URL_FORMAT_SERVICE_ACCOUNT = + "https://iamcredentials.%s/v1/projects/-/serviceAccounts/%s/allowedLocations"; + + static final String IAM_CREDENTIALS_ALLOWED_LOCATIONS_URL_FORMAT_WORKFORCE_POOL = + "https://iamcredentials.%s/v1/locations/global/workforcePools/%s/allowedLocations"; + + static final String IAM_CREDENTIALS_ALLOWED_LOCATIONS_URL_FORMAT_WORKLOAD_POOL = + "https://iamcredentials.%s/v1/projects/%s/locations/global/workloadIdentityPools/%s/allowedLocations"; + // Includes expected server errors from Google token endpoint // Other 5xx codes are either not used or retries are unlikely to succeed public static final Set TOKEN_ENDPOINT_RETRYABLE_STATUS_CODES = diff --git a/oauth2_http/java/com/google/auth/oauth2/TrustBoundary.java b/oauth2_http/java/com/google/auth/oauth2/TrustBoundary.java index 672941e58..ebbb110de 100644 --- a/oauth2_http/java/com/google/auth/oauth2/TrustBoundary.java +++ b/oauth2_http/java/com/google/auth/oauth2/TrustBoundary.java @@ -184,9 +184,7 @@ static TrustBoundary refresh( // Add the cached trust boundary header, if available. if (cachedTrustBoundary != null) { - String headerValue = - cachedTrustBoundary.isNoOp() ? "" : cachedTrustBoundary.getEncodedLocations(); - request.getHeaders().set(TRUST_BOUNDARY_KEY, headerValue); + request.getHeaders().set(TRUST_BOUNDARY_KEY, cachedTrustBoundary.getEncodedLocations()); } // Add retry logic diff --git a/oauth2_http/javatests/com/google/auth/TestUtils.java b/oauth2_http/javatests/com/google/auth/TestUtils.java index 99d601da8..b68b49dc5 100644 --- a/oauth2_http/javatests/com/google/auth/TestUtils.java +++ b/oauth2_http/javatests/com/google/auth/TestUtils.java @@ -42,6 +42,7 @@ import com.google.api.client.json.gson.GsonFactory; import com.google.auth.http.AuthHttpConstants; import com.google.common.base.Splitter; +import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -55,6 +56,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.TimeZone; import javax.annotation.Nullable; /** Utilities for test code under com.google.auth. */ @@ -64,6 +66,9 @@ public class TestUtils { URI.create("https://auth.cloud.google/authorize"); public static final URI WORKFORCE_IDENTITY_FEDERATION_TOKEN_SERVER_URI = URI.create("https://sts.googleapis.com/v1/oauthtoken"); + public static final String TRUST_BOUNDARY_ENCODED_LOCATION = "0x800000"; + public static final List TRUST_BOUNDARY_LOCATIONS = + ImmutableList.of("us-central1", "us-central2"); private static final JsonFactory JSON_FACTORY = GsonFactory.getDefaultInstance(); @@ -147,7 +152,9 @@ public static String getDefaultExpireTime() { Calendar calendar = Calendar.getInstance(); calendar.setTime(new Date()); calendar.add(Calendar.SECOND, 300); - return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").format(calendar.getTime()); + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); + dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); + return dateFormat.format(calendar.getTime()); } private TestUtils() {} diff --git a/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java index e8b401063..795a1207a 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java @@ -1399,4 +1399,34 @@ public AwsSecurityCredentials getCredentials(ExternalAccountSupplierContext cont return credentials; } } + + @Test + public void testRefresh_trustBoundarySuccess() throws IOException { + TestEnvironmentProvider environmentProvider = new TestEnvironmentProvider(); + TrustBoundary.setEnvironmentProviderForTest(environmentProvider); + environmentProvider.setEnv("GOOGLE_AUTH_TRUST_BOUNDARY_ENABLE_EXPERIMENT", "1"); + + MockExternalAccountCredentialsTransportFactory transportFactory = + new MockExternalAccountCredentialsTransportFactory(); + + AwsSecurityCredentialsSupplier supplier = + new TestAwsSecurityCredentialsSupplier("test", programmaticAwsCreds, null, null); + + AwsCredentials awsCredential = + AwsCredentials.newBuilder() + .setAwsSecurityCredentialsSupplier(supplier) + .setHttpTransportFactory(transportFactory) + .setAudience( + "//iam.googleapis.com/projects/12345/locations/global/workloadIdentityPools/pool/providers/provider") + .setTokenUrl(STS_URL) + .setSubjectTokenType("subjectTokenType") + .build(); + + awsCredential.refreshAccessToken(); + + TrustBoundary trustBoundary = awsCredential.getTrustBoundary(); + assertNotNull(trustBoundary); + assertEquals(TestUtils.TRUST_BOUNDARY_ENCODED_LOCATION, trustBoundary.getEncodedLocations()); + TrustBoundary.setEnvironmentProviderForTest(null); + } } diff --git a/oauth2_http/javatests/com/google/auth/oauth2/ComputeEngineCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/ComputeEngineCredentialsTest.java index 275545db2..e13c9ce93 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/ComputeEngineCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/ComputeEngineCredentialsTest.java @@ -33,6 +33,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.TrustBoundary.TRUST_BOUNDARY_KEY; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -1159,7 +1160,8 @@ public void refresh_trustBoundarySuccess() throws IOException { String defaultAccountEmail = "default@email.com"; MockMetadataServerTransportFactory transportFactory = new MockMetadataServerTransportFactory(); TrustBoundary trustBoundary = - new TrustBoundary("0x80000", Collections.singletonList("us-central1")); + new TrustBoundary( + TestUtils.TRUST_BOUNDARY_ENCODED_LOCATION, TestUtils.TRUST_BOUNDARY_LOCATIONS); transportFactory.transport.setTrustBoundary(trustBoundary); transportFactory.transport.setServiceAccountEmail(defaultAccountEmail); @@ -1167,7 +1169,8 @@ public void refresh_trustBoundarySuccess() throws IOException { ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); Map> headers = credentials.getRequestMetadata(); - assertEquals(headers.get("x-allowed-locations"), Arrays.asList("0x80000")); + assertEquals( + headers.get(TRUST_BOUNDARY_KEY), Arrays.asList(TestUtils.TRUST_BOUNDARY_ENCODED_LOCATION)); } @Test diff --git a/oauth2_http/javatests/com/google/auth/oauth2/ExternalAccountAuthorizedUserCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/ExternalAccountAuthorizedUserCredentialsTest.java index 740cabba5..04673a260 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/ExternalAccountAuthorizedUserCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/ExternalAccountAuthorizedUserCredentialsTest.java @@ -35,8 +35,10 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -62,6 +64,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -132,6 +135,11 @@ public void setup() { transportFactory = new MockExternalAccountAuthorizedUserCredentialsTransportFactory(); } + @After + public void tearDown() { + TrustBoundary.setEnvironmentProviderForTest(null); + } + @Test public void builder_allFields() throws IOException { ExternalAccountAuthorizedUserCredentials credentials = @@ -1216,6 +1224,55 @@ public void toString_expectedFormat() { assertEquals(expectedToString, credentials.toString()); } + @Test + public void testRefresh_trustBoundarySuccess() throws IOException { + TestEnvironmentProvider environmentProvider = new TestEnvironmentProvider(); + TrustBoundary.setEnvironmentProviderForTest(environmentProvider); + environmentProvider.setEnv("GOOGLE_AUTH_TRUST_BOUNDARY_ENABLE_EXPERIMENT", "1"); + + ExternalAccountAuthorizedUserCredentials credentials = + ExternalAccountAuthorizedUserCredentials.newBuilder() + .setHttpTransportFactory(transportFactory) + .setAudience(AUDIENCE) + .setClientId(CLIENT_ID) + .setClientSecret(CLIENT_SECRET) + .setRefreshToken(REFRESH_TOKEN) + .setTokenUrl(TOKEN_URL) + .build(); + + credentials.refresh(); + TrustBoundary trustBoundary = credentials.getTrustBoundary(); + assertNotNull(trustBoundary); + assertEquals(TestUtils.TRUST_BOUNDARY_ENCODED_LOCATION, trustBoundary.getEncodedLocations()); + } + + @Test + public void testRefresh_trustBoundaryFails_incorrectAudience() { + TestEnvironmentProvider environmentProvider = new TestEnvironmentProvider(); + TrustBoundary.setEnvironmentProviderForTest(environmentProvider); + environmentProvider.setEnv("GOOGLE_AUTH_TRUST_BOUNDARY_ENABLE_EXPERIMENT", "1"); + + ExternalAccountAuthorizedUserCredentials credentials = + ExternalAccountAuthorizedUserCredentials.newBuilder() + .setHttpTransportFactory(transportFactory) + .setAudience("audience") + .setClientId(CLIENT_ID) + .setClientSecret(CLIENT_SECRET) + .setRefreshToken(REFRESH_TOKEN) + .setTokenUrl(TOKEN_URL) + .build(); + + IllegalStateException exception = + assertThrows( + IllegalStateException.class, + () -> { + credentials.refresh(); + }); + assertEquals( + "The provided audience is not in the correct format for a workforce pool.", + exception.getMessage()); + } + @Test public void serialize() throws IOException, ClassNotFoundException { ExternalAccountAuthorizedUserCredentials credentials = diff --git a/oauth2_http/javatests/com/google/auth/oauth2/ExternalAccountCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/ExternalAccountCredentialsTest.java index 32009f755..14fe4e90b 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/ExternalAccountCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/ExternalAccountCredentialsTest.java @@ -36,6 +36,7 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -56,6 +57,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -93,6 +95,11 @@ public void setup() { transportFactory = new MockExternalAccountCredentialsTransportFactory(); } + @After + public void tearDown() { + TrustBoundary.setEnvironmentProviderForTest(null); + } + @Test public void fromStream_identityPoolCredentials() throws IOException { GenericJson json = buildJsonIdentityPoolCredential(); @@ -1248,6 +1255,194 @@ public void validateServiceAccountImpersonationUrls_invalidUrls() { } } + @Test + public void getTrustBoundaryUrl_workload() throws IOException { + String audience = + "//iam.googleapis.com/projects/12345/locations/global/workloadIdentityPools/my-pool/providers/my-provider"; + ExternalAccountCredentials credentials = + TestExternalAccountCredentials.newBuilder() + .setAudience(audience) + .setSubjectTokenType("subject_token_type") + .setCredentialSource(new TestCredentialSource(FILE_CREDENTIAL_SOURCE_MAP)) + .build(); + + String expectedUrl = + "https://iamcredentials.googleapis.com/v1/projects/12345/locations/global/workloadIdentityPools/my-pool/allowedLocations"; + assertEquals(expectedUrl, credentials.getTrustBoundaryUrl()); + } + + @Test + public void getTrustBoundaryUrl_workforce() throws IOException { + String audience = + "//iam.googleapis.com/locations/global/workforcePools/my-pool/providers/my-provider"; + ExternalAccountCredentials credentials = + TestExternalAccountCredentials.newBuilder() + .setAudience(audience) + .setWorkforcePoolUserProject("12345") + .setSubjectTokenType("subject_token_type") + .setCredentialSource(new TestCredentialSource(FILE_CREDENTIAL_SOURCE_MAP)) + .build(); + + String expectedUrl = + "https://iamcredentials.googleapis.com/v1/locations/global/workforcePools/my-pool/allowedLocations"; + assertEquals(expectedUrl, credentials.getTrustBoundaryUrl()); + } + + @Test + public void getTrustBoundaryUrl_invalidAudience_throws() { + ExternalAccountCredentials credentials = + TestExternalAccountCredentials.newBuilder() + .setAudience("invalid-audience") + .setSubjectTokenType("subject_token_type") + .setCredentialSource(new TestCredentialSource(FILE_CREDENTIAL_SOURCE_MAP)) + .build(); + + IllegalStateException exception = + assertThrows( + IllegalStateException.class, + () -> { + credentials.getTrustBoundaryUrl(); + }); + + assertEquals( + "The provided audience is not in a valid format for either a workload identity pool or a workforce pool.", + exception.getMessage()); + } + + @Test + public void refresh_workload_trustBoundarySuccess() throws IOException { + String audience = + "//iam.googleapis.com/projects/12345/locations/global/workloadIdentityPools/my-pool/providers/my-provider"; + + TestEnvironmentProvider environmentProvider = new TestEnvironmentProvider(); + TrustBoundary.setEnvironmentProviderForTest(environmentProvider); + environmentProvider.setEnv("GOOGLE_AUTH_TRUST_BOUNDARY_ENABLE_EXPERIMENT", "1"); + + ExternalAccountCredentials credentials = + new IdentityPoolCredentials( + IdentityPoolCredentials.newBuilder() + .setHttpTransportFactory(transportFactory) + .setAudience(audience) + .setSubjectTokenType("subject_token_type") + .setTokenUrl(STS_URL) + .setCredentialSource(new TestCredentialSource(FILE_CREDENTIAL_SOURCE_MAP)) + .setEnvironmentProvider(environmentProvider)) { + @Override + public String retrieveSubjectToken() throws IOException { + // This override isolates the test from the filesystem. + return "dummy-subject-token"; + } + }; + credentials.refresh(); + + TrustBoundary trustBoundary = credentials.getTrustBoundary(); + assertNotNull(trustBoundary); + assertEquals(TestUtils.TRUST_BOUNDARY_ENCODED_LOCATION, trustBoundary.getEncodedLocations()); + } + + @Test + public void refresh_workforce_trustBoundarySuccess() throws IOException { + String audience = + "//iam.googleapis.com/locations/global/workforcePools/my-pool/providers/my-provider"; + + TestEnvironmentProvider environmentProvider = new TestEnvironmentProvider(); + TrustBoundary.setEnvironmentProviderForTest(environmentProvider); + environmentProvider.setEnv("GOOGLE_AUTH_TRUST_BOUNDARY_ENABLE_EXPERIMENT", "1"); + + ExternalAccountCredentials credentials = + new IdentityPoolCredentials( + IdentityPoolCredentials.newBuilder() + .setHttpTransportFactory(transportFactory) + .setAudience(audience) + .setWorkforcePoolUserProject("12345") + .setSubjectTokenType("subject_token_type") + .setTokenUrl(STS_URL) + .setCredentialSource(new TestCredentialSource(FILE_CREDENTIAL_SOURCE_MAP)) + .setEnvironmentProvider(environmentProvider)) { + @Override + public String retrieveSubjectToken() throws IOException { + return "dummy-subject-token"; + } + }; + + credentials.refresh(); + + TrustBoundary trustBoundary = credentials.getTrustBoundary(); + assertNotNull(trustBoundary); + assertEquals(TestUtils.TRUST_BOUNDARY_ENCODED_LOCATION, trustBoundary.getEncodedLocations()); + } + + @Test + public void refresh_impersonated_workload_trustBoundarySuccess() throws IOException { + String audience = + "//iam.googleapis.com/projects/12345/locations/global/workloadIdentityPools/my-pool/providers/my-provider"; + TestEnvironmentProvider environmentProvider = new TestEnvironmentProvider(); + TrustBoundary.setEnvironmentProviderForTest(environmentProvider); + environmentProvider.setEnv("GOOGLE_AUTH_TRUST_BOUNDARY_ENABLE_EXPERIMENT", "1"); + + transportFactory.transport.setExpireTime(TestUtils.getDefaultExpireTime()); + + // Use a URL-based source that the mock transport can handle, to avoid file IO. + Map urlCredentialSourceMap = new HashMap<>(); + urlCredentialSourceMap.put("url", "https://www.metadata.google.com"); + Map headers = new HashMap<>(); + headers.put("Metadata-Flavor", "Google"); + urlCredentialSourceMap.put("headers", headers); + + ExternalAccountCredentials credentials = + IdentityPoolCredentials.newBuilder() + .setHttpTransportFactory(transportFactory) + .setAudience(audience) + .setSubjectTokenType("subject_token_type") + .setTokenUrl(STS_URL) + .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) + .setCredentialSource(new IdentityPoolCredentialSource(urlCredentialSourceMap)) + .setEnvironmentProvider(environmentProvider) + .build(); + + credentials.refresh(); + + TrustBoundary trustBoundary = credentials.getTrustBoundary(); + assertNotNull(trustBoundary); + assertEquals(TestUtils.TRUST_BOUNDARY_ENCODED_LOCATION, trustBoundary.getEncodedLocations()); + } + + @Test + public void refresh_impersonated_workforce_trustBoundarySuccess() throws IOException { + String audience = + "//iam.googleapis.com/locations/global/workforcePools/my-pool/providers/my-provider"; + TestEnvironmentProvider environmentProvider = new TestEnvironmentProvider(); + TrustBoundary.setEnvironmentProviderForTest(environmentProvider); + environmentProvider.setEnv("GOOGLE_AUTH_TRUST_BOUNDARY_ENABLE_EXPERIMENT", "1"); + + transportFactory.transport.setExpireTime(TestUtils.getDefaultExpireTime()); + + // Use a URL-based source that the mock transport can handle, to avoid file IO. + Map urlCredentialSourceMap = new HashMap<>(); + urlCredentialSourceMap.put("url", "https://www.metadata.google.com"); + Map headers = new HashMap<>(); + headers.put("Metadata-Flavor", "Google"); + urlCredentialSourceMap.put("headers", headers); + + ExternalAccountCredentials credentials = + IdentityPoolCredentials.newBuilder() + .setHttpTransportFactory(transportFactory) + .setAudience(audience) + .setWorkforcePoolUserProject("12345") + .setSubjectTokenType("subject_token_type") + .setTokenUrl(STS_URL) + .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) + .setCredentialSource(new IdentityPoolCredentialSource(urlCredentialSourceMap)) + .setEnvironmentProvider(environmentProvider) + .build(); + + credentials.refresh(); + + TrustBoundary trustBoundary = credentials.getTrustBoundary(); + assertNotNull(trustBoundary); + assertEquals(TestUtils.TRUST_BOUNDARY_ENCODED_LOCATION, trustBoundary.getEncodedLocations()); + } + private GenericJson buildJsonIdentityPoolCredential() { GenericJson json = new GenericJson(); json.put( diff --git a/oauth2_http/javatests/com/google/auth/oauth2/GoogleCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/GoogleCredentialsTest.java index b8f8c76e9..edf8b104a 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/GoogleCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/GoogleCredentialsTest.java @@ -31,6 +31,7 @@ package com.google.auth.oauth2; +import static com.google.auth.oauth2.TrustBoundary.TRUST_BOUNDARY_KEY; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -977,7 +978,8 @@ public void trustBoundary_shouldFetchAndReturnTrustBoundaryDataSuccessfully() th MockTokenServerTransport transport = new MockTokenServerTransport(); transport.addServiceAccount(SA_CLIENT_EMAIL, ACCESS_TOKEN); TrustBoundary trustBoundary = - new TrustBoundary("0x80000", Collections.singletonList("us-central1")); + new TrustBoundary( + TestUtils.TRUST_BOUNDARY_ENCODED_LOCATION, Collections.singletonList("us-central1")); transport.setTrustBoundary(trustBoundary); ServiceAccountCredentials credentials = @@ -990,7 +992,8 @@ public void trustBoundary_shouldFetchAndReturnTrustBoundaryDataSuccessfully() th .build(); Map> headers = credentials.getRequestMetadata(); - assertEquals(headers.get("x-allowed-locations"), Arrays.asList("0x80000")); + assertEquals( + headers.get(TRUST_BOUNDARY_KEY), Arrays.asList(TestUtils.TRUST_BOUNDARY_ENCODED_LOCATION)); } @Test @@ -1004,7 +1007,8 @@ public void trustBoundary_shouldRetryTrustBoundaryLookupOnFailure() throws IOExc MockTokenServerTransport trustBoundaryTransport = new MockTokenServerTransport(); trustBoundaryTransport.addResponseErrorSequence(new IOException("Service Unavailable")); TrustBoundary trustBoundary = - new TrustBoundary("0x80000", Collections.singletonList("us-central1")); + new TrustBoundary( + TestUtils.TRUST_BOUNDARY_ENCODED_LOCATION, TestUtils.TRUST_BOUNDARY_LOCATIONS); trustBoundaryTransport.setTrustBoundary(trustBoundary); // This transport will be used for the access token refresh. @@ -1034,7 +1038,8 @@ public com.google.api.client.http.LowLevelHttpRequest buildRequest( .build(); Map> headers = credentials.getRequestMetadata(); - assertEquals(headers.get("x-allowed-locations"), Arrays.asList("0x80000")); + assertEquals( + Arrays.asList(TestUtils.TRUST_BOUNDARY_ENCODED_LOCATION), headers.get(TRUST_BOUNDARY_KEY)); } @Test @@ -1135,7 +1140,8 @@ public void trustBoundary_refreshShouldReturnNoOpAndNotCallLookupEndpointWhenCac // Set trust boundary to a valid non No-Op value. transport.setTrustBoundary( - new TrustBoundary("0x80000", Collections.singletonList("us-central1"))); + new TrustBoundary( + TestUtils.TRUST_BOUNDARY_ENCODED_LOCATION, TestUtils.TRUST_BOUNDARY_LOCATIONS)); // Refresh trust boundaries credentials.refresh(); @@ -1153,7 +1159,8 @@ public void trustBoundary_refreshShouldReturnCachedTbIfCallToLookupFails() throw MockTokenServerTransport transport = new MockTokenServerTransport(); transport.addServiceAccount(SA_CLIENT_EMAIL, ACCESS_TOKEN); TrustBoundary trustBoundary = - new TrustBoundary("0x80000", Collections.singletonList("us-central1")); + new TrustBoundary( + TestUtils.TRUST_BOUNDARY_ENCODED_LOCATION, TestUtils.TRUST_BOUNDARY_LOCATIONS); transport.setTrustBoundary(trustBoundary); ServiceAccountCredentials credentials = @@ -1174,7 +1181,8 @@ public void trustBoundary_refreshShouldReturnCachedTbIfCallToLookupFails() throw credentials.refresh(); assertEquals( - trustBoundary.getEncodedLocations(), credentials.getTrustBoundary().getEncodedLocations()); + TestUtils.TRUST_BOUNDARY_ENCODED_LOCATION, + credentials.getTrustBoundary().getEncodedLocations()); } @Test @@ -1239,7 +1247,8 @@ public void trustBoundary_getRequestHeadersShouldAttachTrustBoundaryHeader() thr MockTokenServerTransport transport = new MockTokenServerTransport(); transport.addServiceAccount(SA_CLIENT_EMAIL, ACCESS_TOKEN); TrustBoundary trustBoundary = - new TrustBoundary("0x80000", Collections.singletonList("us-central1")); + new TrustBoundary( + TestUtils.TRUST_BOUNDARY_ENCODED_LOCATION, Collections.singletonList("us-central1")); transport.setTrustBoundary(trustBoundary); ServiceAccountCredentials credentials = @@ -1253,7 +1262,8 @@ public void trustBoundary_getRequestHeadersShouldAttachTrustBoundaryHeader() thr Map> headers = credentials.getRequestMetadata(); - assertEquals(Arrays.asList("0x80000"), headers.get("x-allowed-locations")); + assertEquals( + Arrays.asList(TestUtils.TRUST_BOUNDARY_ENCODED_LOCATION), headers.get(TRUST_BOUNDARY_KEY)); } @Test @@ -1278,7 +1288,7 @@ public void trustBoundary_getRequestHeadersShouldAttachEmptyStringTbHeaderInCase Map> headers = credentials.getRequestMetadata(); - assertEquals(Arrays.asList(""), headers.get("x-allowed-locations")); + assertEquals(Arrays.asList(""), headers.get(TRUST_BOUNDARY_KEY)); } @Test @@ -1303,6 +1313,6 @@ public void trustBoundary_getRequestHeadersShouldNotAttachTbHeaderInCaseOfNonGdu Map> headers = credentials.getRequestMetadata(); - assertNull(headers.get("x-allowed-locations")); + assertNull(headers.get(TRUST_BOUNDARY_KEY)); } } diff --git a/oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java index cce03e085..a48e56c7e 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java @@ -1304,4 +1304,32 @@ void setShouldThrowOnGetCertificatePath(boolean shouldThrow) { this.shouldThrowOnGetCertificatePath = shouldThrow; } } + + @Test + public void testRefresh_trustBoundarySuccess() throws IOException { + TestEnvironmentProvider environmentProvider = new TestEnvironmentProvider(); + TrustBoundary.setEnvironmentProviderForTest(environmentProvider); + environmentProvider.setEnv("GOOGLE_AUTH_TRUST_BOUNDARY_ENABLE_EXPERIMENT", "1"); + + MockExternalAccountCredentialsTransportFactory transportFactory = + new MockExternalAccountCredentialsTransportFactory(); + HttpTransportFactory testingHttpTransportFactory = transportFactory; + + IdentityPoolCredentials credentials = + IdentityPoolCredentials.newBuilder() + .setSubjectTokenSupplier(testProvider) + .setHttpTransportFactory(testingHttpTransportFactory) + .setAudience( + "//iam.googleapis.com/projects/12345/locations/global/workloadIdentityPools/pool/providers/provider") + .setSubjectTokenType("subjectTokenType") + .setTokenUrl(STS_URL) + .build(); + + credentials.refresh(); + + TrustBoundary trustBoundary = credentials.getTrustBoundary(); + assertNotNull(trustBoundary); + assertEquals(TestUtils.TRUST_BOUNDARY_ENCODED_LOCATION, trustBoundary.getEncodedLocations()); + TrustBoundary.setEnvironmentProviderForTest(null); + } } diff --git a/oauth2_http/javatests/com/google/auth/oauth2/ImpersonatedCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/ImpersonatedCredentialsTest.java index 87225c464..abe5d4ed7 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/ImpersonatedCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/ImpersonatedCredentialsTest.java @@ -31,6 +31,7 @@ package com.google.auth.oauth2; +import static com.google.auth.oauth2.TrustBoundary.TRUST_BOUNDARY_KEY; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -67,6 +68,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; +import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Map; @@ -155,7 +157,8 @@ public class ImpersonatedCredentialsTest extends BaseSerializationTest { public static final List DELEGATES = Arrays.asList("sa1@developer.gserviceaccount.com", "sa2@developer.gserviceaccount.com"); public static final TrustBoundary TRUST_BOUNDARY = - new TrustBoundary("0x80000", Arrays.asList("us-central1")); + new TrustBoundary( + TestUtils.TRUST_BOUNDARY_ENCODED_LOCATION, TestUtils.TRUST_BOUNDARY_LOCATIONS); private GoogleCredentials sourceCredentials; private MockIAMCredentialsServiceTransportFactory mockTransportFactory; @@ -1338,7 +1341,9 @@ public void refresh_trustBoundarySuccess() throws IOException { mockTransportFactory); Map> headers = targetCredentials.getRequestMetadata(); - assertEquals(headers.get("x-allowed-locations"), Arrays.asList("0x80000")); + assertEquals( + headers.get(TRUST_BOUNDARY_KEY), + Collections.singletonList(TestUtils.TRUST_BOUNDARY_ENCODED_LOCATION)); } @Test @@ -1347,9 +1352,6 @@ public void refresh_trustBoundaryFails_throwsIOException() throws IOException { TrustBoundary.setEnvironmentProviderForTest(environmentProvider); environmentProvider.setEnv("GOOGLE_AUTH_TRUST_BOUNDARY_ENABLE_EXPERIMENT", "1"); - // Mock trust boundary response - TrustBoundary trustBoundary = TRUST_BOUNDARY; - mockTransportFactory.getTransport().setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); mockTransportFactory.getTransport().setAccessToken(ACCESS_TOKEN); mockTransportFactory.getTransport().setExpireTime(getDefaultExpireTime()); diff --git a/oauth2_http/javatests/com/google/auth/oauth2/MockExternalAccountCredentialsTransport.java b/oauth2_http/javatests/com/google/auth/oauth2/MockExternalAccountCredentialsTransport.java index d1bfdaecf..7be65adf3 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/MockExternalAccountCredentialsTransport.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/MockExternalAccountCredentialsTransport.java @@ -68,6 +68,7 @@ public class MockExternalAccountCredentialsTransport extends MockHttpTransport { private static final String AWS_IMDSV2_SESSION_TOKEN_URL = "https://169.254.169.254/imdsv2"; private static final String METADATA_SERVER_URL = "https://www.metadata.google.com"; private static final String STS_URL = "https://sts.googleapis.com/v1/token"; + private static final String TRUST_BOUNDARY_URL_END = "/allowedLocations"; private static final String SUBJECT_TOKEN = "subjectToken"; private static final String TOKEN_TYPE = "Bearer"; @@ -196,6 +197,18 @@ public LowLevelHttpResponse execute() throws IOException { } if (url.contains(IAM_ENDPOINT)) { + + if (url.endsWith(TRUST_BOUNDARY_URL_END)) { + GenericJson responseJson = new GenericJson(); + responseJson.setFactory(OAuth2Utils.JSON_FACTORY); + responseJson.put("encodedLocations", TestUtils.TRUST_BOUNDARY_ENCODED_LOCATION); + responseJson.put("locations", TestUtils.TRUST_BOUNDARY_LOCATIONS); + String content = responseJson.toPrettyString(); + return new MockLowLevelHttpResponse() + .setContentType(Json.MEDIA_TYPE) + .setContent(content); + } + GenericJson query = OAuth2Utils.JSON_FACTORY .createJsonParser(getContentAsString()) diff --git a/oauth2_http/javatests/com/google/auth/oauth2/MockMetadataServerTransport.java b/oauth2_http/javatests/com/google/auth/oauth2/MockMetadataServerTransport.java index 057d19b94..3e90d0faa 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/MockMetadataServerTransport.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/MockMetadataServerTransport.java @@ -388,8 +388,10 @@ public LowLevelHttpResponse execute() throws IOException { } protected boolean isIamLookupUrl(String url) { - // This is for mocking the call to IAM lookup when we attempt to refresh trust boundaries - // after refreshing the access token. + // Mocking call to the /allowedLocations endpoint for trust boundary refresh. + // For testing convenience, this mock transport handles + // the /allowedLocations endpoint. The actual server for this endpoint + // will be the IAM Credentials API. return url.endsWith("/allowedLocations"); } } diff --git a/oauth2_http/javatests/com/google/auth/oauth2/MockStsTransport.java b/oauth2_http/javatests/com/google/auth/oauth2/MockStsTransport.java index 5b1b3fded..e3a50d027 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/MockStsTransport.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/MockStsTransport.java @@ -62,6 +62,8 @@ public final class MockStsTransport extends MockHttpTransport { private static final String ISSUED_TOKEN_TYPE = "urn:ietf:params:oauth:token-type:access_token"; private static final String VALID_STS_PATTERN = "https:\\/\\/sts.[a-z-_\\.]+\\/v1\\/(token|oauthtoken)"; + private static final String VALID_TRUST_BOUNDARY_PATTERN = + "https:\\/\\/iam.[a-z-_\\.]+\\/v1\\/.*\\/allowedLocations"; private static final String ACCESS_TOKEN = "accessToken"; private static final String TOKEN_TYPE = "Bearer"; private static final Long EXPIRES_IN = 3600L; @@ -99,6 +101,22 @@ public LowLevelHttpRequest buildRequest(final String method, final String url) { new MockLowLevelHttpRequest(url) { @Override public LowLevelHttpResponse execute() throws IOException { + // Mocking call to refresh trust boundaries. + // The lookup endpoint is located in the IAM server. + Matcher trustBoundaryMatcher = + Pattern.compile(VALID_TRUST_BOUNDARY_PATTERN).matcher(url); + if (trustBoundaryMatcher.matches()) { + // Mocking call to the /allowedLocations endpoint for trust boundary refresh. + // For testing convenience, this mock transport handles + // the /allowedLocations endpoint. + GenericJson response = new GenericJson(); + response.put("locations", TestUtils.TRUST_BOUNDARY_LOCATIONS); + response.put("encodedLocations", TestUtils.TRUST_BOUNDARY_ENCODED_LOCATION); + return new MockLowLevelHttpResponse() + .setContentType(Json.MEDIA_TYPE) + .setContent(OAuth2Utils.JSON_FACTORY.toString(response)); + } + // Environment version is prefixed by "aws". e.g. "aws1". Matcher matcher = Pattern.compile(VALID_STS_PATTERN).matcher(url); if (!matcher.matches()) { diff --git a/oauth2_http/javatests/com/google/auth/oauth2/MockTokenServerTransport.java b/oauth2_http/javatests/com/google/auth/oauth2/MockTokenServerTransport.java index c0b9df754..76ef3f807 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/MockTokenServerTransport.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/MockTokenServerTransport.java @@ -327,8 +327,10 @@ public LowLevelHttpResponse execute() throws IOException { }; return request; } else if (urlWithoutQuery.endsWith("/allowedLocations")) { - // This is for mocking the call to IAM lookup when we attempt to refresh trust boundaries - // after refreshing the access token. + // Mocking call to the /allowedLocations endpoint for trust boundary refresh. + // For testing convenience, this mock transport handles + // the /allowedLocations endpoint. The actual server for this endpoint + // will be the IAM Credentials API. request = new MockLowLevelHttpRequest(url) { @Override diff --git a/oauth2_http/javatests/com/google/auth/oauth2/PluggableAuthCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/PluggableAuthCredentialsTest.java index cd321daf3..fa21acc0e 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/PluggableAuthCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/PluggableAuthCredentialsTest.java @@ -603,6 +603,34 @@ public void serialize() throws IOException, ClassNotFoundException { assertThrows(NotSerializableException.class, () -> serializeAndDeserialize(testCredentials)); } + @Test + public void testRefresh_trustBoundarySuccess() throws IOException { + TestEnvironmentProvider environmentProvider = new TestEnvironmentProvider(); + TrustBoundary.setEnvironmentProviderForTest(environmentProvider); + environmentProvider.setEnv("GOOGLE_AUTH_TRUST_BOUNDARY_ENABLE_EXPERIMENT", "1"); + + MockExternalAccountCredentialsTransportFactory transportFactory = + new MockExternalAccountCredentialsTransportFactory(); + transportFactory.transport.setExpireTime(TestUtils.getDefaultExpireTime()); + + PluggableAuthCredentials credentials = + PluggableAuthCredentials.newBuilder() + .setHttpTransportFactory(transportFactory) + .setAudience( + "//iam.googleapis.com/projects/12345/locations/global/workloadIdentityPools/pool/providers/provider") + .setSubjectTokenType("subjectTokenType") + .setTokenUrl(transportFactory.transport.getStsUrl()) + .setCredentialSource(buildCredentialSource()) + .setExecutableHandler(options -> "pluggableAuthToken") + .build(); + + credentials.refresh(); + TrustBoundary trustBoundary = credentials.getTrustBoundary(); + assertNotNull(trustBoundary); + assertEquals(TestUtils.TRUST_BOUNDARY_ENCODED_LOCATION, trustBoundary.getEncodedLocations()); + TrustBoundary.setEnvironmentProviderForTest(null); + } + private static PluggableAuthCredentialSource buildCredentialSource() { return buildCredentialSource("command", null, null); } diff --git a/oauth2_http/javatests/com/google/auth/oauth2/ServiceAccountCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/ServiceAccountCredentialsTest.java index 1407f024f..ca275f1e3 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/ServiceAccountCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/ServiceAccountCredentialsTest.java @@ -31,6 +31,7 @@ package com.google.auth.oauth2; +import static com.google.auth.oauth2.TrustBoundary.TRUST_BOUNDARY_KEY; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -1816,15 +1817,16 @@ public void refresh_trustBoundarySuccess() throws IOException { // Mock trust boundary response TrustBoundary trustBoundary = - new TrustBoundary("0x80000", Collections.singletonList("us-central1")); + new TrustBoundary( + TestUtils.TRUST_BOUNDARY_ENCODED_LOCATION, TestUtils.TRUST_BOUNDARY_LOCATIONS); MockTokenServerTransport transport = new MockTokenServerTransport(); - transport.addServiceAccount("test-client-email@example.com", "test-access-token"); + transport.addServiceAccount(CLIENT_EMAIL, "test-access-token"); transport.setTrustBoundary(trustBoundary); ServiceAccountCredentials credentials = ServiceAccountCredentials.newBuilder() - .setClientEmail("test-client-email@example.com") + .setClientEmail(CLIENT_EMAIL) .setPrivateKey( OAuth2Utils.privateKeyFromPkcs8(ServiceAccountCredentialsTest.PRIVATE_KEY_PKCS8)) .setPrivateKeyId("test-key-id") @@ -1833,7 +1835,8 @@ public void refresh_trustBoundarySuccess() throws IOException { .build(); Map> headers = credentials.getRequestMetadata(); - assertEquals(headers.get("x-allowed-locations"), Arrays.asList("0x80000")); + assertEquals( + headers.get(TRUST_BOUNDARY_KEY), Arrays.asList(TestUtils.TRUST_BOUNDARY_ENCODED_LOCATION)); } @Test @@ -1843,11 +1846,11 @@ public void refresh_trustBoundaryFails_throwsIOException() throws IOException { environmentProvider.setEnv("GOOGLE_AUTH_TRUST_BOUNDARY_ENABLE_EXPERIMENT", "1"); MockTokenServerTransport transport = new MockTokenServerTransport(); - transport.addServiceAccount("test-client-email@example.com", "test-access-token"); + transport.addServiceAccount(CLIENT_EMAIL, "test-access-token"); ServiceAccountCredentials credentials = ServiceAccountCredentials.newBuilder() - .setClientEmail("test-client-email@example.com") + .setClientEmail(CLIENT_EMAIL) .setPrivateKey( OAuth2Utils.privateKeyFromPkcs8(ServiceAccountCredentialsTest.PRIVATE_KEY_PKCS8)) .setPrivateKeyId("test-key-id")