diff --git a/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java b/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java index 27acdfc54..c4268d167 100644 --- a/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java @@ -48,8 +48,8 @@ import java.net.URI; import java.nio.charset.StandardCharsets; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Locale; @@ -68,9 +68,6 @@ public abstract class ExternalAccountCredentials extends GoogleCredentials { private static final long serialVersionUID = 8049126194174465023L; - private static final String CLOUD_PLATFORM_SCOPE = - "https://www.googleapis.com/auth/cloud-platform"; - static final String EXECUTABLE_SOURCE_KEY = "executable"; static final String DEFAULT_TOKEN_URL = "https://sts.{UNIVERSE_DOMAIN}/v1/token"; @@ -200,7 +197,9 @@ protected ExternalAccountCredentials( this.clientId = clientId; this.clientSecret = clientSecret; this.scopes = - (scopes == null || scopes.isEmpty()) ? Arrays.asList(CLOUD_PLATFORM_SCOPE) : scopes; + (scopes == null || scopes.isEmpty()) + ? Collections.singletonList(OAuth2Utils.CLOUD_PLATFORM_SCOPE) + : scopes; this.environmentProvider = environmentProvider == null ? SystemEnvironmentProvider.getInstance() : environmentProvider; this.workforcePoolUserProject = null; @@ -245,7 +244,7 @@ protected ExternalAccountCredentials(ExternalAccountCredentials.Builder builder) this.scopes = (builder.scopes == null || builder.scopes.isEmpty()) - ? Arrays.asList(CLOUD_PLATFORM_SCOPE) + ? Collections.singletonList(OAuth2Utils.CLOUD_PLATFORM_SCOPE) : builder.scopes; this.environmentProvider = builder.environmentProvider == null diff --git a/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java b/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java index bf8c56888..18d7cd0f8 100644 --- a/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java @@ -50,6 +50,8 @@ import com.google.auth.oauth2.MetricsUtils.RequestType; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.MoreObjects; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.io.IOException; @@ -58,9 +60,9 @@ import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; -import java.util.Arrays; import java.util.Calendar; import java.util.Collection; +import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Map; @@ -99,14 +101,12 @@ public class ImpersonatedCredentials extends GoogleCredentials private static final String RFC3339 = "yyyy-MM-dd'T'HH:mm:ssX"; private static final int TWELVE_HOURS_IN_SECONDS = 43200; private static final int DEFAULT_LIFETIME_IN_SECONDS = 3600; - private static final String CLOUD_PLATFORM_SCOPE = - "https://www.googleapis.com/auth/cloud-platform"; private GoogleCredentials sourceCredentials; - private String targetPrincipal; + private final String targetPrincipal; private List delegates; - private List scopes; - private int lifetime; - private String iamEndpointOverride; + private final List scopes; + private final int lifetime; + private final String iamEndpointOverride; private final String transportFactoryClassName; private static final LoggerProvider LOGGER_PROVIDER = LoggerProvider.forClazz(ImpersonatedCredentials.class); @@ -390,6 +390,10 @@ static ImpersonatedCredentials fromJson( String quotaProjectId; String targetPrincipal; String serviceAccountImpersonationUrl; + // This applies to the scopes applied for the impersonated token and not the + // underlying source credential. Default to empty list to keep the existing + // behavior (when json file did not populate a scopes field). + List scopes = ImmutableList.of(); try { serviceAccountImpersonationUrl = (String) json.get("service_account_impersonation_url"); if (json.containsKey("delegates")) { @@ -399,6 +403,9 @@ static ImpersonatedCredentials fromJson( sourceCredentialsType = (String) sourceCredentialsJson.get("type"); quotaProjectId = (String) json.get("quota_project_id"); targetPrincipal = extractTargetPrincipal(serviceAccountImpersonationUrl); + if (json.containsKey("scopes")) { + scopes = ImmutableList.copyOf((List) json.get("scopes")); + } } catch (ClassCastException | NullPointerException | IllegalArgumentException e) { throw new CredentialFormatException("An invalid input stream was provided.", e); } @@ -421,7 +428,7 @@ static ImpersonatedCredentials fromJson( .setSourceCredentials(sourceCredentials) .setTargetPrincipal(targetPrincipal) .setDelegates(delegates) - .setScopes(new ArrayList<>()) + .setScopes(scopes) .setLifetime(DEFAULT_LIFETIME_IN_SECONDS) .setHttpTransportFactory(transportFactory) .setQuotaProjectId(quotaProjectId) @@ -436,7 +443,7 @@ public boolean createScopedRequired() { @Override public GoogleCredentials createScoped(Collection scopes) { - return toBuilder().setScopes(new ArrayList<>(scopes)).setAccessToken(null).build(); + return toBuilder().setScopes(ImmutableList.copyOf(scopes)).setAccessToken(null).build(); } @Override @@ -468,7 +475,7 @@ private ImpersonatedCredentials(Builder builder) throws IOException { this.sourceCredentials = builder.getSourceCredentials(); this.targetPrincipal = builder.getTargetPrincipal(); this.delegates = builder.getDelegates(); - this.scopes = builder.getScopes(); + this.scopes = ImmutableList.copyOf(builder.getScopes()); this.lifetime = builder.getLifetime(); this.transportFactory = firstNonNull( @@ -480,9 +487,6 @@ private ImpersonatedCredentials(Builder builder) throws IOException { if (this.delegates == null) { this.delegates = new ArrayList<>(); } - if (this.scopes == null) { - throw new IllegalStateException("Scopes cannot be null"); - } if (this.lifetime > TWELVE_HOURS_IN_SECONDS) { throw new IllegalStateException("lifetime must be less than or equal to 43200"); } @@ -516,8 +520,10 @@ public String getUniverseDomain() throws IOException { @Override public AccessToken refreshAccessToken() throws IOException { if (this.sourceCredentials.getAccessToken() == null) { + // Apply the `CLOUD_PLATFORM_SCOPE` to access the iamcredentials endpoint this.sourceCredentials = - this.sourceCredentials.createScoped(Arrays.asList(CLOUD_PLATFORM_SCOPE)); + this.sourceCredentials.createScoped( + Collections.singletonList(OAuth2Utils.CLOUD_PLATFORM_SCOPE)); } // skip for SA with SSJ flow because it uses self-signed JWT @@ -551,7 +557,7 @@ public AccessToken refreshAccessToken() throws IOException { GenericUrl url = new GenericUrl(endpointUrl); Map body = - ImmutableMap.of( + ImmutableMap.of( "delegates", this.delegates, "scope", this.scopes, "lifetime", this.lifetime + "s"); HttpContent requestContent = new JsonHttpContent(parser.getJsonFactory(), body); @@ -741,12 +747,22 @@ public List getDelegates() { return this.delegates; } + /** + * Set the scopes to be applied on the impersonated token and not on the source credential. This + * user configuration has precedence over the scopes listed in the source credential json file. + * + * @param scopes List of scopes to apply to the impersonated token + */ @CanIgnoreReturnValue public Builder setScopes(List scopes) { + Preconditions.checkNotNull(scopes, "Scopes cannot be null"); this.scopes = scopes; return this; } + /** + * @return List of scopes to be applied to the impersonated token. + */ public List getScopes() { return this.scopes; } diff --git a/oauth2_http/java/com/google/auth/oauth2/OAuth2Utils.java b/oauth2_http/java/com/google/auth/oauth2/OAuth2Utils.java index 0359a34e5..21278e8b6 100644 --- a/oauth2_http/java/com/google/auth/oauth2/OAuth2Utils.java +++ b/oauth2_http/java/com/google/auth/oauth2/OAuth2Utils.java @@ -95,6 +95,9 @@ public class OAuth2Utils { static final URI TOKEN_REVOKE_URI = URI.create("https://oauth2.googleapis.com/revoke"); static final URI USER_AUTH_URI = URI.create("https://accounts.google.com/o/oauth2/auth"); + public static final String CLOUD_PLATFORM_SCOPE = + "https://www.googleapis.com/auth/cloud-platform"; + static final HttpTransport HTTP_TRANSPORT = new NetHttpTransport(); public static final HttpTransportFactory HTTP_TRANSPORT_FACTORY = diff --git a/oauth2_http/javatests/com/google/auth/oauth2/DownscopedCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/DownscopedCredentialsTest.java index 7f4da0611..d054d7ee6 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/DownscopedCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/DownscopedCredentialsTest.java @@ -236,7 +236,7 @@ public void builder_noTransport_defaults() throws IOException { .build(); GoogleCredentials scopedSourceCredentials = - sourceCredentials.createScoped("https://www.googleapis.com/auth/cloud-platform"); + sourceCredentials.createScoped(OAuth2Utils.CLOUD_PLATFORM_SCOPE); assertEquals(scopedSourceCredentials, credentials.getSourceCredentials()); assertEquals(CREDENTIAL_ACCESS_BOUNDARY, credentials.getCredentialAccessBoundary()); assertEquals(OAuth2Utils.HTTP_TRANSPORT_FACTORY, credentials.getTransportFactory()); @@ -254,7 +254,7 @@ public void builder_noUniverseDomain_defaults() throws IOException { .build(); GoogleCredentials scopedSourceCredentials = - sourceCredentials.createScoped("https://www.googleapis.com/auth/cloud-platform"); + sourceCredentials.createScoped(OAuth2Utils.CLOUD_PLATFORM_SCOPE); assertEquals(OAuth2Utils.HTTP_TRANSPORT_FACTORY, credentials.getTransportFactory()); assertEquals(scopedSourceCredentials, credentials.getSourceCredentials()); assertEquals(CREDENTIAL_ACCESS_BOUNDARY, credentials.getCredentialAccessBoundary()); @@ -320,7 +320,7 @@ private static GoogleCredentials getServiceAccountSourceCredentials(boolean canR transportFactory.transport.setError(new IOException()); } - return sourceCredentials.createScoped("https://www.googleapis.com/auth/cloud-platform"); + return sourceCredentials.createScoped(OAuth2Utils.CLOUD_PLATFORM_SCOPE); } private static GoogleCredentials getUserSourceCredentials() { diff --git a/oauth2_http/javatests/com/google/auth/oauth2/GoogleCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/GoogleCredentialsTest.java index 7bd7ec1e0..5004fd6b6 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/GoogleCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/GoogleCredentialsTest.java @@ -619,7 +619,8 @@ public void fromStream_Impersonation_providesToken_WithQuotaProject() throws IOE ImpersonatedCredentialsTest.writeImpersonationCredentialsStream( ImpersonatedCredentialsTest.IMPERSONATION_OVERRIDE_URL, ImpersonatedCredentialsTest.DELEGATES, - ImpersonatedCredentialsTest.QUOTA_PROJECT_ID); + ImpersonatedCredentialsTest.QUOTA_PROJECT_ID, + ImpersonatedCredentialsTest.IMMUTABLE_SCOPES_LIST); ImpersonatedCredentials credentials = (ImpersonatedCredentials) @@ -649,7 +650,8 @@ public void fromStream_Impersonation_defaultUniverse() throws IOException { ImpersonatedCredentialsTest.writeImpersonationCredentialsStream( ImpersonatedCredentialsTest.IMPERSONATION_OVERRIDE_URL, ImpersonatedCredentialsTest.DELEGATES, - ImpersonatedCredentialsTest.QUOTA_PROJECT_ID); + ImpersonatedCredentialsTest.QUOTA_PROJECT_ID, + ImpersonatedCredentialsTest.IMMUTABLE_SCOPES_LIST); ImpersonatedCredentials credentials = (ImpersonatedCredentials) @@ -684,7 +686,8 @@ public void fromStream_Impersonation_providesToken_WithoutQuotaProject() throws ImpersonatedCredentialsTest.writeImpersonationCredentialsStream( ImpersonatedCredentialsTest.IMPERSONATION_OVERRIDE_URL, ImpersonatedCredentialsTest.DELEGATES, - null); + null, + ImpersonatedCredentialsTest.IMMUTABLE_SCOPES_LIST); ImpersonatedCredentials credentials = (ImpersonatedCredentials) @@ -916,7 +919,8 @@ public void getCredentialInfo_impersonatedServiceAccount() throws IOException { ImpersonatedCredentialsTest.writeImpersonationCredentialsStream( ImpersonatedCredentialsTest.IMPERSONATION_OVERRIDE_URL, ImpersonatedCredentialsTest.DELEGATES, - null); + null, + ImpersonatedCredentialsTest.IMMUTABLE_SCOPES_LIST); ImpersonatedCredentials credentials = (ImpersonatedCredentials) GoogleCredentials.fromStream(impersonationCredentialsStream); diff --git a/oauth2_http/javatests/com/google/auth/oauth2/ITDownscopingTest.java b/oauth2_http/javatests/com/google/auth/oauth2/ITDownscopingTest.java index bd00f42c4..bf7946164 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/ITDownscopingTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/ITDownscopingTest.java @@ -102,7 +102,7 @@ public AccessToken refreshAccessToken() throws IOException { ServiceAccountCredentials credentials = (ServiceAccountCredentials) GoogleCredentials.getApplicationDefault() - .createScoped("https://www.googleapis.com/auth/cloud-platform"); + .createScoped(OAuth2Utils.CLOUD_PLATFORM_SCOPE); DownscopedCredentials downscopedCredentials = DownscopedCredentials.newBuilder() diff --git a/oauth2_http/javatests/com/google/auth/oauth2/ITWorkloadIdentityFederationTest.java b/oauth2_http/javatests/com/google/auth/oauth2/ITWorkloadIdentityFederationTest.java index 36cced617..25982e544 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/ITWorkloadIdentityFederationTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/ITWorkloadIdentityFederationTest.java @@ -408,8 +408,7 @@ private void callGcs(GoogleCredentials credentials) throws IOException { */ private String generateGoogleIdToken(String audience) throws IOException { GoogleCredentials googleCredentials = - GoogleCredentials.getApplicationDefault() - .createScoped("https://www.googleapis.com/auth/cloud-platform"); + GoogleCredentials.getApplicationDefault().createScoped(OAuth2Utils.CLOUD_PLATFORM_SCOPE); HttpCredentialsAdapter credentialsAdapter = new HttpCredentialsAdapter(googleCredentials); HttpRequestFactory requestFactory = diff --git a/oauth2_http/javatests/com/google/auth/oauth2/ImpersonatedCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/ImpersonatedCredentialsTest.java index d76491c59..1cfde9cf8 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/ImpersonatedCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/ImpersonatedCredentialsTest.java @@ -123,11 +123,10 @@ public class ImpersonatedCredentialsTest extends BaseSerializationTest { static final List IMMUTABLE_SCOPES_LIST = ImmutableList.of("scope1", "scope2"); static final int VALID_LIFETIME = 300; private static final int INVALID_LIFETIME = 43210; - private static JsonFactory JSON_FACTORY = GsonFactory.getDefaultInstance(); + private static final JsonFactory JSON_FACTORY = GsonFactory.getDefaultInstance(); private static final String RFC3339 = "yyyy-MM-dd'T'HH:mm:ssX"; - private static final String DEFAULT_UNIVERSE_DOMAIN = "googleapis.com"; private static final String TEST_UNIVERSE_DOMAIN = "test.xyz"; private static final String OLD_IMPERSONATION_URL = "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/" @@ -136,7 +135,7 @@ public class ImpersonatedCredentialsTest extends BaseSerializationTest { public static final String DEFAULT_IMPERSONATION_URL = String.format( IamUtils.IAM_ACCESS_TOKEN_ENDPOINT_FORMAT, - DEFAULT_UNIVERSE_DOMAIN, + GoogleCredentials.GOOGLE_DEFAULT_UNIVERSE, IMPERSONATED_CLIENT_EMAIL); private static final String NONGDU_IMPERSONATION_URL = String.format( @@ -190,14 +189,15 @@ public void fromJson_userAsSource_WithQuotaProjectId() throws IOException { QUOTA_PROJECT_ID, USER_ACCOUNT_CLIENT_ID, USER_ACCOUNT_CLIENT_SECRET, - REFRESH_TOKEN); + REFRESH_TOKEN, + IMMUTABLE_SCOPES_LIST); ImpersonatedCredentials credentials = ImpersonatedCredentials.fromJson(json, mockTransportFactory); assertEquals(IMPERSONATED_CLIENT_EMAIL, credentials.getAccount()); assertEquals(IMPERSONATION_OVERRIDE_URL, credentials.getIamEndpointOverride()); assertEquals(QUOTA_PROJECT_ID, credentials.getQuotaProjectId()); assertEquals(DELEGATES, credentials.getDelegates()); - assertEquals(new ArrayList(), credentials.getScopes()); + assertEquals(IMMUTABLE_SCOPES_LIST, credentials.getScopes()); assertEquals(3600, credentials.getLifetime()); GoogleCredentials sourceCredentials = credentials.getSourceCredentials(); assertTrue(sourceCredentials instanceof UserCredentials); @@ -212,14 +212,15 @@ public void fromJson_userAsSource_WithoutQuotaProjectId() throws IOException { null, USER_ACCOUNT_CLIENT_ID, USER_ACCOUNT_CLIENT_SECRET, - REFRESH_TOKEN); + REFRESH_TOKEN, + IMMUTABLE_SCOPES_LIST); ImpersonatedCredentials credentials = ImpersonatedCredentials.fromJson(json, mockTransportFactory); assertEquals(IMPERSONATED_CLIENT_EMAIL, credentials.getAccount()); assertEquals(IMPERSONATION_OVERRIDE_URL, credentials.getIamEndpointOverride()); assertNull(credentials.getQuotaProjectId()); assertEquals(DELEGATES, credentials.getDelegates()); - assertEquals(new ArrayList(), credentials.getScopes()); + assertEquals(IMMUTABLE_SCOPES_LIST, credentials.getScopes()); assertEquals(3600, credentials.getLifetime()); GoogleCredentials sourceCredentials = credentials.getSourceCredentials(); assertTrue(sourceCredentials instanceof UserCredentials); @@ -234,7 +235,8 @@ public void fromJson_userAsSource_MissingDelegatesField() throws IOException { null, USER_ACCOUNT_CLIENT_ID, USER_ACCOUNT_CLIENT_SECRET, - REFRESH_TOKEN); + REFRESH_TOKEN, + IMMUTABLE_SCOPES_LIST); json.remove("delegates"); ImpersonatedCredentials credentials = ImpersonatedCredentials.fromJson(json, mockTransportFactory); @@ -242,7 +244,7 @@ public void fromJson_userAsSource_MissingDelegatesField() throws IOException { assertEquals(IMPERSONATION_OVERRIDE_URL, credentials.getIamEndpointOverride()); assertNull(credentials.getQuotaProjectId()); assertEquals(new ArrayList(), credentials.getDelegates()); - assertEquals(new ArrayList(), credentials.getScopes()); + assertEquals(IMMUTABLE_SCOPES_LIST, credentials.getScopes()); assertEquals(3600, credentials.getLifetime()); GoogleCredentials sourceCredentials = credentials.getSourceCredentials(); assertTrue(sourceCredentials instanceof UserCredentials); @@ -251,14 +253,15 @@ public void fromJson_userAsSource_MissingDelegatesField() throws IOException { @Test() public void fromJson_ServiceAccountAsSource() throws IOException { GenericJson json = - buildImpersonationCredentialsJson(IMPERSONATION_OVERRIDE_URL, DELEGATES, QUOTA_PROJECT_ID); + buildImpersonationCredentialsJson( + IMPERSONATION_OVERRIDE_URL, DELEGATES, QUOTA_PROJECT_ID, IMMUTABLE_SCOPES_LIST); ImpersonatedCredentials credentials = ImpersonatedCredentials.fromJson(json, mockTransportFactory); assertEquals(IMPERSONATED_CLIENT_EMAIL, credentials.getAccount()); assertEquals(IMPERSONATION_OVERRIDE_URL, credentials.getIamEndpointOverride()); assertEquals(QUOTA_PROJECT_ID, credentials.getQuotaProjectId()); assertEquals(DELEGATES, credentials.getDelegates()); - assertEquals(new ArrayList(), credentials.getScopes()); + assertEquals(IMMUTABLE_SCOPES_LIST, credentials.getScopes()); assertEquals(3600, credentials.getLifetime()); GoogleCredentials sourceCredentials = credentials.getSourceCredentials(); assertTrue(sourceCredentials instanceof ServiceAccountCredentials); @@ -481,18 +484,11 @@ public void credential_with_invalid_lifetime() throws IOException, IllegalStateE @Test() public void credential_with_invalid_scope() throws IOException, IllegalStateException { - - try { - ImpersonatedCredentials targetCredentials = - ImpersonatedCredentials.create( - sourceCredentials, IMPERSONATED_CLIENT_EMAIL, null, null, VALID_LIFETIME); - targetCredentials.refreshAccessToken().getTokenValue(); - fail( - String.format( - "Should throw exception with message containing '%s'", "Scopes cannot be null")); - } catch (IllegalStateException expected) { - assertTrue(expected.getMessage().contains("Scopes cannot be null")); - } + assertThrows( + NullPointerException.class, + () -> + ImpersonatedCredentials.create( + sourceCredentials, IMPERSONATED_CLIENT_EMAIL, null, null, VALID_LIFETIME)); } @Test() @@ -1221,6 +1217,42 @@ public void universeDomain_whenExplicit_AllowedIfMatchesSourceUD() throws IOExce assertTrue(impersonatedCredentials.isExplicitUniverseDomain()); } + @Test + public void scopes_userConfigured() { + ImpersonatedCredentials impersonatedCredentials = + ImpersonatedCredentials.newBuilder().setScopes(IMMUTABLE_SCOPES_LIST).build(); + assertArrayEquals( + IMMUTABLE_SCOPES_LIST.toArray(), impersonatedCredentials.getScopes().toArray()); + } + + @Test + public void scopes_fromJson() throws IOException { + ImpersonatedCredentials impersonatedCredentials = + ImpersonatedCredentials.fromJson( + buildImpersonationCredentialsJson( + DEFAULT_IMPERSONATION_URL, DELEGATES, null, IMMUTABLE_SCOPES_LIST), + mockTransportFactory); + assertArrayEquals( + IMMUTABLE_SCOPES_LIST.toArray(), impersonatedCredentials.getScopes().toArray()); + } + + // Tests that user configured scopes has precedence over the one in the json. + // From the ADC flow, the json is parsed and the credential is returned back + // to the user + @Test + public void scopes_userConfiguredAndFromJson() throws IOException { + List userConfiguredScopes = ImmutableList.of("nonsense-scopes"); + ImpersonatedCredentials impersonatedCredentials = + ImpersonatedCredentials.fromJson( + buildImpersonationCredentialsJson( + DEFAULT_IMPERSONATION_URL, DELEGATES, null, IMMUTABLE_SCOPES_LIST), + mockTransportFactory); + ImpersonatedCredentials newImpersonatedCredentials = + impersonatedCredentials.toBuilder().setScopes(userConfiguredScopes).build(); + assertArrayEquals( + userConfiguredScopes.toArray(), newImpersonatedCredentials.getScopes().toArray()); + } + @Test public void hashCode_equals() throws IOException { mockTransportFactory.getTransport().setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); @@ -1333,7 +1365,8 @@ static GenericJson buildImpersonationCredentialsJson( String quotaProjectId, String sourceClientId, String sourceClientSecret, - String sourceRefreshToken) { + String sourceRefreshToken, + List scopes) { GenericJson sourceJson = new GenericJson(); sourceJson.put("client_id", sourceClientId); @@ -1348,12 +1381,13 @@ static GenericJson buildImpersonationCredentialsJson( json.put("quota_project_id", quotaProjectId); } json.put("source_credentials", sourceJson); + json.put("scopes", scopes); json.put("type", "impersonated_service_account"); return json; } static GenericJson buildImpersonationCredentialsJson( - String impersonationUrl, List delegates, String quotaProjectId) { + String impersonationUrl, List delegates, String quotaProjectId, List scopes) { GenericJson sourceJson = new GenericJson(); sourceJson.put("type", "service_account"); sourceJson.put("project_id", PROJECT_ID); @@ -1375,6 +1409,7 @@ static GenericJson buildImpersonationCredentialsJson( if (quotaProjectId != null) { json.put("quota_project_id", quotaProjectId); } + json.put("scopes", scopes); json.put("type", "impersonated_service_account"); return json; } @@ -1386,9 +1421,10 @@ static GenericJson buildInvalidCredentialsJson() { } static InputStream writeImpersonationCredentialsStream( - String impersonationUrl, List delegates, String quotaProjectId) throws IOException { + String impersonationUrl, List delegates, String quotaProjectId, List scopes) + throws IOException { GenericJson json = - buildImpersonationCredentialsJson(impersonationUrl, delegates, quotaProjectId); + buildImpersonationCredentialsJson(impersonationUrl, delegates, quotaProjectId, scopes); return TestUtils.jsonToInputStream(json); } } diff --git a/oauth2_http/javatests/com/google/auth/oauth2/MockExternalAccountCredentialsTransport.java b/oauth2_http/javatests/com/google/auth/oauth2/MockExternalAccountCredentialsTransport.java index 84e0c5606..d1bfdaecf 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/MockExternalAccountCredentialsTransport.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/MockExternalAccountCredentialsTransport.java @@ -62,8 +62,6 @@ public class MockExternalAccountCredentialsTransport extends MockHttpTransport { private static final String EXPECTED_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:token-exchange"; - private static final String CLOUD_PLATFORM_SCOPE = - "https://www.googleapis.com/auth/cloud-platform"; private static final String ISSUED_TOKEN_TYPE = "urn:ietf:params:oauth:token-type:access_token"; private static final String AWS_CREDENTIALS_URL = "https://169.254.169.254"; private static final String AWS_REGION_URL = "https://169.254.169.254/region"; @@ -202,7 +200,9 @@ public LowLevelHttpResponse execute() throws IOException { OAuth2Utils.JSON_FACTORY .createJsonParser(getContentAsString()) .parseAndClose(GenericJson.class); - assertEquals(CLOUD_PLATFORM_SCOPE, ((ArrayList) query.get("scope")).get(0)); + assertEquals( + OAuth2Utils.CLOUD_PLATFORM_SCOPE, + ((ArrayList) query.get("scope")).get(0)); assertEquals(1, getHeaders().get("authorization").size()); assertTrue(getHeaders().containsKey("authorization")); assertNotNull(getHeaders().get("authorization").get(0)); diff --git a/oauth2_http/javatests/com/google/auth/oauth2/StsRequestHandlerTest.java b/oauth2_http/javatests/com/google/auth/oauth2/StsRequestHandlerTest.java index eb9294ac1..e91558fa7 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/StsRequestHandlerTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/StsRequestHandlerTest.java @@ -42,6 +42,7 @@ import com.google.common.base.Joiner; import java.io.IOException; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Map; import org.junit.Before; @@ -56,8 +57,6 @@ public final class StsRequestHandlerTest { private static final String TOKEN_EXCHANGE_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:token-exchange"; - private static final String CLOUD_PLATFORM_SCOPE = - "https://www.googleapis.com/auth/cloud-platform"; private static final String DEFAULT_REQUESTED_TOKEN_TYPE = "urn:ietf:params:oauth:token-type:access_token"; private static final String TOKEN_URL = "https://sts.googleapis.com/v1/token"; @@ -73,7 +72,7 @@ public void setup() { public void exchangeToken() throws IOException { StsTokenExchangeRequest stsTokenExchangeRequest = StsTokenExchangeRequest.newBuilder("credential", "subjectTokenType") - .setScopes(Arrays.asList(CLOUD_PLATFORM_SCOPE)) + .setScopes(Collections.singletonList(OAuth2Utils.CLOUD_PLATFORM_SCOPE)) .build(); StsRequestHandler requestHandler = @@ -93,7 +92,7 @@ public void exchangeToken() throws IOException { GenericData expectedRequestContent = new GenericData() .set("grant_type", TOKEN_EXCHANGE_GRANT_TYPE) - .set("scope", CLOUD_PLATFORM_SCOPE) + .set("scope", OAuth2Utils.CLOUD_PLATFORM_SCOPE) .set("requested_token_type", DEFAULT_REQUESTED_TOKEN_TYPE) .set("subject_token_type", stsTokenExchangeRequest.getSubjectTokenType()) .set("subject_token", stsTokenExchangeRequest.getSubjectToken()); @@ -257,7 +256,7 @@ public void exchangeToken_noExpiresInReturned() throws IOException { StsTokenExchangeRequest stsTokenExchangeRequest = StsTokenExchangeRequest.newBuilder("credential", "subjectTokenType") - .setScopes(Arrays.asList(CLOUD_PLATFORM_SCOPE)) + .setScopes(Collections.singletonList(OAuth2Utils.CLOUD_PLATFORM_SCOPE)) .build(); StsRequestHandler requestHandler = diff --git a/oauth2_http/javatests/com/google/auth/oauth2/UserAuthorizerTest.java b/oauth2_http/javatests/com/google/auth/oauth2/UserAuthorizerTest.java index 1d1341560..4f2a8173e 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/UserAuthorizerTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/UserAuthorizerTest.java @@ -313,7 +313,7 @@ public void testGetTokenResponseFromAuthCodeExchange_workforceIdentityFederation UserAuthorizer authorizer = UserAuthorizer.newBuilder() .setClientId(CLIENT_ID) - .setScopes(Collections.singletonList("https://www.googleapis.com/auth/cloud-platform")) + .setScopes(Collections.singletonList(OAuth2Utils.CLOUD_PLATFORM_SCOPE)) .setTokenServerUri(WORKFORCE_IDENTITY_FEDERATION_TOKEN_SERVER_URI) .setUserAuthUri(WORKFORCE_IDENTITY_FEDERATION_AUTH_URI) .setClientAuthenticationType(ClientAuthenticationType.CLIENT_SECRET_BASIC) @@ -354,7 +354,7 @@ public void testGetTokenResponseFromAuthCodeExchange_workforceIdentityFederation UserAuthorizer authorizer = UserAuthorizer.newBuilder() .setClientId(CLIENT_ID) - .setScopes(Collections.singletonList("https://www.googleapis.com/auth/cloud-platform")) + .setScopes(Collections.singletonList(OAuth2Utils.CLOUD_PLATFORM_SCOPE)) .setTokenServerUri(WORKFORCE_IDENTITY_FEDERATION_TOKEN_SERVER_URI) .setUserAuthUri(WORKFORCE_IDENTITY_FEDERATION_AUTH_URI) .setClientAuthenticationType(ClientAuthenticationType.NONE) diff --git a/oauth2_http/javatests/com/google/auth/oauth2/functional/FTComputeEngineCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/functional/FTComputeEngineCredentialsTest.java index 03bb42ab0..3d88cff7a 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/functional/FTComputeEngineCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/functional/FTComputeEngineCredentialsTest.java @@ -44,12 +44,12 @@ import com.google.auth.oauth2.IdToken; import com.google.auth.oauth2.IdTokenCredentials; import com.google.auth.oauth2.IdTokenProvider; +import com.google.auth.oauth2.OAuth2Utils; import org.junit.Test; public final class FTComputeEngineCredentialsTest { private final String computeUrl = "https://compute.googleapis.com/compute/v1/projects/gcloud-devel/zones/us-central1-a/instances"; - private final String cloudPlatformScope = "https://www.googleapis.com/auth/cloud-platform"; @Test public void RefreshCredentials() throws Exception { @@ -64,7 +64,7 @@ public void RefreshCredentials() throws Exception { @Test public void DefaultCredentials() throws Exception { final GoogleCredentials defaultCredential = - GoogleCredentials.getApplicationDefault().createScoped(cloudPlatformScope); + GoogleCredentials.getApplicationDefault().createScoped(OAuth2Utils.CLOUD_PLATFORM_SCOPE); AccessToken accessToken = defaultCredential.refreshAccessToken(); assertNotNull(accessToken); diff --git a/oauth2_http/javatests/com/google/auth/oauth2/functional/FTServiceAccountCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/functional/FTServiceAccountCredentialsTest.java index e70db86a2..82a38e3df 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/functional/FTServiceAccountCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/functional/FTServiceAccountCredentialsTest.java @@ -49,13 +49,12 @@ import com.google.auth.oauth2.IdToken; import com.google.auth.oauth2.IdTokenCredentials; import com.google.auth.oauth2.IdTokenProvider; +import com.google.auth.oauth2.OAuth2Utils; import java.io.FileNotFoundException; import java.io.IOException; import org.junit.Test; public final class FTServiceAccountCredentialsTest { - private final String cloudPlatformScope = "https://www.googleapis.com/auth/cloud-platform"; - private final String cloudTasksUrl = "https://cloudtasks.googleapis.com/v2/projects/gcloud-devel/locations"; private final String storageUrl = @@ -108,27 +107,30 @@ public void AudienceSetNoScopeTest() throws Exception { @Test public void ScopeSetNoAudienceStorageTest() throws Exception { - HttpResponse response = executeRequestWithCredentialsWithScope(storageUrl, cloudPlatformScope); + HttpResponse response = + executeRequestWithCredentialsWithScope(storageUrl, OAuth2Utils.CLOUD_PLATFORM_SCOPE); assertEquals(200, response.getStatusCode()); } @Test public void ScopeSetNoAudienceComputeTest() throws Exception { - HttpResponse response = executeRequestWithCredentialsWithScope(computeUrl, cloudPlatformScope); + HttpResponse response = + executeRequestWithCredentialsWithScope(computeUrl, OAuth2Utils.CLOUD_PLATFORM_SCOPE); assertEquals(200, response.getStatusCode()); } @Test public void ScopeSetNoAudienceBigQueryTest() throws Exception { - HttpResponse response = executeRequestWithCredentialsWithScope(bigQueryUrl, cloudPlatformScope); + HttpResponse response = + executeRequestWithCredentialsWithScope(bigQueryUrl, OAuth2Utils.CLOUD_PLATFORM_SCOPE); assertEquals(200, response.getStatusCode()); } @Test public void ScopeSetNoAudienceOnePlatformTest() throws Exception { HttpResponse response = - executeRequestWithCredentialsWithScope(cloudTasksUrl, cloudPlatformScope); + executeRequestWithCredentialsWithScope(cloudTasksUrl, OAuth2Utils.CLOUD_PLATFORM_SCOPE); assertEquals(200, response.getStatusCode()); }