diff --git a/oauth2_http/java/com/google/auth/oauth2/DownscopedCredentials.java b/oauth2_http/java/com/google/auth/oauth2/DownscopedCredentials.java index 43709d779..7232a234d 100644 --- a/oauth2_http/java/com/google/auth/oauth2/DownscopedCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/DownscopedCredentials.java @@ -34,6 +34,7 @@ import static com.google.common.base.MoreObjects.firstNonNull; import static com.google.common.base.Preconditions.checkNotNull; +import com.google.auth.Credentials; import com.google.auth.http.HttpTransportFactory; import com.google.common.annotations.VisibleForTesting; import com.google.errorprone.annotations.CanIgnoreReturnValue; @@ -87,22 +88,45 @@ */ public final class DownscopedCredentials extends OAuth2Credentials { - private static final String TOKEN_EXCHANGE_ENDPOINT = "https://sts.googleapis.com/v1/token"; - + private final String TOKEN_EXCHANGE_URL_FORMAT = "https://sts.{universe_domain}/v1/token"; private final GoogleCredentials sourceCredential; private final CredentialAccessBoundary credentialAccessBoundary; + private final String universeDomain; + private final transient HttpTransportFactory transportFactory; - private DownscopedCredentials( - GoogleCredentials sourceCredential, - CredentialAccessBoundary credentialAccessBoundary, - HttpTransportFactory transportFactory) { + private final String tokenExchangeEndpoint; + + /** Internal constructor. See {@link Builder}. */ + private DownscopedCredentials(Builder builder) { this.transportFactory = firstNonNull( - transportFactory, + builder.transportFactory, getFromServiceLoader(HttpTransportFactory.class, OAuth2Utils.HTTP_TRANSPORT_FACTORY)); - this.sourceCredential = checkNotNull(sourceCredential); - this.credentialAccessBoundary = checkNotNull(credentialAccessBoundary); + this.sourceCredential = checkNotNull(builder.sourceCredential); + this.credentialAccessBoundary = checkNotNull(builder.credentialAccessBoundary); + + // Default to GDU when not supplied. + if (builder.universeDomain == null || builder.universeDomain.trim().isEmpty()) { + this.universeDomain = Credentials.GOOGLE_DEFAULT_UNIVERSE; + } else { + this.universeDomain = builder.universeDomain; + } + + // Ensure source credential's universe domain matches. + try { + if (!this.universeDomain.equals(sourceCredential.getUniverseDomain())) { + throw new IllegalArgumentException( + "The downscoped credential's universe domain must be the same as the source " + + "credential."); + } + } catch (IOException e) { + // Throwing an IOException would be a breaking change, so wrap it here. + throw new IllegalStateException( + "Error occurred when attempting to retrieve source credential universe domain.", e); + } + this.tokenExchangeEndpoint = + TOKEN_EXCHANGE_URL_FORMAT.replace("{universe_domain}", universeDomain); } @Override @@ -122,7 +146,7 @@ public AccessToken refreshAccessToken() throws IOException { StsRequestHandler handler = StsRequestHandler.newBuilder( - TOKEN_EXCHANGE_ENDPOINT, request, transportFactory.create().createRequestFactory()) + tokenExchangeEndpoint, request, transportFactory.create().createRequestFactory()) .setInternalOptions(credentialAccessBoundary.toJson()) .build(); @@ -150,6 +174,17 @@ public CredentialAccessBoundary getCredentialAccessBoundary() { return credentialAccessBoundary; } + /** + * Returns the universe domain for the credential. + * + * @return An explicit universe domain if it was explicitly provided, otherwise the default Google + * universe will be returned. + */ + @Override + public String getUniverseDomain() { + return universeDomain; + } + @VisibleForTesting HttpTransportFactory getTransportFactory() { return transportFactory; @@ -164,31 +199,62 @@ public static class Builder extends OAuth2Credentials.Builder { private GoogleCredentials sourceCredential; private CredentialAccessBoundary credentialAccessBoundary; private HttpTransportFactory transportFactory; + private String universeDomain; private Builder() {} + /** + * Sets the required source credential used to acquire the downscoped credential. + * + * @param sourceCredential the {@code GoogleCredentials} to set + * @return this {@code Builder} object + */ @CanIgnoreReturnValue public Builder setSourceCredential(GoogleCredentials sourceCredential) { this.sourceCredential = sourceCredential; return this; } + /** + * Sets the required credential access boundary which specifies the upper bound of permissions + * that the credential can access. See {@link CredentialAccessBoundary} for more information. + * + * @param credentialAccessBoundary the {@code CredentialAccessBoundary} to set + * @return this {@code Builder} object + */ @CanIgnoreReturnValue public Builder setCredentialAccessBoundary(CredentialAccessBoundary credentialAccessBoundary) { this.credentialAccessBoundary = credentialAccessBoundary; return this; } + /** + * Sets the HTTP transport factory. + * + * @param transportFactory the {@code HttpTransportFactory} to set + * @return this {@code Builder} object + */ @CanIgnoreReturnValue public Builder setHttpTransportFactory(HttpTransportFactory transportFactory) { this.transportFactory = transportFactory; return this; } + /** + * Sets the optional universe domain. + * + * @param universeDomain the universe domain to set + * @return this {@code Builder} object + */ + @CanIgnoreReturnValue + public Builder setUniverseDomain(String universeDomain) { + this.universeDomain = universeDomain; + return this; + } + @Override public DownscopedCredentials build() { - return new DownscopedCredentials( - sourceCredential, credentialAccessBoundary, transportFactory); + return new DownscopedCredentials(this); } } } diff --git a/oauth2_http/java/com/google/auth/oauth2/ExternalAccountAuthorizedUserCredentials.java b/oauth2_http/java/com/google/auth/oauth2/ExternalAccountAuthorizedUserCredentials.java index d01623a62..1e811caa4 100644 --- a/oauth2_http/java/com/google/auth/oauth2/ExternalAccountAuthorizedUserCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/ExternalAccountAuthorizedUserCredentials.java @@ -317,6 +317,7 @@ static ExternalAccountAuthorizedUserCredentials fromJson( String clientId = (String) json.get("client_id"); String clientSecret = (String) json.get("client_secret"); String quotaProjectId = (String) json.get("quota_project_id"); + String universeDomain = (String) json.get("universe_domain"); return ExternalAccountAuthorizedUserCredentials.newBuilder() .setAudience(audience) @@ -329,6 +330,7 @@ static ExternalAccountAuthorizedUserCredentials fromJson( .setRefreshToken(refreshToken) .setHttpTransportFactory(transportFactory) .setQuotaProjectId(quotaProjectId) + .setUniverseDomain(universeDomain) .build(); } @@ -522,6 +524,19 @@ public Builder setAccessToken(AccessToken accessToken) { return this; } + /** + * Sets the optional universe domain. The Google Default Universe is used when not provided. + * + * @param universeDomain the universe domain to set + * @return this {@code Builder} object + */ + @CanIgnoreReturnValue + @Override + public Builder setUniverseDomain(String universeDomain) { + super.setUniverseDomain(universeDomain); + return this; + } + @Override public ExternalAccountAuthorizedUserCredentials build() { return new ExternalAccountAuthorizedUserCredentials(this); diff --git a/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java index 6a70e3138..94dbf5845 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java @@ -31,6 +31,7 @@ package com.google.auth.oauth2; +import static com.google.auth.Credentials.GOOGLE_DEFAULT_UNIVERSE; import static org.junit.Assert.*; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -964,7 +965,44 @@ public void shouldUseMetadataServer_noEnvironmentVars() { } @Test - public void builder() { + public void builder_allFields() throws IOException { + List scopes = Arrays.asList("scope1", "scope2"); + + AwsCredentials credentials = + (AwsCredentials) + AwsCredentials.newBuilder() + .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) + .setAudience("audience") + .setSubjectTokenType("subjectTokenType") + .setTokenUrl(STS_URL) + .setTokenInfoUrl("tokenInfoUrl") + .setCredentialSource(AWS_CREDENTIAL_SOURCE) + .setTokenInfoUrl("tokenInfoUrl") + .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) + .setQuotaProjectId("quotaProjectId") + .setClientId("clientId") + .setClientSecret("clientSecret") + .setScopes(scopes) + .setUniverseDomain("universeDomain") + .build(); + + assertEquals("audience", credentials.getAudience()); + assertEquals("subjectTokenType", credentials.getSubjectTokenType()); + assertEquals(STS_URL, credentials.getTokenUrl()); + assertEquals("tokenInfoUrl", credentials.getTokenInfoUrl()); + assertEquals( + SERVICE_ACCOUNT_IMPERSONATION_URL, credentials.getServiceAccountImpersonationUrl()); + assertEquals(AWS_CREDENTIAL_SOURCE, credentials.getCredentialSource()); + assertEquals("quotaProjectId", credentials.getQuotaProjectId()); + assertEquals("clientId", credentials.getClientId()); + assertEquals("clientSecret", credentials.getClientSecret()); + assertEquals(scopes, credentials.getScopes()); + assertEquals(SystemEnvironmentProvider.getInstance(), credentials.getEnvironmentProvider()); + assertEquals("universeDomain", credentials.getUniverseDomain()); + } + + @Test + public void builder_missingUniverseDomain_defaults() throws IOException { List scopes = Arrays.asList("scope1", "scope2"); AwsCredentials credentials = @@ -986,16 +1024,94 @@ public void builder() { assertEquals("audience", credentials.getAudience()); assertEquals("subjectTokenType", credentials.getSubjectTokenType()); - assertEquals(credentials.getTokenUrl(), STS_URL); - assertEquals(credentials.getTokenInfoUrl(), "tokenInfoUrl"); + assertEquals(STS_URL, credentials.getTokenUrl()); + assertEquals("tokenInfoUrl", credentials.getTokenInfoUrl()); assertEquals( - credentials.getServiceAccountImpersonationUrl(), SERVICE_ACCOUNT_IMPERSONATION_URL); - assertEquals(credentials.getCredentialSource(), AWS_CREDENTIAL_SOURCE); - assertEquals(credentials.getQuotaProjectId(), "quotaProjectId"); - assertEquals(credentials.getClientId(), "clientId"); - assertEquals(credentials.getClientSecret(), "clientSecret"); - assertEquals(credentials.getScopes(), scopes); - assertEquals(credentials.getEnvironmentProvider(), SystemEnvironmentProvider.getInstance()); + SERVICE_ACCOUNT_IMPERSONATION_URL, credentials.getServiceAccountImpersonationUrl()); + assertEquals(AWS_CREDENTIAL_SOURCE, credentials.getCredentialSource()); + assertEquals("quotaProjectId", credentials.getQuotaProjectId()); + assertEquals("clientId", credentials.getClientId()); + assertEquals("clientSecret", credentials.getClientSecret()); + assertEquals(scopes, credentials.getScopes()); + assertEquals(SystemEnvironmentProvider.getInstance(), credentials.getEnvironmentProvider()); + assertEquals(GOOGLE_DEFAULT_UNIVERSE, credentials.getUniverseDomain()); + } + + @Test + public void newBuilder_allFields() throws IOException { + List scopes = Arrays.asList("scope1", "scope2"); + + AwsCredentials credentials = + (AwsCredentials) + AwsCredentials.newBuilder() + .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) + .setAudience("audience") + .setSubjectTokenType("subjectTokenType") + .setTokenUrl(STS_URL) + .setTokenInfoUrl("tokenInfoUrl") + .setCredentialSource(AWS_CREDENTIAL_SOURCE) + .setTokenInfoUrl("tokenInfoUrl") + .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) + .setQuotaProjectId("quotaProjectId") + .setClientId("clientId") + .setClientSecret("clientSecret") + .setScopes(scopes) + .setUniverseDomain("universeDomain") + .build(); + + AwsCredentials newBuilderCreds = AwsCredentials.newBuilder(credentials).build(); + assertEquals(credentials.getAudience(), newBuilderCreds.getAudience()); + assertEquals(credentials.getSubjectTokenType(), newBuilderCreds.getSubjectTokenType()); + assertEquals(credentials.getTokenUrl(), newBuilderCreds.getTokenUrl()); + assertEquals(credentials.getTokenInfoUrl(), newBuilderCreds.getTokenInfoUrl()); + assertEquals( + credentials.getServiceAccountImpersonationUrl(), + newBuilderCreds.getServiceAccountImpersonationUrl()); + assertEquals(credentials.getCredentialSource(), newBuilderCreds.getCredentialSource()); + assertEquals(credentials.getQuotaProjectId(), newBuilderCreds.getQuotaProjectId()); + assertEquals(credentials.getClientId(), newBuilderCreds.getClientId()); + assertEquals(credentials.getClientSecret(), newBuilderCreds.getClientSecret()); + assertEquals(credentials.getScopes(), newBuilderCreds.getScopes()); + assertEquals(credentials.getEnvironmentProvider(), newBuilderCreds.getEnvironmentProvider()); + assertEquals(credentials.getUniverseDomain(), newBuilderCreds.getUniverseDomain()); + } + + @Test + public void newBuilder_noUniverseDomain_defaults() throws IOException { + List scopes = Arrays.asList("scope1", "scope2"); + + AwsCredentials credentials = + (AwsCredentials) + AwsCredentials.newBuilder() + .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) + .setAudience("audience") + .setSubjectTokenType("subjectTokenType") + .setTokenUrl(STS_URL) + .setTokenInfoUrl("tokenInfoUrl") + .setCredentialSource(AWS_CREDENTIAL_SOURCE) + .setTokenInfoUrl("tokenInfoUrl") + .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) + .setQuotaProjectId("quotaProjectId") + .setClientId("clientId") + .setClientSecret("clientSecret") + .setScopes(scopes) + .build(); + + AwsCredentials newBuilderCreds = AwsCredentials.newBuilder(credentials).build(); + assertEquals(credentials.getAudience(), newBuilderCreds.getAudience()); + assertEquals(credentials.getSubjectTokenType(), newBuilderCreds.getSubjectTokenType()); + assertEquals(credentials.getTokenUrl(), newBuilderCreds.getTokenUrl()); + assertEquals(credentials.getTokenInfoUrl(), newBuilderCreds.getTokenInfoUrl()); + assertEquals( + credentials.getServiceAccountImpersonationUrl(), + newBuilderCreds.getServiceAccountImpersonationUrl()); + assertEquals(credentials.getCredentialSource(), newBuilderCreds.getCredentialSource()); + assertEquals(credentials.getQuotaProjectId(), newBuilderCreds.getQuotaProjectId()); + assertEquals(credentials.getClientId(), newBuilderCreds.getClientId()); + assertEquals(credentials.getClientSecret(), newBuilderCreds.getClientSecret()); + assertEquals(credentials.getScopes(), newBuilderCreds.getScopes()); + assertEquals(credentials.getEnvironmentProvider(), newBuilderCreds.getEnvironmentProvider()); + assertEquals(GOOGLE_DEFAULT_UNIVERSE, newBuilderCreds.getUniverseDomain()); } @Test diff --git a/oauth2_http/javatests/com/google/auth/oauth2/DownscopedCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/DownscopedCredentialsTest.java index d7cade9f6..06fb13ebe 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/DownscopedCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/DownscopedCredentialsTest.java @@ -31,6 +31,7 @@ package com.google.auth.oauth2; +import static com.google.auth.Credentials.GOOGLE_DEFAULT_UNIVERSE; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; @@ -49,6 +50,7 @@ @RunWith(JUnit4.class) public class DownscopedCredentialsTest { + private final String TOKEN_EXCHANGE_URL_FORMAT = "https://sts.%s/v1/token"; private static final String SA_PRIVATE_KEY_PKCS8 = "-----BEGIN PRIVATE KEY-----\n" + "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALX0PQoe1igW12i" @@ -107,6 +109,45 @@ public void refreshAccessToken() throws IOException { assertEquals(CREDENTIAL_ACCESS_BOUNDARY.toJson(), query.get("options")); assertEquals( "urn:ietf:params:oauth:token-type:access_token", query.get("requested_token_type")); + + // Verify domain. + String url = transportFactory.transport.getRequest().getUrl(); + assertEquals(url, String.format(TOKEN_EXCHANGE_URL_FORMAT, GOOGLE_DEFAULT_UNIVERSE)); + } + + @Test + public void refreshAccessToken_withCustomUniverseDomain() throws IOException { + MockStsTransportFactory transportFactory = new MockStsTransportFactory(); + String universeDomain = "foobar"; + GoogleCredentials sourceCredentials = + getServiceAccountSourceCredentials(/* canRefresh= */ true) + .toBuilder() + .setUniverseDomain(universeDomain) + .build(); + + DownscopedCredentials downscopedCredentials = + DownscopedCredentials.newBuilder() + .setSourceCredential(sourceCredentials) + .setCredentialAccessBoundary(CREDENTIAL_ACCESS_BOUNDARY) + .setHttpTransportFactory(transportFactory) + .setUniverseDomain(universeDomain) + .build(); + + AccessToken accessToken = downscopedCredentials.refreshAccessToken(); + + assertEquals(transportFactory.transport.getAccessToken(), accessToken.getTokenValue()); + + // Validate CAB specific params. + Map query = + TestUtils.parseQuery(transportFactory.transport.getRequest().getContentAsString()); + assertNotNull(query.get("options")); + assertEquals(CREDENTIAL_ACCESS_BOUNDARY.toJson(), query.get("options")); + assertEquals( + "urn:ietf:params:oauth:token-type:access_token", query.get("requested_token_type")); + + // Verify domain. + String url = transportFactory.transport.getRequest().getUrl(); + assertEquals(url, String.format(TOKEN_EXCHANGE_URL_FORMAT, universeDomain)); } @Test @@ -202,6 +243,63 @@ public void builder_noTransport_defaults() throws IOException { assertEquals(OAuth2Utils.HTTP_TRANSPORT_FACTORY, credentials.getTransportFactory()); } + @Test + public void builder_noUniverseDomain_defaults() throws IOException { + GoogleCredentials sourceCredentials = + getServiceAccountSourceCredentials(/* canRefresh= */ true); + DownscopedCredentials credentials = + DownscopedCredentials.newBuilder() + .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) + .setSourceCredential(sourceCredentials) + .setCredentialAccessBoundary(CREDENTIAL_ACCESS_BOUNDARY) + .build(); + + GoogleCredentials scopedSourceCredentials = + sourceCredentials.createScoped("https://www.googleapis.com/auth/cloud-platform"); + assertEquals(OAuth2Utils.HTTP_TRANSPORT_FACTORY, credentials.getTransportFactory()); + assertEquals(scopedSourceCredentials, credentials.getSourceCredentials()); + assertEquals(CREDENTIAL_ACCESS_BOUNDARY, credentials.getCredentialAccessBoundary()); + assertEquals(GOOGLE_DEFAULT_UNIVERSE, credentials.getUniverseDomain()); + } + + @Test + public void builder_universeDomainMismatch_throws() throws IOException { + GoogleCredentials sourceCredentials = + getServiceAccountSourceCredentials(/* canRefresh= */ true); + + try { + DownscopedCredentials.newBuilder() + .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) + .setSourceCredential(sourceCredentials) + .setCredentialAccessBoundary(CREDENTIAL_ACCESS_BOUNDARY) + .setUniverseDomain("differentUniverseDomain") + .build(); + fail("Should fail with universe domain mismatch."); + } catch (IllegalArgumentException e) { + assertEquals( + "The downscoped credential's universe domain must be the same as the source credential.", + e.getMessage()); + } + } + + @Test + public void builder_sourceUniverseDomainUnavailable_throws() throws IOException { + GoogleCredentials sourceCredentials = new MockSourceCredentialWithoutUniverseDomain(); + + try { + DownscopedCredentials.newBuilder() + .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) + .setSourceCredential(sourceCredentials) + .setCredentialAccessBoundary(CREDENTIAL_ACCESS_BOUNDARY) + .build(); + fail("Should fail to retrieve source credential universe domain."); + } catch (IllegalStateException e) { + assertEquals( + "Error occurred when attempting to retrieve source credential universe domain.", + e.getMessage()); + } + } + private static GoogleCredentials getServiceAccountSourceCredentials(boolean canRefresh) throws IOException { MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); @@ -239,4 +337,11 @@ private static GoogleCredentials getUserSourceCredentials() { .setHttpTransportFactory(transportFactory) .build(); } + + static class MockSourceCredentialWithoutUniverseDomain extends GoogleCredentials { + @Override + public String getUniverseDomain() throws IOException { + throw new IOException(); + } + } } diff --git a/oauth2_http/javatests/com/google/auth/oauth2/ExternalAccountAuthorizedUserCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/ExternalAccountAuthorizedUserCredentialsTest.java index 2fb89dfdc..75296957f 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/ExternalAccountAuthorizedUserCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/ExternalAccountAuthorizedUserCredentialsTest.java @@ -31,6 +31,7 @@ package com.google.auth.oauth2; +import static com.google.auth.Credentials.GOOGLE_DEFAULT_UNIVERSE; import static com.google.auth.oauth2.ExternalAccountAuthorizedUserCredentials.EXTERNAL_ACCOUNT_AUTHORIZED_USER_FILE_TYPE; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -79,6 +80,8 @@ public class ExternalAccountAuthorizedUserCredentialsTest extends BaseSerializat private static final String TOKEN_INFO_URL = "https://sts.googleapis.com/v1/introspect"; private static final String REVOKE_URL = "https://sts.googleapis.com/v1/revoke"; private static final String QUOTA_PROJECT = "sample-quota-project-id"; + private static final String UNIVERSE_DOMAIN = "foo.bar"; + private static final String BASIC_AUTH = String.format( "Basic %s", @@ -109,7 +112,7 @@ public void setup() { } @Test - public void builder_allFields() { + public void builder_allFields() throws IOException { ExternalAccountAuthorizedUserCredentials credentials = ExternalAccountAuthorizedUserCredentials.newBuilder() .setAudience(AUDIENCE) @@ -121,6 +124,7 @@ public void builder_allFields() { .setRevokeUrl(REVOKE_URL) .setAccessToken(new AccessToken(ACCESS_TOKEN, /* expirationTime= */ null)) .setQuotaProjectId(QUOTA_PROJECT) + .setUniverseDomain(UNIVERSE_DOMAIN) .build(); assertEquals(AUDIENCE, credentials.getAudience()); @@ -132,6 +136,7 @@ public void builder_allFields() { assertEquals(REVOKE_URL, credentials.getRevokeUrl()); assertEquals(ACCESS_TOKEN, credentials.getAccessToken().getTokenValue()); assertEquals(QUOTA_PROJECT, credentials.getQuotaProjectId()); + assertEquals(UNIVERSE_DOMAIN, credentials.getUniverseDomain()); } @Test @@ -289,7 +294,34 @@ public void builder_missingClientSecret_throws() { } @Test - public void toBuilder() { + public void builder_missingUniverseDomain_defaults() throws IOException { + ExternalAccountAuthorizedUserCredentials credentials = + ExternalAccountAuthorizedUserCredentials.newBuilder() + .setAudience(AUDIENCE) + .setClientId(CLIENT_ID) + .setClientSecret(CLIENT_SECRET) + .setRefreshToken(REFRESH_TOKEN) + .setTokenUrl(TOKEN_URL) + .setTokenInfoUrl(TOKEN_INFO_URL) + .setRevokeUrl(REVOKE_URL) + .setAccessToken(new AccessToken(ACCESS_TOKEN, /* expirationTime= */ null)) + .setQuotaProjectId(QUOTA_PROJECT) + .build(); + + assertEquals(AUDIENCE, credentials.getAudience()); + assertEquals(CLIENT_ID, credentials.getClientId()); + assertEquals(CLIENT_SECRET, credentials.getClientSecret()); + assertEquals(REFRESH_TOKEN, credentials.getRefreshToken()); + assertEquals(TOKEN_URL, credentials.getTokenUrl()); + assertEquals(TOKEN_INFO_URL, credentials.getTokenInfoUrl()); + assertEquals(REVOKE_URL, credentials.getRevokeUrl()); + assertEquals(ACCESS_TOKEN, credentials.getAccessToken().getTokenValue()); + assertEquals(QUOTA_PROJECT, credentials.getQuotaProjectId()); + assertEquals(GOOGLE_DEFAULT_UNIVERSE, credentials.getUniverseDomain()); + } + + @Test + public void toBuilder_allFields() { ExternalAccountAuthorizedUserCredentials credentials = ExternalAccountAuthorizedUserCredentials.newBuilder() .setAudience(AUDIENCE) @@ -301,6 +333,7 @@ public void toBuilder() { .setRevokeUrl(REVOKE_URL) .setAccessToken(new AccessToken(ACCESS_TOKEN, new Date())) .setQuotaProjectId(QUOTA_PROJECT) + .setUniverseDomain(UNIVERSE_DOMAIN) .build(); ExternalAccountAuthorizedUserCredentials secondCredentials = credentials.toBuilder().build(); @@ -308,6 +341,27 @@ public void toBuilder() { assertEquals(credentials, secondCredentials); } + @Test + public void toBuilder_missingUniverseDomain_defaults() throws IOException { + ExternalAccountAuthorizedUserCredentials credentials = + ExternalAccountAuthorizedUserCredentials.newBuilder() + .setAudience(AUDIENCE) + .setClientId(CLIENT_ID) + .setClientSecret(CLIENT_SECRET) + .setRefreshToken(REFRESH_TOKEN) + .setTokenUrl(TOKEN_URL) + .setTokenInfoUrl(TOKEN_INFO_URL) + .setRevokeUrl(REVOKE_URL) + .setAccessToken(new AccessToken(ACCESS_TOKEN, new Date())) + .setQuotaProjectId(QUOTA_PROJECT) + .build(); + + ExternalAccountAuthorizedUserCredentials secondCredentials = credentials.toBuilder().build(); + + assertEquals(credentials, secondCredentials); + assertEquals(GOOGLE_DEFAULT_UNIVERSE, secondCredentials.getUniverseDomain()); + } + @Test public void fromJson_allFields() throws IOException { ExternalAccountAuthorizedUserCredentials credentials = @@ -322,6 +376,7 @@ public void fromJson_allFields() throws IOException { assertEquals(TOKEN_INFO_URL, credentials.getTokenInfoUrl()); assertEquals(REVOKE_URL, credentials.getRevokeUrl()); assertEquals(QUOTA_PROJECT, credentials.getQuotaProjectId()); + assertEquals(UNIVERSE_DOMAIN, credentials.getUniverseDomain()); } @Test @@ -427,6 +482,25 @@ public void fromJson_missingClientSecret_throws() throws IOException { } } + @Test + public void fromJson_missingUniverseDomain_defaults() throws IOException { + GenericJson json = buildJsonCredentials(); + json.remove("universe_domain"); + + ExternalAccountAuthorizedUserCredentials credentials = + ExternalAccountAuthorizedUserCredentials.fromJson(json, OAuth2Utils.HTTP_TRANSPORT_FACTORY); + + assertEquals(AUDIENCE, credentials.getAudience()); + assertEquals(CLIENT_ID, credentials.getClientId()); + assertEquals(CLIENT_SECRET, credentials.getClientSecret()); + assertEquals(REFRESH_TOKEN, credentials.getRefreshToken()); + assertEquals(TOKEN_URL, credentials.getTokenUrl()); + assertEquals(TOKEN_INFO_URL, credentials.getTokenInfoUrl()); + assertEquals(REVOKE_URL, credentials.getRevokeUrl()); + assertEquals(QUOTA_PROJECT, credentials.getQuotaProjectId()); + assertEquals(GOOGLE_DEFAULT_UNIVERSE, credentials.getUniverseDomain()); + } + @Test public void fromStream_allFields() throws IOException { GenericJson json = buildJsonCredentials(); @@ -546,6 +620,25 @@ public void fromStream_missingClientSecret_throws() throws IOException { } } + @Test + public void fromStream_missingUniverseDomain_defaults() throws IOException { + GenericJson json = buildJsonCredentials(); + json.remove("universe_domain"); + + ExternalAccountAuthorizedUserCredentials credentials = + ExternalAccountAuthorizedUserCredentials.fromStream(TestUtils.jsonToInputStream(json)); + + assertEquals(AUDIENCE, credentials.getAudience()); + assertEquals(CLIENT_ID, credentials.getClientId()); + assertEquals(CLIENT_SECRET, credentials.getClientSecret()); + assertEquals(REFRESH_TOKEN, credentials.getRefreshToken()); + assertEquals(TOKEN_URL, credentials.getTokenUrl()); + assertEquals(TOKEN_INFO_URL, credentials.getTokenInfoUrl()); + assertEquals(REVOKE_URL, credentials.getRevokeUrl()); + assertEquals(QUOTA_PROJECT, credentials.getQuotaProjectId()); + assertEquals(GOOGLE_DEFAULT_UNIVERSE, credentials.getUniverseDomain()); + } + @Test public void fromStream_invalidInputStream_throws() throws IOException { try { @@ -1038,6 +1131,31 @@ public void equals_differentTransportFactory() { assertNotEquals(credentials.hashCode(), secondCredentials.hashCode()); } + @Test + public void equals_differentUniverseDomain() { + ExternalAccountAuthorizedUserCredentials credentials = + ExternalAccountAuthorizedUserCredentials.newBuilder() + .setAudience(AUDIENCE) + .setClientId(CLIENT_ID) + .setClientSecret(CLIENT_SECRET) + .setRefreshToken(REFRESH_TOKEN) + .setTokenUrl(TOKEN_URL) + .setTokenInfoUrl(TOKEN_INFO_URL) + .setRevokeUrl(REVOKE_URL) + .setAccessToken(new AccessToken(ACCESS_TOKEN, /* expirationTime= */ null)) + .setQuotaProjectId(QUOTA_PROJECT) + .setHttpTransportFactory(transportFactory) + .setUniverseDomain(UNIVERSE_DOMAIN) + .build(); + + ExternalAccountAuthorizedUserCredentials secondCredentials = + credentials.toBuilder().setUniverseDomain("different").build(); + + assertNotEquals(secondCredentials, credentials); + assertNotEquals(credentials, secondCredentials); + assertNotEquals(credentials.hashCode(), secondCredentials.hashCode()); + } + @Test public void toString_expectedFormat() { AccessToken accessToken = new AccessToken(ACCESS_TOKEN, new Date()); @@ -1111,6 +1229,7 @@ static GenericJson buildJsonCredentials() { json.put("token_info_url", TOKEN_INFO_URL); json.put("revoke_url", REVOKE_URL); json.put("quota_project_id", QUOTA_PROJECT); + json.put("universe_domain", UNIVERSE_DOMAIN); return json; } diff --git a/oauth2_http/javatests/com/google/auth/oauth2/GoogleCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/GoogleCredentialsTest.java index 07ca6c704..0de777172 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/GoogleCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/GoogleCredentialsTest.java @@ -584,9 +584,10 @@ public void fromStream_externalAccountAuthorizedUserCredentials_defaultUniverse( throws IOException { MockExternalAccountAuthorizedUserCredentialsTransportFactory transportFactory = new MockExternalAccountAuthorizedUserCredentialsTransportFactory(); - InputStream stream = - TestUtils.jsonToInputStream( - ExternalAccountAuthorizedUserCredentialsTest.buildJsonCredentials()); + + GenericJson json = ExternalAccountAuthorizedUserCredentialsTest.buildJsonCredentials(); + json.remove("universe_domain"); + InputStream stream = TestUtils.jsonToInputStream(json); GoogleCredentials credentials = GoogleCredentials.fromStream(stream, transportFactory); diff --git a/oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java index 7bb64ab1e..26d99b89c 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java @@ -31,6 +31,7 @@ package com.google.auth.oauth2; +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 org.junit.Assert.*; @@ -672,7 +673,7 @@ public void identityPoolCredentialSource_subjectTokenFieldNameUnset() { } @Test - public void builder() { + public void builder_allFields() throws IOException { List scopes = Arrays.asList("scope1", "scope2"); IdentityPoolCredentials credentials = @@ -689,20 +690,22 @@ public void builder() { .setClientId("clientId") .setClientSecret("clientSecret") .setScopes(scopes) + .setUniverseDomain("universeDomain") .build(); assertEquals("audience", credentials.getAudience()); assertEquals("subjectTokenType", credentials.getSubjectTokenType()); - assertEquals(credentials.getTokenUrl(), STS_URL); - assertEquals(credentials.getTokenInfoUrl(), "tokenInfoUrl"); + assertEquals(STS_URL, credentials.getTokenUrl()); + assertEquals("tokenInfoUrl", credentials.getTokenInfoUrl()); assertEquals( - credentials.getServiceAccountImpersonationUrl(), SERVICE_ACCOUNT_IMPERSONATION_URL); - assertEquals(credentials.getCredentialSource(), FILE_CREDENTIAL_SOURCE); - assertEquals(credentials.getQuotaProjectId(), "quotaProjectId"); - assertEquals(credentials.getClientId(), "clientId"); - assertEquals(credentials.getClientSecret(), "clientSecret"); - assertEquals(credentials.getScopes(), scopes); - assertEquals(credentials.getEnvironmentProvider(), SystemEnvironmentProvider.getInstance()); + SERVICE_ACCOUNT_IMPERSONATION_URL, credentials.getServiceAccountImpersonationUrl()); + assertEquals(FILE_CREDENTIAL_SOURCE, credentials.getCredentialSource()); + assertEquals("quotaProjectId", credentials.getQuotaProjectId()); + assertEquals("clientId", credentials.getClientId()); + assertEquals("clientSecret", credentials.getClientSecret()); + assertEquals(scopes, credentials.getScopes()); + assertEquals(SystemEnvironmentProvider.getInstance(), credentials.getEnvironmentProvider()); + assertEquals("universeDomain", credentials.getUniverseDomain()); } @Test @@ -760,6 +763,126 @@ public void builder_emptyWorkforceUserProjectWithWorkforceAudience() { assertTrue(credentials.isWorkforcePoolConfiguration()); } + @Test + public void builder_missingUniverseDomain_defaults() throws IOException { + List scopes = Arrays.asList("scope1", "scope2"); + + IdentityPoolCredentials credentials = + (IdentityPoolCredentials) + IdentityPoolCredentials.newBuilder() + .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) + .setAudience("audience") + .setSubjectTokenType("subjectTokenType") + .setTokenUrl(STS_URL) + .setTokenInfoUrl("tokenInfoUrl") + .setCredentialSource(FILE_CREDENTIAL_SOURCE) + .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) + .setQuotaProjectId("quotaProjectId") + .setClientId("clientId") + .setClientSecret("clientSecret") + .setScopes(scopes) + .build(); + + assertEquals("audience", credentials.getAudience()); + assertEquals("subjectTokenType", credentials.getSubjectTokenType()); + assertEquals(STS_URL, credentials.getTokenUrl()); + assertEquals("tokenInfoUrl", credentials.getTokenInfoUrl()); + assertEquals( + SERVICE_ACCOUNT_IMPERSONATION_URL, credentials.getServiceAccountImpersonationUrl()); + assertEquals(FILE_CREDENTIAL_SOURCE, credentials.getCredentialSource()); + assertEquals("quotaProjectId", credentials.getQuotaProjectId()); + assertEquals("clientId", credentials.getClientId()); + assertEquals("clientSecret", credentials.getClientSecret()); + assertEquals(scopes, credentials.getScopes()); + assertEquals(SystemEnvironmentProvider.getInstance(), credentials.getEnvironmentProvider()); + assertEquals(GOOGLE_DEFAULT_UNIVERSE, credentials.getUniverseDomain()); + } + + @Test + public void newBuilder_allFields() throws IOException { + List scopes = Arrays.asList("scope1", "scope2"); + + IdentityPoolCredentials credentials = + (IdentityPoolCredentials) + IdentityPoolCredentials.newBuilder() + .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) + .setAudience( + "//iam.googleapis.com/locations/global/workforcePools/pool/providers/provider") + .setSubjectTokenType("subjectTokenType") + .setTokenUrl(STS_URL) + .setTokenInfoUrl("tokenInfoUrl") + .setCredentialSource(FILE_CREDENTIAL_SOURCE) + .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) + .setQuotaProjectId("quotaProjectId") + .setClientId("clientId") + .setClientSecret("clientSecret") + .setScopes(scopes) + .setWorkforcePoolUserProject("workforcePoolUserProject") + .setUniverseDomain("universeDomain") + .build(); + + IdentityPoolCredentials newBuilderCreds = + IdentityPoolCredentials.newBuilder(credentials).build(); + assertEquals(credentials.getAudience(), newBuilderCreds.getAudience()); + assertEquals(credentials.getSubjectTokenType(), newBuilderCreds.getSubjectTokenType()); + assertEquals(credentials.getTokenUrl(), newBuilderCreds.getTokenUrl()); + assertEquals(credentials.getTokenInfoUrl(), newBuilderCreds.getTokenInfoUrl()); + assertEquals( + credentials.getServiceAccountImpersonationUrl(), + newBuilderCreds.getServiceAccountImpersonationUrl()); + assertEquals(credentials.getCredentialSource(), newBuilderCreds.getCredentialSource()); + assertEquals(credentials.getQuotaProjectId(), newBuilderCreds.getQuotaProjectId()); + assertEquals(credentials.getClientId(), newBuilderCreds.getClientId()); + assertEquals(credentials.getClientSecret(), newBuilderCreds.getClientSecret()); + assertEquals(credentials.getScopes(), newBuilderCreds.getScopes()); + assertEquals(credentials.getEnvironmentProvider(), newBuilderCreds.getEnvironmentProvider()); + assertEquals( + credentials.getWorkforcePoolUserProject(), newBuilderCreds.getWorkforcePoolUserProject()); + assertEquals(credentials.getUniverseDomain(), newBuilderCreds.getUniverseDomain()); + } + + @Test + public void newBuilder_noUniverseDomain_defaults() throws IOException { + List scopes = Arrays.asList("scope1", "scope2"); + + IdentityPoolCredentials credentials = + (IdentityPoolCredentials) + IdentityPoolCredentials.newBuilder() + .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) + .setAudience( + "//iam.googleapis.com/locations/global/workforcePools/pool/providers/provider") + .setSubjectTokenType("subjectTokenType") + .setTokenUrl(STS_URL) + .setTokenInfoUrl("tokenInfoUrl") + .setCredentialSource(FILE_CREDENTIAL_SOURCE) + .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) + .setQuotaProjectId("quotaProjectId") + .setClientId("clientId") + .setClientSecret("clientSecret") + .setScopes(scopes) + .setWorkforcePoolUserProject("workforcePoolUserProject") + .build(); + + IdentityPoolCredentials newBuilderCreds = + IdentityPoolCredentials.newBuilder(credentials).build(); + assertEquals(credentials.getAudience(), newBuilderCreds.getAudience()); + assertEquals(credentials.getSubjectTokenType(), newBuilderCreds.getSubjectTokenType()); + assertEquals(credentials.getTokenUrl(), newBuilderCreds.getTokenUrl()); + assertEquals(credentials.getTokenInfoUrl(), newBuilderCreds.getTokenInfoUrl()); + assertEquals( + credentials.getServiceAccountImpersonationUrl(), + newBuilderCreds.getServiceAccountImpersonationUrl()); + assertEquals(credentials.getCredentialSource(), newBuilderCreds.getCredentialSource()); + assertEquals(credentials.getQuotaProjectId(), newBuilderCreds.getQuotaProjectId()); + assertEquals(credentials.getClientId(), newBuilderCreds.getClientId()); + assertEquals(credentials.getClientSecret(), newBuilderCreds.getClientSecret()); + assertEquals(credentials.getScopes(), newBuilderCreds.getScopes()); + assertEquals(credentials.getEnvironmentProvider(), newBuilderCreds.getEnvironmentProvider()); + assertEquals( + credentials.getWorkforcePoolUserProject(), newBuilderCreds.getWorkforcePoolUserProject()); + assertEquals(GOOGLE_DEFAULT_UNIVERSE, newBuilderCreds.getUniverseDomain()); + } + @Test public void serialize() throws IOException, ClassNotFoundException { IdentityPoolCredentials testCredentials = diff --git a/oauth2_http/javatests/com/google/auth/oauth2/MockStsTransport.java b/oauth2_http/javatests/com/google/auth/oauth2/MockStsTransport.java index 8251be13c..14ff35800 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/MockStsTransport.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/MockStsTransport.java @@ -51,6 +51,8 @@ import java.util.List; import java.util.Map; import java.util.Queue; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** Transport that mocks a basic STS endpoint. */ public final class MockStsTransport extends MockHttpTransport { @@ -58,8 +60,8 @@ public final class MockStsTransport extends MockHttpTransport { private static final String EXPECTED_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:token-exchange"; private static final String ISSUED_TOKEN_TYPE = "urn:ietf:params:oauth:token-type:access_token"; - private static final String STS_URL = "https://sts.googleapis.com/v1/token"; - private static final String STS_OAUTHTOKEN_URL = "https://sts.googleapis.com/v1/oauthtoken"; + private static final String VALID_STS_PATTERN = + "https:\\/\\/sts.[a-z-_\\.]+\\/v1\\/(token|oauthtoken)"; private static final String ACCESS_TOKEN = "accessToken"; private static final String TOKEN_TYPE = "Bearer"; private static final Long EXPIRES_IN = 3600L; @@ -89,7 +91,9 @@ public LowLevelHttpRequest buildRequest(final String method, final String url) { new MockLowLevelHttpRequest(url) { @Override public LowLevelHttpResponse execute() throws IOException { - if (!STS_URL.equals(url) && !STS_OAUTHTOKEN_URL.equals(url)) { + // Environment version is prefixed by "aws". e.g. "aws1". + Matcher matcher = Pattern.compile(VALID_STS_PATTERN).matcher(url); + if (!matcher.matches()) { return makeErrorResponse(); } @@ -101,7 +105,7 @@ public LowLevelHttpResponse execute() throws IOException { response.setFactory(new GsonFactory()); Map query = TestUtils.parseQuery(getContentAsString()); - if (STS_URL.equals(url)) { + if (!url.contains("v1/oauthtoken")) { assertEquals(EXPECTED_GRANT_TYPE, query.get("grant_type")); assertNotNull(query.get("subject_token_type")); assertNotNull(query.get("subject_token")); diff --git a/oauth2_http/javatests/com/google/auth/oauth2/PluggableAuthCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/PluggableAuthCredentialsTest.java index 3ffa2c24f..a63801c0a 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/PluggableAuthCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/PluggableAuthCredentialsTest.java @@ -31,6 +31,7 @@ package com.google.auth.oauth2; +import static com.google.auth.Credentials.GOOGLE_DEFAULT_UNIVERSE; import static com.google.auth.oauth2.MockExternalAccountCredentialsTransport.SERVICE_ACCOUNT_IMPERSONATION_URL; import static org.junit.Assert.*; @@ -398,7 +399,48 @@ public void pluggableAuthCredentialSource_missingExecutableCommandField_throws() } @Test - public void builder_allFields() { + public void builder_allFields() throws IOException { + List scopes = Arrays.asList("scope1", "scope2"); + + CredentialSource source = buildCredentialSource(); + ExecutableHandler handler = options -> "Token"; + + PluggableAuthCredentials credentials = + (PluggableAuthCredentials) + PluggableAuthCredentials.newBuilder() + .setExecutableHandler(handler) + .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) + .setAudience("audience") + .setSubjectTokenType("subjectTokenType") + .setTokenUrl(STS_URL) + .setTokenInfoUrl("tokenInfoUrl") + .setCredentialSource(source) + .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) + .setQuotaProjectId("quotaProjectId") + .setClientId("clientId") + .setClientSecret("clientSecret") + .setScopes(scopes) + .setUniverseDomain("universeDomain") + .build(); + + assertEquals(handler, credentials.getExecutableHandler()); + assertEquals("audience", credentials.getAudience()); + assertEquals("subjectTokenType", credentials.getSubjectTokenType()); + assertEquals(STS_URL, credentials.getTokenUrl()); + assertEquals("tokenInfoUrl", credentials.getTokenInfoUrl()); + assertEquals( + SERVICE_ACCOUNT_IMPERSONATION_URL, credentials.getServiceAccountImpersonationUrl()); + assertEquals(source, credentials.getCredentialSource()); + assertEquals("quotaProjectId", credentials.getQuotaProjectId()); + assertEquals("clientId", credentials.getClientId()); + assertEquals("clientSecret", credentials.getClientSecret()); + assertEquals(scopes, credentials.getScopes()); + assertEquals(SystemEnvironmentProvider.getInstance(), credentials.getEnvironmentProvider()); + assertEquals("universeDomain", credentials.getUniverseDomain()); + } + + @Test + public void builder_missingUniverseDomain_defaults() throws IOException { List scopes = Arrays.asList("scope1", "scope2"); CredentialSource source = buildCredentialSource(); @@ -421,19 +463,105 @@ public void builder_allFields() { .setScopes(scopes) .build(); - assertEquals(credentials.getExecutableHandler(), handler); + assertEquals(handler, credentials.getExecutableHandler()); assertEquals("audience", credentials.getAudience()); assertEquals("subjectTokenType", credentials.getSubjectTokenType()); - assertEquals(credentials.getTokenUrl(), STS_URL); - assertEquals(credentials.getTokenInfoUrl(), "tokenInfoUrl"); + assertEquals(STS_URL, credentials.getTokenUrl()); + assertEquals("tokenInfoUrl", credentials.getTokenInfoUrl()); + assertEquals( + SERVICE_ACCOUNT_IMPERSONATION_URL, credentials.getServiceAccountImpersonationUrl()); + assertEquals(source, credentials.getCredentialSource()); + assertEquals("quotaProjectId", credentials.getQuotaProjectId()); + assertEquals("clientId", credentials.getClientId()); + assertEquals("clientSecret", credentials.getClientSecret()); + assertEquals(scopes, credentials.getScopes()); + assertEquals(SystemEnvironmentProvider.getInstance(), credentials.getEnvironmentProvider()); + assertEquals(GOOGLE_DEFAULT_UNIVERSE, credentials.getUniverseDomain()); + } + + @Test + public void newBuilder_allFields() throws IOException { + List scopes = Arrays.asList("scope1", "scope2"); + + CredentialSource source = buildCredentialSource(); + ExecutableHandler handler = options -> "Token"; + + PluggableAuthCredentials credentials = + (PluggableAuthCredentials) + PluggableAuthCredentials.newBuilder() + .setExecutableHandler(handler) + .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) + .setAudience("audience") + .setSubjectTokenType("subjectTokenType") + .setTokenUrl(STS_URL) + .setTokenInfoUrl("tokenInfoUrl") + .setCredentialSource(source) + .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) + .setQuotaProjectId("quotaProjectId") + .setClientId("clientId") + .setClientSecret("clientSecret") + .setScopes(scopes) + .setUniverseDomain("universeDomain") + .build(); + + PluggableAuthCredentials newBuilderCreds = + PluggableAuthCredentials.newBuilder(credentials).build(); + assertEquals(credentials.getAudience(), newBuilderCreds.getAudience()); + assertEquals(credentials.getSubjectTokenType(), newBuilderCreds.getSubjectTokenType()); + assertEquals(credentials.getTokenUrl(), newBuilderCreds.getTokenUrl()); + assertEquals(credentials.getTokenInfoUrl(), newBuilderCreds.getTokenInfoUrl()); + assertEquals( + credentials.getServiceAccountImpersonationUrl(), + newBuilderCreds.getServiceAccountImpersonationUrl()); + assertEquals(credentials.getCredentialSource(), newBuilderCreds.getCredentialSource()); + assertEquals(credentials.getQuotaProjectId(), newBuilderCreds.getQuotaProjectId()); + assertEquals(credentials.getClientId(), newBuilderCreds.getClientId()); + assertEquals(credentials.getClientSecret(), newBuilderCreds.getClientSecret()); + assertEquals(credentials.getScopes(), newBuilderCreds.getScopes()); + assertEquals(credentials.getEnvironmentProvider(), newBuilderCreds.getEnvironmentProvider()); + assertEquals(credentials.getUniverseDomain(), newBuilderCreds.getUniverseDomain()); + } + + @Test + public void newBuilder_noUniverseDomain_defaults() throws IOException { + List scopes = Arrays.asList("scope1", "scope2"); + + CredentialSource source = buildCredentialSource(); + ExecutableHandler handler = options -> "Token"; + + PluggableAuthCredentials credentials = + (PluggableAuthCredentials) + PluggableAuthCredentials.newBuilder() + .setExecutableHandler(handler) + .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) + .setAudience("audience") + .setSubjectTokenType("subjectTokenType") + .setTokenUrl(STS_URL) + .setTokenInfoUrl("tokenInfoUrl") + .setCredentialSource(source) + .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) + .setQuotaProjectId("quotaProjectId") + .setClientId("clientId") + .setClientSecret("clientSecret") + .setScopes(scopes) + .build(); + + PluggableAuthCredentials newBuilderCreds = + PluggableAuthCredentials.newBuilder(credentials).build(); + assertEquals(credentials.getAudience(), newBuilderCreds.getAudience()); + assertEquals(credentials.getSubjectTokenType(), newBuilderCreds.getSubjectTokenType()); + assertEquals(credentials.getTokenUrl(), newBuilderCreds.getTokenUrl()); + assertEquals(credentials.getTokenInfoUrl(), newBuilderCreds.getTokenInfoUrl()); assertEquals( - credentials.getServiceAccountImpersonationUrl(), SERVICE_ACCOUNT_IMPERSONATION_URL); - assertEquals(credentials.getCredentialSource(), source); - assertEquals(credentials.getQuotaProjectId(), "quotaProjectId"); - assertEquals(credentials.getClientId(), "clientId"); - assertEquals(credentials.getClientSecret(), "clientSecret"); - assertEquals(credentials.getScopes(), scopes); - assertEquals(credentials.getEnvironmentProvider(), SystemEnvironmentProvider.getInstance()); + credentials.getServiceAccountImpersonationUrl(), + newBuilderCreds.getServiceAccountImpersonationUrl()); + assertEquals(credentials.getCredentialSource(), newBuilderCreds.getCredentialSource()); + assertEquals(credentials.getQuotaProjectId(), newBuilderCreds.getQuotaProjectId()); + assertEquals(credentials.getClientId(), newBuilderCreds.getClientId()); + assertEquals(credentials.getClientSecret(), newBuilderCreds.getClientSecret()); + assertEquals(credentials.getScopes(), newBuilderCreds.getScopes()); + assertEquals(credentials.getEnvironmentProvider(), newBuilderCreds.getEnvironmentProvider()); + assertEquals(GOOGLE_DEFAULT_UNIVERSE, newBuilderCreds.getUniverseDomain()); } @Test