From 887c67dfdfafe55837eee4873261c7a5030ac41d Mon Sep 17 00:00:00 2001 From: Chaoren Liu Date: Mon, 22 Mar 2021 18:09:46 -0400 Subject: [PATCH 01/22] ADC can load impersonation credentials --- .../auth/oauth2/CredentialTypeException.java | 40 +++ .../oauth2/ExternalAccountCredentials.java | 15 +- .../google/auth/oauth2/GoogleCredentials.java | 4 + .../auth/oauth2/ImpersonatedCredentials.java | 238 ++++++++++++++---- 4 files changed, 236 insertions(+), 61 deletions(-) create mode 100644 oauth2_http/java/com/google/auth/oauth2/CredentialTypeException.java diff --git a/oauth2_http/java/com/google/auth/oauth2/CredentialTypeException.java b/oauth2_http/java/com/google/auth/oauth2/CredentialTypeException.java new file mode 100644 index 000000000..c8b60daa9 --- /dev/null +++ b/oauth2_http/java/com/google/auth/oauth2/CredentialTypeException.java @@ -0,0 +1,40 @@ +/* + * Copyright 2021 Google LLC + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Google LLC nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.google.auth.oauth2; + +import java.io.IOException; + +/** Indicates that the provided credential has a unsupported type. */ +public class CredentialTypeException extends IOException { + CredentialTypeException(String message) { + super(message); + } +} diff --git a/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java b/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java index 9293d5855..5442e6793 100644 --- a/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java @@ -198,7 +198,7 @@ private ImpersonatedCredentials initializeImpersonatedCredentials() { .build(); } - String targetPrincipal = extractTargetPrincipal(serviceAccountImpersonationUrl); + String targetPrincipal = ImpersonatedCredentials.extractTargetPrincipal(serviceAccountImpersonationUrl); return ImpersonatedCredentials.newBuilder() .setSourceCredentials(sourceCredentials) .setHttpTransportFactory(transportFactory) @@ -337,19 +337,6 @@ protected AccessToken exchangeExternalCredentialForAccessToken( return response.getAccessToken(); } - private static String extractTargetPrincipal(String serviceAccountImpersonationUrl) { - // Extract the target principal. - int startIndex = serviceAccountImpersonationUrl.lastIndexOf('/'); - int endIndex = serviceAccountImpersonationUrl.indexOf(":generateAccessToken"); - - if (startIndex != -1 && endIndex != -1 && startIndex < endIndex) { - return serviceAccountImpersonationUrl.substring(startIndex + 1, endIndex); - } else { - throw new IllegalArgumentException( - "Unable to determine target principal from service account impersonation URL."); - } - } - /** * Retrieves the external subject token to be exchanged for a GCP access token. * diff --git a/oauth2_http/java/com/google/auth/oauth2/GoogleCredentials.java b/oauth2_http/java/com/google/auth/oauth2/GoogleCredentials.java index 5a215322f..30bbd2c85 100644 --- a/oauth2_http/java/com/google/auth/oauth2/GoogleCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/GoogleCredentials.java @@ -54,6 +54,7 @@ public class GoogleCredentials extends OAuth2Credentials { static final String QUOTA_PROJECT_ID_HEADER_KEY = "x-goog-user-project"; static final String USER_FILE_TYPE = "authorized_user"; static final String SERVICE_ACCOUNT_FILE_TYPE = "service_account"; + static final String IMPERSONATED_SERVICE_ACCOUNT = "impersonated_service_account"; private static final DefaultCredentialsProvider defaultCredentialsProvider = new DefaultCredentialsProvider(); @@ -169,6 +170,9 @@ public static GoogleCredentials fromStream( if (ExternalAccountCredentials.EXTERNAL_ACCOUNT_FILE_TYPE.equals(fileType)) { return ExternalAccountCredentials.fromJson(fileContents, transportFactory); } + if (IMPERSONATED_SERVICE_ACCOUNT.equals(fileType)) { + return ImpersonatedCredentials.fromJson(fileContents, transportFactory); + } throw new IOException( String.format( "Error reading credentials from stream, 'type' value '%s' not recognized." diff --git a/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java b/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java index 6ddb116cc..4d1ab2195 100644 --- a/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java @@ -32,6 +32,7 @@ package com.google.auth.oauth2; import static com.google.common.base.MoreObjects.firstNonNull; +import static com.google.common.base.Preconditions.checkNotNull; import com.google.api.client.http.GenericUrl; import com.google.api.client.http.HttpContent; @@ -53,6 +54,7 @@ import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Date; import java.util.List; import java.util.Map; @@ -85,7 +87,7 @@ * */ public class ImpersonatedCredentials extends GoogleCredentials - implements ServiceAccountSigner, IdTokenProvider { + implements ServiceAccountSigner, IdTokenProvider, QuotaProjectIdProvider { private static final long serialVersionUID = -2133257318957488431L; private static final String RFC3339 = "yyyy-MM-dd'T'HH:mm:ss'Z'"; @@ -101,29 +103,31 @@ public class ImpersonatedCredentials extends GoogleCredentials private List delegates; private List scopes; private int lifetime; + private String quotaProjectId; private final String transportFactoryClassName; private transient HttpTransportFactory transportFactory; /** * @param sourceCredentials the source credential used as to acquire the impersonated credentials - * @param targetPrincipal the service account to impersonate - * @param delegates the chained list of delegates required to grant the final access_token. If - * set, the sequence of identities must have "Service Account Token Creator" capability - * granted to the preceding identity. For example, if set to [serviceAccountB, - * serviceAccountC], the sourceCredential must have the Token Creator role on serviceAccountB. - * serviceAccountB must have the Token Creator on serviceAccountC. Finally, C must have Token - * Creator on target_principal. If unset, sourceCredential must have that role on - * targetPrincipal. - * @param scopes scopes to request during the authorization grant - * @param lifetime number of seconds the delegated credential should be valid. By default this - * value should be at most 3600. However, you can follow these - * instructions to set up the service account and extend the maximum lifetime to 43200 (12 - * hours). If the given lifetime is 0, default value 3600 will be used instead when creating - * the credentials. - * @param transportFactory HTTP transport factory that creates the transport used to get access - * tokens + * @param targetPrincipal the service account to impersonate + * @param delegates the chained list of delegates required to grant the final + * access_token. If set, the sequence of identities must have "Service + * Account Token Creator" capability granted to the preceding identity. + * For example, if set to [serviceAccountB, serviceAccountC], the + * sourceCredential must have the Token Creator role on serviceAccountB. + * serviceAccountB must have the Token Creator on serviceAccountC. + * Finally, C must have Token Creator on target_principal. If unset, + * sourceCredential must have that role on targetPrincipal. + * @param scopes scopes to request during the authorization grant + * @param lifetime number of seconds the delegated credential should be valid. By default + * this value should be at most 3600. However, you can follow these + * instructions to set up the service account and extend the maximum + * lifetime to 43200 (12 hours). If the given lifetime is 0, default + * value 3600 will be used instead when creating the credentials. + * @param transportFactory HTTP transport factory that creates the transport used to get access + * tokens * @return new credentials */ public static ImpersonatedCredentials create( @@ -145,23 +149,65 @@ public static ImpersonatedCredentials create( /** * @param sourceCredentials the source credential used as to acquire the impersonated credentials - * @param targetPrincipal the service account to impersonate - * @param delegates the chained list of delegates required to grant the final access_token. If - * set, the sequence of identities must have "Service Account Token Creator" capability - * granted to the preceding identity. For example, if set to [serviceAccountB, - * serviceAccountC], the sourceCredential must have the Token Creator role on serviceAccountB. - * serviceAccountB must have the Token Creator on serviceAccountC. Finally, C must have Token - * Creator on target_principal. If left unset, sourceCredential must have that role on - * targetPrincipal. - * @param scopes scopes to request during the authorization grant - * @param lifetime number of seconds the delegated credential should be valid. By default this - * value should be at most 3600. However, you can follow these - * instructions to set up the service account and extend the maximum lifetime to 43200 (12 - * hours). - * https://cloud.google.com/iam/docs/creating-short-lived-service-account-credentials#sa-credentials-oauth - * If the given lifetime is 0, default value 3600 will be used instead when creating the - * credentials. + * @param targetPrincipal the service account to impersonate + * @param delegates the chained list of delegates required to grant the final + * access_token. If set, the sequence of identities must have "Service + * Account Token Creator" capability granted to the preceding identity. + * For example, if set to [serviceAccountB, serviceAccountC], the + * sourceCredential must have the Token Creator role on serviceAccountB. + * serviceAccountB must have the Token Creator on serviceAccountC. + * Finally, C must have Token Creator on target_principal. If unset, + * sourceCredential must have that role on targetPrincipal. + * @param scopes scopes to request during the authorization grant + * @param lifetime number of seconds the delegated credential should be valid. By default + * this value should be at most 3600. However, you can follow these + * instructions to set up the service account and extend the maximum + * lifetime to 43200 (12 hours). If the given lifetime is 0, default + * value 3600 will be used instead when creating the credentials. + * @param transportFactory HTTP transport factory that creates the transport used to get access + * tokens + * @param quotaProjectId the project used for quota and billing purposes. May be null. + * @return new credentials + */ + public static ImpersonatedCredentials create( + GoogleCredentials sourceCredentials, + String targetPrincipal, + List delegates, + List scopes, + int lifetime, + HttpTransportFactory transportFactory, + String quotaProjectId) { + return ImpersonatedCredentials.newBuilder() + .setSourceCredentials(sourceCredentials) + .setTargetPrincipal(targetPrincipal) + .setDelegates(delegates) + .setScopes(scopes) + .setLifetime(lifetime) + .setHttpTransportFactory(transportFactory) + .setQuotaProjectId(quotaProjectId) + .build(); + } + + /** + * @param sourceCredentials the source credential used as to acquire the impersonated credentials + * @param targetPrincipal the service account to impersonate + * @param delegates the chained list of delegates required to grant the final + * access_token. If set, the sequence of identities must have "Service + * Account Token Creator" capability granted to the preceding identity. + * For example, if set to [serviceAccountB, serviceAccountC], the + * sourceCredential must have the Token Creator role on serviceAccountB. + * serviceAccountB must have the Token Creator on serviceAccountC. + * Finally, C must have Token Creator on target_principal. If left unset, + * sourceCredential must have that role on targetPrincipal. + * @param scopes scopes to request during the authorization grant + * @param lifetime number of seconds the delegated credential should be valid. By default + * this value should be at most 3600. However, you can follow these + * instructions to set up the service account and extend the maximum + * lifetime to 43200 (12 hours). https://cloud.google.com/iam/docs/creating-short-lived-service-account-credentials#sa-credentials-oauth + * If the given lifetime is 0, default value 3600 will be used instead + * when creating the credentials. * @return new credentials */ public static ImpersonatedCredentials create( @@ -179,6 +225,19 @@ public static ImpersonatedCredentials create( .build(); } + static String extractTargetPrincipal(String serviceAccountImpersonationUrl) { + // Extract the target principal. + int startIndex = serviceAccountImpersonationUrl.lastIndexOf('/'); + int endIndex = serviceAccountImpersonationUrl.indexOf(":generateAccessToken"); + + if (startIndex != -1 && endIndex != -1 && startIndex < endIndex) { + return serviceAccountImpersonationUrl.substring(startIndex + 1, endIndex); + } else { + throw new IllegalArgumentException( + "Unable to determine target principal from service account impersonation URL."); + } + } + /** * Returns the email field of the serviceAccount that is being impersonated. * @@ -189,19 +248,24 @@ public String getAccount() { return this.targetPrincipal; } + @Override + public String getQuotaProjectId() { + return this.quotaProjectId; + } + int getLifetime() { return this.lifetime; } /** - * Signs the provided bytes using the private key associated with the impersonated service account + * Signs the provided bytes using the private key associated with the impersonated service + * account * * @param toSign bytes to sign * @return signed bytes * @throws SigningException if the attempt to sign the provided bytes failed - * @see Blob - * Signing + * @see Blob + * Signing */ @Override public byte[] sign(byte[] toSign) { @@ -213,6 +277,70 @@ public byte[] sign(byte[] toSign) { ImmutableMap.of("delegates", this.delegates)); } + /** + * Returns impersonation account credentials defined by JSON using the format generated by + * gCloud. + * + * @param json a map from the JSON representing the credentials + * @param transportFactory HTTP transport factory, creates the transport used to get access + * tokens + * @return the credentials defined by the JSON + * @throws IOException if the credential cannot be created from the JSON. + */ + static ImpersonatedCredentials fromJson(Map json, + HttpTransportFactory transportFactory) + throws IOException { + + checkNotNull(json); + checkNotNull(transportFactory); + + String serviceAccountImpersonationUrl = (String) json.get("service_account_impersonation_url"); + String targetPrincipal = extractTargetPrincipal(serviceAccountImpersonationUrl); + + List delegates = (List) json.get("delegates"); + Map sourceCredentialsJson = (Map) json + .get("source_credentials"); + String sourceCredentialsType = (String) sourceCredentialsJson.get("type"); + GoogleCredentials sourceCredentials = null; + if (GoogleCredentials.USER_FILE_TYPE.equals(sourceCredentialsType)) { + sourceCredentials = UserCredentials.fromJson(sourceCredentialsJson, transportFactory); + } else if (GoogleCredentials.SERVICE_ACCOUNT_FILE_TYPE.equals(sourceCredentialsType)) { + sourceCredentials = ServiceAccountCredentials + .fromJson(sourceCredentialsJson, transportFactory); + } else { + throw new CredentialTypeException(String + .format("The source_credentials of %s type has an unsupported type.", + sourceCredentialsType)); + } + // optional + String quotaProjectId = (String) json.get("quota_project_id"); + return ImpersonatedCredentials.newBuilder() + .setSourceCredentials(sourceCredentials) + .setTargetPrincipal(targetPrincipal) + .setDelegates(delegates) + .setScopes(new ArrayList()) + .setLifetime(DEFAULT_LIFETIME_IN_SECONDS) + .setHttpTransportFactory(transportFactory) + .setQuotaProjectId(quotaProjectId) + .build(); + } + + @Override + public boolean createScopedRequired() { + return this.scopes == null || this.scopes.isEmpty(); + } + + @Override + public GoogleCredentials createScoped(Collection scopes) { + return toBuilder() + .setScopes((List) scopes) + .setLifetime(this.lifetime) + .setDelegates(this.delegates) + .setHttpTransportFactory(this.transportFactory) + .setQuotaProjectId(this.quotaProjectId) + .build(); + } + private ImpersonatedCredentials(Builder builder) { this.sourceCredentials = builder.getSourceCredentials(); this.targetPrincipal = builder.getTargetPrincipal(); @@ -223,6 +351,7 @@ private ImpersonatedCredentials(Builder builder) { firstNonNull( builder.getHttpTransportFactory(), getFromServiceLoader(HttpTransportFactory.class, OAuth2Utils.HTTP_TRANSPORT_FACTORY)); + this.quotaProjectId = builder.quotaProjectId; this.transportFactoryClassName = this.transportFactory.getClass().getName(); if (this.delegates == null) { this.delegates = new ArrayList(); @@ -294,11 +423,12 @@ public AccessToken refreshAccessToken() throws IOException { * Returns an IdToken for the current Credential. * * @param targetAudience the audience field for the issued ID token - * @param options credential specific options for for the token. For example, an ID token for an - * ImpersonatedCredentials can return the email address within the token claims if - * "ImpersonatedCredentials.INCLUDE_EMAIL" is provided as a list option.
- * Only one option value is supported: "ImpersonatedCredentials.INCLUDE_EMAIL" If no options - * are set, the default excludes the "includeEmail" attribute in the API request. + * @param options credential specific options for for the token. For example, an ID token + * for an ImpersonatedCredentials can return the email address within the + * token claims if "ImpersonatedCredentials.INCLUDE_EMAIL" is provided as a + * list option.
Only one option value is supported: + * "ImpersonatedCredentials.INCLUDE_EMAIL" If no options are set, the + * default excludes the "includeEmail" attribute in the API request. * @return IdToken object which includes the raw id_token, expiration, and audience * @throws IOException if the attempt to get an ID token failed */ @@ -318,7 +448,8 @@ public IdToken idTokenWithAudience(String targetAudience, List scopes; private int lifetime = DEFAULT_LIFETIME_IN_SECONDS; private HttpTransportFactory transportFactory; + private String quotaProjectId; - protected Builder() {} + protected Builder() { + } protected Builder(GoogleCredentials sourceCredentials, String targetPrincipal) { this.sourceCredentials = sourceCredentials; @@ -425,6 +560,15 @@ public HttpTransportFactory getHttpTransportFactory() { return transportFactory; } + public Builder setQuotaProjectId(String quotaProjectId) { + this.quotaProjectId = quotaProjectId; + return this; + } + + public String getQuotaProjectId() { + return quotaProjectId; + } + public ImpersonatedCredentials build() { return new ImpersonatedCredentials(this); } From f98b61c242d56d41989315313bc0b9770d09aa6b Mon Sep 17 00:00:00 2001 From: Chaoren Liu Date: Tue, 23 Mar 2021 17:44:15 -0400 Subject: [PATCH 02/22] Add tests for new features in ImpersonationCredentials --- .../auth/oauth2/ImpersonatedCredentials.java | 21 ++ .../oauth2/ImpersonatedCredentialsTest.java | 210 ++++++++++++++++++ 2 files changed, 231 insertions(+) diff --git a/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java b/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java index 4d1ab2195..59857c570 100644 --- a/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java @@ -253,6 +253,18 @@ public String getQuotaProjectId() { return this.quotaProjectId; } + public List getDelegates() { + return delegates; + } + + public List getScopes() { + return scopes; + } + + public GoogleCredentials getSourceCredentials() { + return sourceCredentials; + } + int getLifetime() { return this.lifetime; } @@ -341,6 +353,15 @@ public GoogleCredentials createScoped(Collection scopes) { .build(); } + @Override + protected Map> getAdditionalHeaders() { + Map> headers = super.getAdditionalHeaders(); + if (quotaProjectId != null) { + return addQuotaProjectIdToRequestMetadata(quotaProjectId, headers); + } + return headers; + } + private ImpersonatedCredentials(Builder builder) { this.sourceCredentials = builder.getSourceCredentials(); this.targetPrincipal = builder.getTargetPrincipal(); diff --git a/oauth2_http/javatests/com/google/auth/oauth2/ImpersonatedCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/ImpersonatedCredentialsTest.java index b7c3bd29e..fcc71c3e3 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/ImpersonatedCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/ImpersonatedCredentialsTest.java @@ -33,7 +33,9 @@ import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -61,6 +63,7 @@ import java.util.Calendar; import java.util.Date; import java.util.List; +import java.util.Map; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -114,6 +117,13 @@ public class ImpersonatedCredentialsTest extends BaseSerializationTest { private static JsonFactory JSON_FACTORY = GsonFactory.getDefaultInstance(); private static final String RFC3339 = "yyyy-MM-dd'T'HH:mm:ss'Z'"; + private static final String IMPERSONATION_URL = + "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/"+ IMPERSONATED_CLIENT_EMAIL + ":generateAccessToken"; + private static final String USER_ACCOUNT_CLIENT_ID = "76408650-6qr441hur.apps.googleusercontent.com"; + private static final String USER_ACCOUNT_CLIENT_SECRET = "d-F499q74hFpdHD0T5"; + private static final String QUOTA_PROJECT_ID = "quota-project-id"; + private static final String REFRESH_TOKEN = "dasdfasdffa4ffdfadgyjirasdfadsft"; + private static final List DELEGATES = Arrays.asList("sa1@developer.gserviceaccount.com", "sa2@developer.gserviceaccount.com"); static class MockIAMCredentialsServiceTransportFactory implements HttpTransportFactory { @@ -142,6 +152,111 @@ private GoogleCredentials getSourceCredentials() throws IOException { return sourceCredentials; } + @Test() + public void fromJson_userAsSource_WithQuotaProjectId() throws IOException { + GenericJson json = buildImpersonationCredentialsJson(IMPERSONATION_URL, DELEGATES, + QUOTA_PROJECT_ID, USER_ACCOUNT_CLIENT_ID, USER_ACCOUNT_CLIENT_SECRET, REFRESH_TOKEN); + MockIAMCredentialsServiceTransportFactory mtransportFactory = + new MockIAMCredentialsServiceTransportFactory(); + ImpersonatedCredentials credentials = ImpersonatedCredentials.fromJson(json, mtransportFactory); + assertEquals(IMPERSONATED_CLIENT_EMAIL, credentials.getAccount()); + assertEquals(QUOTA_PROJECT_ID, credentials.getQuotaProjectId()); + assertEquals(DELEGATES, credentials.getDelegates()); + assertEquals(new ArrayList(), credentials.getScopes()); + assertEquals(credentials.getLifetime(), 3600); + GoogleCredentials sourceCredentials = credentials.getSourceCredentials(); + assertTrue(sourceCredentials instanceof UserCredentials); + } + + @Test() + public void fromJson_userAsSource_WithoutQuotaProjectId() throws IOException { + GenericJson json = buildImpersonationCredentialsJson(IMPERSONATION_URL, DELEGATES, null, + USER_ACCOUNT_CLIENT_ID, USER_ACCOUNT_CLIENT_SECRET, REFRESH_TOKEN); + MockIAMCredentialsServiceTransportFactory mtransportFactory = + new MockIAMCredentialsServiceTransportFactory(); + ImpersonatedCredentials credentials = ImpersonatedCredentials.fromJson(json, mtransportFactory); + assertEquals(IMPERSONATED_CLIENT_EMAIL, credentials.getAccount()); + assertNull(credentials.getQuotaProjectId()); + assertEquals(DELEGATES, credentials.getDelegates()); + assertEquals(new ArrayList(), credentials.getScopes()); + assertEquals(credentials.getLifetime(), 3600); + GoogleCredentials sourceCredentials = credentials.getSourceCredentials(); + assertTrue(sourceCredentials instanceof UserCredentials); + } + + @Test() + public void fromJson_ServiceAccountAsSource() throws IOException { + GenericJson json = buildImpersonationCredentialsJson(IMPERSONATION_URL, DELEGATES, QUOTA_PROJECT_ID); + MockIAMCredentialsServiceTransportFactory mtransportFactory = + new MockIAMCredentialsServiceTransportFactory(); + ImpersonatedCredentials credentials = ImpersonatedCredentials.fromJson(json, mtransportFactory); + assertEquals(IMPERSONATED_CLIENT_EMAIL, credentials.getAccount()); + assertEquals(QUOTA_PROJECT_ID, credentials.getQuotaProjectId()); + assertEquals(DELEGATES, credentials.getDelegates()); + assertEquals(new ArrayList(), credentials.getScopes()); + assertEquals(credentials.getLifetime(), 3600); + GoogleCredentials sourceCredentials = credentials.getSourceCredentials(); + assertTrue(sourceCredentials instanceof ServiceAccountCredentials); + } + + @Test() + public void createScopedRequired_True() throws IOException { + GoogleCredentials sourceCredentials = getSourceCredentials(); + MockIAMCredentialsServiceTransportFactory mtransportFactory = + new MockIAMCredentialsServiceTransportFactory(); + ImpersonatedCredentials targetCredentials = + ImpersonatedCredentials.create( + sourceCredentials, + IMPERSONATED_CLIENT_EMAIL, + null, + new ArrayList(), + VALID_LIFETIME, + mtransportFactory); + assertTrue(targetCredentials.createScopedRequired()); + } + + @Test() + public void createScopedRequired_False() throws IOException { + GoogleCredentials sourceCredentials = getSourceCredentials(); + MockIAMCredentialsServiceTransportFactory mtransportFactory = + new MockIAMCredentialsServiceTransportFactory(); + ImpersonatedCredentials targetCredentials = + ImpersonatedCredentials.create( + sourceCredentials, + IMPERSONATED_CLIENT_EMAIL, + null, + SCOPES, + VALID_LIFETIME, + mtransportFactory); + assertFalse(targetCredentials.createScopedRequired()); + } + + @Test() + public void createScoped() throws IOException { + GoogleCredentials sourceCredentials = getSourceCredentials(); + MockIAMCredentialsServiceTransportFactory mtransportFactory = + new MockIAMCredentialsServiceTransportFactory(); + ImpersonatedCredentials targetCredentials = + ImpersonatedCredentials.create( + sourceCredentials, + IMPERSONATED_CLIENT_EMAIL, + DELEGATES, + SCOPES, + VALID_LIFETIME, + mtransportFactory, + QUOTA_PROJECT_ID); + + ImpersonatedCredentials scoped_credentials = (ImpersonatedCredentials) targetCredentials + .createScoped(Arrays.asList("scope1", "scope2")); + assertEquals(targetCredentials.getAccount(), scoped_credentials.getAccount()); + assertEquals(targetCredentials.getDelegates(), scoped_credentials.getDelegates()); + assertEquals(targetCredentials.getLifetime(), scoped_credentials.getLifetime()); + assertEquals(targetCredentials.getSourceCredentials(), + scoped_credentials.getSourceCredentials()); + assertEquals(targetCredentials.getQuotaProjectId(), scoped_credentials.getQuotaProjectId()); + assertEquals(Arrays.asList("scope1", "scope2"), scoped_credentials.getScopes()); + } + @Test() public void refreshAccessToken_unauthorized() throws IOException { @@ -263,6 +378,54 @@ public void refreshAccessToken_success() throws IOException, IllegalStateExcepti assertEquals(ACCESS_TOKEN, targetCredentials.refreshAccessToken().getTokenValue()); } + @Test() + public void getRequestMetadata_withQuotaProjectId() throws IOException, IllegalStateException { + + GoogleCredentials sourceCredentials = getSourceCredentials(); + MockIAMCredentialsServiceTransportFactory mtransportFactory = + new MockIAMCredentialsServiceTransportFactory(); + mtransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); + mtransportFactory.transport.setAccessToken(ACCESS_TOKEN); + mtransportFactory.transport.setExpireTime(getDefaultExpireTime()); + ImpersonatedCredentials targetCredentials = + ImpersonatedCredentials.create( + sourceCredentials, + IMPERSONATED_CLIENT_EMAIL, + null, + SCOPES, + VALID_LIFETIME, + mtransportFactory, + QUOTA_PROJECT_ID); + + Map> metadata = targetCredentials.getRequestMetadata(); + assertTrue(metadata.containsKey("x-goog-user-project")); + List headerValues = metadata.get("x-goog-user-project"); + assertEquals(1, headerValues.size()); + assertEquals(QUOTA_PROJECT_ID, headerValues.get(0)); + } + + @Test() + public void getRequestMetadata_withoutQuotaProjectId() throws IOException, IllegalStateException { + + GoogleCredentials sourceCredentials = getSourceCredentials(); + MockIAMCredentialsServiceTransportFactory mtransportFactory = + new MockIAMCredentialsServiceTransportFactory(); + mtransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); + mtransportFactory.transport.setAccessToken(ACCESS_TOKEN); + mtransportFactory.transport.setExpireTime(getDefaultExpireTime()); + ImpersonatedCredentials targetCredentials = + ImpersonatedCredentials.create( + sourceCredentials, + IMPERSONATED_CLIENT_EMAIL, + null, + SCOPES, + VALID_LIFETIME, + mtransportFactory); + + Map> metadata = targetCredentials.getRequestMetadata(); + assertFalse(metadata.containsKey("x-goog-user-project")); + } + @Test() public void refreshAccessToken_delegates_success() throws IOException, IllegalStateException { @@ -736,4 +899,51 @@ private String generateErrorJson( generator.close(); return bout.toString(); } + + static GenericJson buildImpersonationCredentialsJson( + String impersonationUrl, List delegates, String quotaProjectId, + String sourceClientId, String sourceClientSecret, String sourceRefreshToken) { + GenericJson sourceJson = new GenericJson(); + + sourceJson.put("client_id", sourceClientId); + sourceJson.put("client_secret", sourceClientSecret); + sourceJson.put("refresh_token", sourceRefreshToken); + sourceJson.put("type", "authorized_user"); + GenericJson json = new GenericJson(); + + json.put("service_account_impersonation_url", impersonationUrl); + json.put("delegates", delegates); + if (quotaProjectId != null) { + json.put("quota_project_id", quotaProjectId); + } + json.put("source_credentials", sourceJson); + json.put("type", "impersonated_service_account"); + return json; + } + + static GenericJson buildImpersonationCredentialsJson( + String impersonationUrl, List delegates, String quotaProjectId) { + GenericJson sourceJson = new GenericJson(); + sourceJson.put("type", "service_account"); + sourceJson.put("project_id", PROJECT_ID); + sourceJson.put("private_key_id", SA_PRIVATE_KEY_ID); + sourceJson.put("private_key", SA_PRIVATE_KEY_PKCS8); + sourceJson.put("client_email", SA_CLIENT_EMAIL); + sourceJson.put("client_id", "10848832332323213"); + sourceJson.put("auth_uri", "https://oauth2.googleapis.com/o/oauth2/auth"); + sourceJson.put("token_uri", "https://oauth2.googleapis.com/token"); + sourceJson.put("auth_provider_x509_cert_url", "https://www.googleapis.com/oauth2/v1/certs"); + sourceJson.put("client_x509_cert_url", + "https://www.googleapis.com/robot/v1/metadata/x509/chaoren-test-sc%40cloudsdktest.iam.gserviceaccount.com"); + + GenericJson json = new GenericJson(); + json.put("source_credentials", sourceJson); + json.put("service_account_impersonation_url", impersonationUrl); + json.put("delegates", delegates); + if (quotaProjectId != null) { + json.put("quota_project_id", quotaProjectId); + } + json.put("type", "impersonated_service_account"); + return json; + } } From ebeba8bc1610e39506b09ab0cd78fa61d8433625 Mon Sep 17 00:00:00 2001 From: Chaoren Liu Date: Wed, 24 Mar 2021 17:08:44 -0400 Subject: [PATCH 03/22] Add tests for GoogleCredentials --- .../auth/oauth2/ImpersonatedCredentials.java | 4 +++ .../auth/oauth2/GoogleCredentialsTest.java | 36 +++++++++++++++++++ .../oauth2/ImpersonatedCredentialsTest.java | 24 +++++++++---- 3 files changed, 57 insertions(+), 7 deletions(-) diff --git a/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java b/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java index 59857c570..90f19800d 100644 --- a/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java @@ -269,6 +269,10 @@ int getLifetime() { return this.lifetime; } + public void setTransportFactory(HttpTransportFactory httpTransportFactory) { + this.transportFactory = httpTransportFactory; + } + /** * Signs the provided bytes using the private key associated with the impersonated service * account diff --git a/oauth2_http/javatests/com/google/auth/oauth2/GoogleCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/GoogleCredentialsTest.java index a87dc84fe..b21ff0a74 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/GoogleCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/GoogleCredentialsTest.java @@ -41,6 +41,7 @@ import com.google.auth.TestUtils; import com.google.auth.http.HttpTransportFactory; import com.google.auth.oauth2.IdentityPoolCredentialsTest.MockExternalAccountCredentialsTransportFactory; +import com.google.auth.oauth2.ImpersonatedCredentialsTest.MockIAMCredentialsServiceTransportFactory; import com.google.common.collect.ImmutableList; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -270,6 +271,41 @@ public void fromStream_awsCredentials_providesToken() throws IOException { TestUtils.assertContainsBearerToken(metadata, transportFactory.transport.getAccessToken()); } + @Test + public void fromStream_Impersonation_providesToken() throws IOException { + MockTokenServerTransportFactory transportFactoryForSource = + new MockTokenServerTransportFactory(); + transportFactoryForSource.transport.addServiceAccount( + ImpersonatedCredentialsTest.SA_CLIENT_EMAIL, ImpersonatedCredentialsTest.ACCESS_TOKEN); + + MockIAMCredentialsServiceTransportFactory transportFactory = + new MockIAMCredentialsServiceTransportFactory(); + transportFactory.transport.setTargetPrincipal( + ImpersonatedCredentialsTest.IMPERSONATED_CLIENT_EMAIL); + transportFactory.transport.setAccessToken(ImpersonatedCredentialsTest.ACCESS_TOKEN); + transportFactory.transport.setExpireTime(ImpersonatedCredentialsTest.getDefaultExpireTime()); + + InputStream impersonationCredentialsStream = + ImpersonatedCredentialsTest.writeImpersonationCredentialsStream( + ImpersonatedCredentialsTest.IMPERSONATION_URL, + ImpersonatedCredentialsTest.DELEGATES, + ImpersonatedCredentialsTest.QUOTA_PROJECT_ID); + + ImpersonatedCredentials credentials = + (ImpersonatedCredentials) + GoogleCredentials.fromStream(impersonationCredentialsStream, transportFactoryForSource); + credentials.setTransportFactory(transportFactory); + + assertNotNull(credentials); + Map> metadata = credentials.getRequestMetadata(CALL_URI); + TestUtils.assertContainsBearerToken(metadata, ImpersonatedCredentialsTest.ACCESS_TOKEN); + + assertTrue(metadata.containsKey("x-goog-user-project")); + List headerValues = metadata.get("x-goog-user-project"); + assertEquals(1, headerValues.size()); + assertEquals(ImpersonatedCredentialsTest.QUOTA_PROJECT_ID, headerValues.get(0)); + } + @Test public void createScoped_overloadCallsImplementation() { final AtomicReference> called = new AtomicReference<>(); diff --git a/oauth2_http/javatests/com/google/auth/oauth2/ImpersonatedCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/ImpersonatedCredentialsTest.java index fcc71c3e3..d4f4adee7 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/ImpersonatedCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/ImpersonatedCredentialsTest.java @@ -52,9 +52,11 @@ import com.google.auth.ServiceAccountSigner.SigningException; import com.google.auth.http.HttpTransportFactory; import com.google.auth.oauth2.GoogleCredentialsTest.MockTokenServerTransportFactory; +import com.google.auth.TestUtils; import com.google.common.collect.ImmutableList; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.InputStream; import java.nio.charset.Charset; import java.security.PrivateKey; import java.text.SimpleDateFormat; @@ -72,7 +74,7 @@ @RunWith(JUnit4.class) public class ImpersonatedCredentialsTest extends BaseSerializationTest { - private static final String SA_CLIENT_EMAIL = + public static final String SA_CLIENT_EMAIL = "36680232662-vrd7ji19qe3nelgchd0ah2csanun6bnr@developer.gserviceaccount.com"; private static final String SA_PRIVATE_KEY_ID = "d84a4fefcf50791d4a90f2d7af17469d6282df9d"; static final String SA_PRIVATE_KEY_PKCS8 = @@ -105,25 +107,25 @@ public class ImpersonatedCredentialsTest extends BaseSerializationTest { + "lZC1yYXktMTA0MTE3LmlhbS5nc2VydmljZWFjY291bnQuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImV4cC" + "I6MTU2NDUzMzA0MiwiaWF0IjoxNTY0NTI5NDQyLCJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iL" + "CJzdWIiOiIxMDIxMDE1NTA4MzQyMDA3MDg1NjgifQ.redacted"; + public static final String ACCESS_TOKEN = "1/MkSJoj1xsli0AccessToken_NKPY2"; private static final String PROJECT_ID = "project-id"; - private static final String IMPERSONATED_CLIENT_EMAIL = + public static final String IMPERSONATED_CLIENT_EMAIL = "impersonated-account@iam.gserviceaccount.com"; private static final List SCOPES = Arrays.asList("https://www.googleapis.com/auth/devstorage.read_only"); - private static final String ACCESS_TOKEN = "1/MkSJoj1xsli0AccessToken_NKPY2"; private static final int VALID_LIFETIME = 300; private static final int INVALID_LIFETIME = 43210; private static JsonFactory JSON_FACTORY = GsonFactory.getDefaultInstance(); private static final String RFC3339 = "yyyy-MM-dd'T'HH:mm:ss'Z'"; - private static final String IMPERSONATION_URL = + public static final String IMPERSONATION_URL = "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/"+ IMPERSONATED_CLIENT_EMAIL + ":generateAccessToken"; private static final String USER_ACCOUNT_CLIENT_ID = "76408650-6qr441hur.apps.googleusercontent.com"; private static final String USER_ACCOUNT_CLIENT_SECRET = "d-F499q74hFpdHD0T5"; - private static final String QUOTA_PROJECT_ID = "quota-project-id"; + public static final String QUOTA_PROJECT_ID = "quota-project-id"; private static final String REFRESH_TOKEN = "dasdfasdffa4ffdfadgyjirasdfadsft"; - private static final List DELEGATES = Arrays.asList("sa1@developer.gserviceaccount.com", "sa2@developer.gserviceaccount.com"); + public static final List DELEGATES = Arrays.asList("sa1@developer.gserviceaccount.com", "sa2@developer.gserviceaccount.com"); static class MockIAMCredentialsServiceTransportFactory implements HttpTransportFactory { @@ -853,7 +855,7 @@ public void serialize() throws IOException, ClassNotFoundException { assertSame(deserializedCredentials.clock, Clock.SYSTEM); } - private String getDefaultExpireTime() { + public static String getDefaultExpireTime() { Date currentDate = new Date(); Calendar c = Calendar.getInstance(); c.setTime(currentDate); @@ -946,4 +948,12 @@ static GenericJson buildImpersonationCredentialsJson( json.put("type", "impersonated_service_account"); return json; } + + static InputStream writeImpersonationCredentialsStream(String impersonationUrl, + List delegates, String quotaProjectId) + throws IOException { + GenericJson json = buildImpersonationCredentialsJson(impersonationUrl, delegates, + quotaProjectId); + return TestUtils.jsonToInputStream(json); + } } From 33f6db00b04843b118d104d4b52907253845cdc9 Mon Sep 17 00:00:00 2001 From: Chaoren Liu Date: Thu, 25 Mar 2021 15:53:32 -0400 Subject: [PATCH 04/22] Fix linter errors --- .../auth/oauth2/CredentialTypeException.java | 6 +- .../oauth2/ExternalAccountCredentials.java | 3 +- .../auth/oauth2/ImpersonatedCredentials.java | 159 +++++++++--------- .../oauth2/ImpersonatedCredentialsTest.java | 61 ++++--- 4 files changed, 122 insertions(+), 107 deletions(-) diff --git a/oauth2_http/java/com/google/auth/oauth2/CredentialTypeException.java b/oauth2_http/java/com/google/auth/oauth2/CredentialTypeException.java index c8b60daa9..c33353c4e 100644 --- a/oauth2_http/java/com/google/auth/oauth2/CredentialTypeException.java +++ b/oauth2_http/java/com/google/auth/oauth2/CredentialTypeException.java @@ -34,7 +34,7 @@ /** Indicates that the provided credential has a unsupported type. */ public class CredentialTypeException extends IOException { - CredentialTypeException(String message) { - super(message); - } + CredentialTypeException(String message) { + super(message); + } } diff --git a/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java b/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java index 5442e6793..1c3ac1e1a 100644 --- a/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java @@ -198,7 +198,8 @@ private ImpersonatedCredentials initializeImpersonatedCredentials() { .build(); } - String targetPrincipal = ImpersonatedCredentials.extractTargetPrincipal(serviceAccountImpersonationUrl); + String targetPrincipal = + ImpersonatedCredentials.extractTargetPrincipal(serviceAccountImpersonationUrl); return ImpersonatedCredentials.newBuilder() .setSourceCredentials(sourceCredentials) .setHttpTransportFactory(transportFactory) diff --git a/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java b/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java index 90f19800d..4d95cd204 100644 --- a/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java @@ -110,24 +110,23 @@ public class ImpersonatedCredentials extends GoogleCredentials /** * @param sourceCredentials the source credential used as to acquire the impersonated credentials - * @param targetPrincipal the service account to impersonate - * @param delegates the chained list of delegates required to grant the final - * access_token. If set, the sequence of identities must have "Service - * Account Token Creator" capability granted to the preceding identity. - * For example, if set to [serviceAccountB, serviceAccountC], the - * sourceCredential must have the Token Creator role on serviceAccountB. - * serviceAccountB must have the Token Creator on serviceAccountC. - * Finally, C must have Token Creator on target_principal. If unset, - * sourceCredential must have that role on targetPrincipal. - * @param scopes scopes to request during the authorization grant - * @param lifetime number of seconds the delegated credential should be valid. By default - * this value should be at most 3600. However, you can follow these - * instructions to set up the service account and extend the maximum - * lifetime to 43200 (12 hours). If the given lifetime is 0, default - * value 3600 will be used instead when creating the credentials. - * @param transportFactory HTTP transport factory that creates the transport used to get access - * tokens + * @param targetPrincipal the service account to impersonate + * @param delegates the chained list of delegates required to grant the final access_token. If + * set, the sequence of identities must have "Service Account Token Creator" capability + * granted to the preceding identity. For example, if set to [serviceAccountB, + * serviceAccountC], the sourceCredential must have the Token Creator role on serviceAccountB. + * serviceAccountB must have the Token Creator on serviceAccountC. Finally, C must have Token + * Creator on target_principal. If unset, sourceCredential must have that role on + * targetPrincipal. + * @param scopes scopes to request during the authorization grant + * @param lifetime number of seconds the delegated credential should be valid. By default this + * value should be at most 3600. However, you can follow these + * instructions to set up the service account and extend the maximum lifetime to 43200 (12 + * hours). If the given lifetime is 0, default value 3600 will be used instead when creating + * the credentials. + * @param transportFactory HTTP transport factory that creates the transport used to get access + * tokens * @return new credentials */ public static ImpersonatedCredentials create( @@ -149,25 +148,24 @@ public static ImpersonatedCredentials create( /** * @param sourceCredentials the source credential used as to acquire the impersonated credentials - * @param targetPrincipal the service account to impersonate - * @param delegates the chained list of delegates required to grant the final - * access_token. If set, the sequence of identities must have "Service - * Account Token Creator" capability granted to the preceding identity. - * For example, if set to [serviceAccountB, serviceAccountC], the - * sourceCredential must have the Token Creator role on serviceAccountB. - * serviceAccountB must have the Token Creator on serviceAccountC. - * Finally, C must have Token Creator on target_principal. If unset, - * sourceCredential must have that role on targetPrincipal. - * @param scopes scopes to request during the authorization grant - * @param lifetime number of seconds the delegated credential should be valid. By default - * this value should be at most 3600. However, you can follow these - * instructions to set up the service account and extend the maximum - * lifetime to 43200 (12 hours). If the given lifetime is 0, default - * value 3600 will be used instead when creating the credentials. - * @param transportFactory HTTP transport factory that creates the transport used to get access - * tokens - * @param quotaProjectId the project used for quota and billing purposes. May be null. + * @param targetPrincipal the service account to impersonate + * @param delegates the chained list of delegates required to grant the final access_token. If + * set, the sequence of identities must have "Service Account Token Creator" capability + * granted to the preceding identity. For example, if set to [serviceAccountB, + * serviceAccountC], the sourceCredential must have the Token Creator role on serviceAccountB. + * serviceAccountB must have the Token Creator on serviceAccountC. Finally, C must have Token + * Creator on target_principal. If unset, sourceCredential must have that role on + * targetPrincipal. + * @param scopes scopes to request during the authorization grant + * @param lifetime number of seconds the delegated credential should be valid. By default this + * value should be at most 3600. However, you can follow these + * instructions to set up the service account and extend the maximum lifetime to 43200 (12 + * hours). If the given lifetime is 0, default value 3600 will be used instead when creating + * the credentials. + * @param transportFactory HTTP transport factory that creates the transport used to get access + * tokens + * @param quotaProjectId the project used for quota and billing purposes. May be null. * @return new credentials */ public static ImpersonatedCredentials create( @@ -191,23 +189,23 @@ public static ImpersonatedCredentials create( /** * @param sourceCredentials the source credential used as to acquire the impersonated credentials - * @param targetPrincipal the service account to impersonate - * @param delegates the chained list of delegates required to grant the final - * access_token. If set, the sequence of identities must have "Service - * Account Token Creator" capability granted to the preceding identity. - * For example, if set to [serviceAccountB, serviceAccountC], the - * sourceCredential must have the Token Creator role on serviceAccountB. - * serviceAccountB must have the Token Creator on serviceAccountC. - * Finally, C must have Token Creator on target_principal. If left unset, - * sourceCredential must have that role on targetPrincipal. - * @param scopes scopes to request during the authorization grant - * @param lifetime number of seconds the delegated credential should be valid. By default - * this value should be at most 3600. However, you can follow these - * instructions to set up the service account and extend the maximum - * lifetime to 43200 (12 hours). https://cloud.google.com/iam/docs/creating-short-lived-service-account-credentials#sa-credentials-oauth - * If the given lifetime is 0, default value 3600 will be used instead - * when creating the credentials. + * @param targetPrincipal the service account to impersonate + * @param delegates the chained list of delegates required to grant the final access_token. If + * set, the sequence of identities must have "Service Account Token Creator" capability + * granted to the preceding identity. For example, if set to [serviceAccountB, + * serviceAccountC], the sourceCredential must have the Token Creator role on serviceAccountB. + * serviceAccountB must have the Token Creator on serviceAccountC. Finally, C must have Token + * Creator on target_principal. If left unset, sourceCredential must have that role on + * targetPrincipal. + * @param scopes scopes to request during the authorization grant + * @param lifetime number of seconds the delegated credential should be valid. By default this + * value should be at most 3600. However, you can follow these + * instructions to set up the service account and extend the maximum lifetime to 43200 (12 + * hours). + * https://cloud.google.com/iam/docs/creating-short-lived-service-account-credentials#sa-credentials-oauth + * If the given lifetime is 0, default value 3600 will be used instead when creating the + * credentials. * @return new credentials */ public static ImpersonatedCredentials create( @@ -274,14 +272,14 @@ public void setTransportFactory(HttpTransportFactory httpTransportFactory) { } /** - * Signs the provided bytes using the private key associated with the impersonated service - * account + * Signs the provided bytes using the private key associated with the impersonated service account * * @param toSign bytes to sign * @return signed bytes * @throws SigningException if the attempt to sign the provided bytes failed - * @see Blob - * Signing + * @see Blob + * Signing */ @Override public byte[] sign(byte[] toSign) { @@ -294,18 +292,15 @@ public byte[] sign(byte[] toSign) { } /** - * Returns impersonation account credentials defined by JSON using the format generated by - * gCloud. + * Returns impersonation account credentials defined by JSON using the format generated by gCloud. * - * @param json a map from the JSON representing the credentials - * @param transportFactory HTTP transport factory, creates the transport used to get access - * tokens + * @param json a map from the JSON representing the credentials + * @param transportFactory HTTP transport factory, creates the transport used to get access tokens * @return the credentials defined by the JSON * @throws IOException if the credential cannot be created from the JSON. */ - static ImpersonatedCredentials fromJson(Map json, - HttpTransportFactory transportFactory) - throws IOException { + static ImpersonatedCredentials fromJson( + Map json, HttpTransportFactory transportFactory) throws IOException { checkNotNull(json); checkNotNull(transportFactory); @@ -314,19 +309,19 @@ static ImpersonatedCredentials fromJson(Map json, String targetPrincipal = extractTargetPrincipal(serviceAccountImpersonationUrl); List delegates = (List) json.get("delegates"); - Map sourceCredentialsJson = (Map) json - .get("source_credentials"); + Map sourceCredentialsJson = + (Map) json.get("source_credentials"); String sourceCredentialsType = (String) sourceCredentialsJson.get("type"); GoogleCredentials sourceCredentials = null; if (GoogleCredentials.USER_FILE_TYPE.equals(sourceCredentialsType)) { sourceCredentials = UserCredentials.fromJson(sourceCredentialsJson, transportFactory); } else if (GoogleCredentials.SERVICE_ACCOUNT_FILE_TYPE.equals(sourceCredentialsType)) { - sourceCredentials = ServiceAccountCredentials - .fromJson(sourceCredentialsJson, transportFactory); + sourceCredentials = + ServiceAccountCredentials.fromJson(sourceCredentialsJson, transportFactory); } else { - throw new CredentialTypeException(String - .format("The source_credentials of %s type has an unsupported type.", - sourceCredentialsType)); + throw new CredentialTypeException( + String.format( + "The source_credentials of %s type has an unsupported type.", sourceCredentialsType)); } // optional String quotaProjectId = (String) json.get("quota_project_id"); @@ -448,12 +443,11 @@ public AccessToken refreshAccessToken() throws IOException { * Returns an IdToken for the current Credential. * * @param targetAudience the audience field for the issued ID token - * @param options credential specific options for for the token. For example, an ID token - * for an ImpersonatedCredentials can return the email address within the - * token claims if "ImpersonatedCredentials.INCLUDE_EMAIL" is provided as a - * list option.
Only one option value is supported: - * "ImpersonatedCredentials.INCLUDE_EMAIL" If no options are set, the - * default excludes the "includeEmail" attribute in the API request. + * @param options credential specific options for for the token. For example, an ID token for an + * ImpersonatedCredentials can return the email address within the token claims if + * "ImpersonatedCredentials.INCLUDE_EMAIL" is provided as a list option.
+ * Only one option value is supported: "ImpersonatedCredentials.INCLUDE_EMAIL" If no options + * are set, the default excludes the "includeEmail" attribute in the API request. * @return IdToken object which includes the raw id_token, expiration, and audience * @throws IOException if the attempt to get an ID token failed */ @@ -473,8 +467,8 @@ public IdToken idTokenWithAudience(String targetAudience, List DELEGATES = Arrays.asList("sa1@developer.gserviceaccount.com", "sa2@developer.gserviceaccount.com"); + public static final List DELEGATES = + Arrays.asList("sa1@developer.gserviceaccount.com", "sa2@developer.gserviceaccount.com"); static class MockIAMCredentialsServiceTransportFactory implements HttpTransportFactory { @@ -156,8 +160,14 @@ private GoogleCredentials getSourceCredentials() throws IOException { @Test() public void fromJson_userAsSource_WithQuotaProjectId() throws IOException { - GenericJson json = buildImpersonationCredentialsJson(IMPERSONATION_URL, DELEGATES, - QUOTA_PROJECT_ID, USER_ACCOUNT_CLIENT_ID, USER_ACCOUNT_CLIENT_SECRET, REFRESH_TOKEN); + GenericJson json = + buildImpersonationCredentialsJson( + IMPERSONATION_URL, + DELEGATES, + QUOTA_PROJECT_ID, + USER_ACCOUNT_CLIENT_ID, + USER_ACCOUNT_CLIENT_SECRET, + REFRESH_TOKEN); MockIAMCredentialsServiceTransportFactory mtransportFactory = new MockIAMCredentialsServiceTransportFactory(); ImpersonatedCredentials credentials = ImpersonatedCredentials.fromJson(json, mtransportFactory); @@ -172,8 +182,14 @@ public void fromJson_userAsSource_WithQuotaProjectId() throws IOException { @Test() public void fromJson_userAsSource_WithoutQuotaProjectId() throws IOException { - GenericJson json = buildImpersonationCredentialsJson(IMPERSONATION_URL, DELEGATES, null, - USER_ACCOUNT_CLIENT_ID, USER_ACCOUNT_CLIENT_SECRET, REFRESH_TOKEN); + GenericJson json = + buildImpersonationCredentialsJson( + IMPERSONATION_URL, + DELEGATES, + null, + USER_ACCOUNT_CLIENT_ID, + USER_ACCOUNT_CLIENT_SECRET, + REFRESH_TOKEN); MockIAMCredentialsServiceTransportFactory mtransportFactory = new MockIAMCredentialsServiceTransportFactory(); ImpersonatedCredentials credentials = ImpersonatedCredentials.fromJson(json, mtransportFactory); @@ -188,7 +204,8 @@ public void fromJson_userAsSource_WithoutQuotaProjectId() throws IOException { @Test() public void fromJson_ServiceAccountAsSource() throws IOException { - GenericJson json = buildImpersonationCredentialsJson(IMPERSONATION_URL, DELEGATES, QUOTA_PROJECT_ID); + GenericJson json = + buildImpersonationCredentialsJson(IMPERSONATION_URL, DELEGATES, QUOTA_PROJECT_ID); MockIAMCredentialsServiceTransportFactory mtransportFactory = new MockIAMCredentialsServiceTransportFactory(); ImpersonatedCredentials credentials = ImpersonatedCredentials.fromJson(json, mtransportFactory); @@ -248,13 +265,13 @@ public void createScoped() throws IOException { mtransportFactory, QUOTA_PROJECT_ID); - ImpersonatedCredentials scoped_credentials = (ImpersonatedCredentials) targetCredentials - .createScoped(Arrays.asList("scope1", "scope2")); + ImpersonatedCredentials scoped_credentials = + (ImpersonatedCredentials) targetCredentials.createScoped(Arrays.asList("scope1", "scope2")); assertEquals(targetCredentials.getAccount(), scoped_credentials.getAccount()); assertEquals(targetCredentials.getDelegates(), scoped_credentials.getDelegates()); assertEquals(targetCredentials.getLifetime(), scoped_credentials.getLifetime()); - assertEquals(targetCredentials.getSourceCredentials(), - scoped_credentials.getSourceCredentials()); + assertEquals( + targetCredentials.getSourceCredentials(), scoped_credentials.getSourceCredentials()); assertEquals(targetCredentials.getQuotaProjectId(), scoped_credentials.getQuotaProjectId()); assertEquals(Arrays.asList("scope1", "scope2"), scoped_credentials.getScopes()); } @@ -903,8 +920,12 @@ private String generateErrorJson( } static GenericJson buildImpersonationCredentialsJson( - String impersonationUrl, List delegates, String quotaProjectId, - String sourceClientId, String sourceClientSecret, String sourceRefreshToken) { + String impersonationUrl, + List delegates, + String quotaProjectId, + String sourceClientId, + String sourceClientSecret, + String sourceRefreshToken) { GenericJson sourceJson = new GenericJson(); sourceJson.put("client_id", sourceClientId); @@ -935,7 +956,8 @@ static GenericJson buildImpersonationCredentialsJson( sourceJson.put("auth_uri", "https://oauth2.googleapis.com/o/oauth2/auth"); sourceJson.put("token_uri", "https://oauth2.googleapis.com/token"); sourceJson.put("auth_provider_x509_cert_url", "https://www.googleapis.com/oauth2/v1/certs"); - sourceJson.put("client_x509_cert_url", + sourceJson.put( + "client_x509_cert_url", "https://www.googleapis.com/robot/v1/metadata/x509/chaoren-test-sc%40cloudsdktest.iam.gserviceaccount.com"); GenericJson json = new GenericJson(); @@ -949,11 +971,10 @@ static GenericJson buildImpersonationCredentialsJson( return json; } - static InputStream writeImpersonationCredentialsStream(String impersonationUrl, - List delegates, String quotaProjectId) - throws IOException { - GenericJson json = buildImpersonationCredentialsJson(impersonationUrl, delegates, - quotaProjectId); + static InputStream writeImpersonationCredentialsStream( + String impersonationUrl, List delegates, String quotaProjectId) throws IOException { + GenericJson json = + buildImpersonationCredentialsJson(impersonationUrl, delegates, quotaProjectId); return TestUtils.jsonToInputStream(json); } } From 8b3884421c49f4a9f2926cf3dd05cd85f3ab3135 Mon Sep 17 00:00:00 2001 From: Chaoren Liu Date: Thu, 25 Mar 2021 16:03:12 -0400 Subject: [PATCH 05/22] Fix linter errors in ImpersonatedCredentialsTest --- .../com/google/auth/oauth2/ImpersonatedCredentialsTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/oauth2_http/javatests/com/google/auth/oauth2/ImpersonatedCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/ImpersonatedCredentialsTest.java index d324863d7..ee35dd996 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/ImpersonatedCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/ImpersonatedCredentialsTest.java @@ -50,9 +50,9 @@ import com.google.api.client.testing.http.MockLowLevelHttpRequest; import com.google.api.client.util.Clock; import com.google.auth.ServiceAccountSigner.SigningException; +import com.google.auth.TestUtils; import com.google.auth.http.HttpTransportFactory; import com.google.auth.oauth2.GoogleCredentialsTest.MockTokenServerTransportFactory; -import com.google.auth.TestUtils; import com.google.common.collect.ImmutableList; import java.io.ByteArrayOutputStream; import java.io.IOException; From 3a98164e83e5f1b90256bbb84b16d3e956d6593b Mon Sep 17 00:00:00 2001 From: Chaoren Liu Date: Thu, 1 Apr 2021 13:30:36 -0400 Subject: [PATCH 06/22] Fix issues after receiving comments --- .../auth/oauth2/CredentialTypeException.java | 40 ------------------- .../google/auth/oauth2/GoogleCredentials.java | 8 +++- .../auth/oauth2/ImpersonatedCredentials.java | 16 +++++--- .../auth/oauth2/GoogleCredentialsTest.java | 34 +++++++++++++++- 4 files changed, 50 insertions(+), 48 deletions(-) delete mode 100644 oauth2_http/java/com/google/auth/oauth2/CredentialTypeException.java diff --git a/oauth2_http/java/com/google/auth/oauth2/CredentialTypeException.java b/oauth2_http/java/com/google/auth/oauth2/CredentialTypeException.java deleted file mode 100644 index c33353c4e..000000000 --- a/oauth2_http/java/com/google/auth/oauth2/CredentialTypeException.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2021 Google LLC - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * - * * Neither the name of Google LLC nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package com.google.auth.oauth2; - -import java.io.IOException; - -/** Indicates that the provided credential has a unsupported type. */ -public class CredentialTypeException extends IOException { - CredentialTypeException(String message) { - super(message); - } -} diff --git a/oauth2_http/java/com/google/auth/oauth2/GoogleCredentials.java b/oauth2_http/java/com/google/auth/oauth2/GoogleCredentials.java index 30bbd2c85..b783acb48 100644 --- a/oauth2_http/java/com/google/auth/oauth2/GoogleCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/GoogleCredentials.java @@ -79,8 +79,12 @@ public static GoogleCredentials create(AccessToken accessToken) { *
    *
  1. Credentials file pointed to by the {@code GOOGLE_APPLICATION_CREDENTIALS} environment * variable - *
  2. Credentials provided by the Google Cloud SDK {@code gcloud auth application-default - * login} command + *
  3. Credentials provided by the Google Cloud SDK. + *
      + *
    1. {@code gcloud auth application-default login} for user account credentials. + *
    2. {@code gcloud auth application-default login --impersonate-service-account} for + * impersonated service account credentials. + *
    *
  4. Google App Engine built-in credentials *
  5. Google Cloud Shell built-in credentials *
  6. Google Compute Engine built-in credentials diff --git a/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java b/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java index 4d95cd204..f19059905 100644 --- a/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java @@ -164,8 +164,10 @@ public static ImpersonatedCredentials create( * hours). If the given lifetime is 0, default value 3600 will be used instead when creating * the credentials. * @param transportFactory HTTP transport factory that creates the transport used to get access - * tokens - * @param quotaProjectId the project used for quota and billing purposes. May be null. + * tokens. + * @param quotaProjectId the project used for quota and billing purposes. Should be null unless + * the caller wants to use a project different from the one that owns the impersonated + * credential for billing/quota purposes. * @return new credentials */ public static ImpersonatedCredentials create( @@ -293,6 +295,8 @@ public byte[] sign(byte[] toSign) { /** * Returns impersonation account credentials defined by JSON using the format generated by gCloud. + * The source credentials in the JSON must be either user account credentials or service account + * credentials. * * @param json a map from the JSON representing the credentials * @param transportFactory HTTP transport factory, creates the transport used to get access tokens @@ -300,7 +304,8 @@ public byte[] sign(byte[] toSign) { * @throws IOException if the credential cannot be created from the JSON. */ static ImpersonatedCredentials fromJson( - Map json, HttpTransportFactory transportFactory) throws IOException { + Map json, HttpTransportFactory transportFactory) + throws IOException { checkNotNull(json); checkNotNull(transportFactory); @@ -319,9 +324,10 @@ static ImpersonatedCredentials fromJson( sourceCredentials = ServiceAccountCredentials.fromJson(sourceCredentialsJson, transportFactory); } else { - throw new CredentialTypeException( + throw new IOException( String.format( - "The source_credentials of %s type has an unsupported type.", sourceCredentialsType)); + "A credential of type %s is not supported as source credential for impersonation.", + sourceCredentialsType)); } // optional String quotaProjectId = (String) json.get("quota_project_id"); diff --git a/oauth2_http/javatests/com/google/auth/oauth2/GoogleCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/GoogleCredentialsTest.java index b21ff0a74..8efb5cb5b 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/GoogleCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/GoogleCredentialsTest.java @@ -32,6 +32,7 @@ package com.google.auth.oauth2; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -272,7 +273,7 @@ public void fromStream_awsCredentials_providesToken() throws IOException { } @Test - public void fromStream_Impersonation_providesToken() throws IOException { + public void fromStream_Impersonation_providesToken_WithQuotaProject() throws IOException { MockTokenServerTransportFactory transportFactoryForSource = new MockTokenServerTransportFactory(); transportFactoryForSource.transport.addServiceAccount( @@ -306,6 +307,37 @@ public void fromStream_Impersonation_providesToken() throws IOException { assertEquals(ImpersonatedCredentialsTest.QUOTA_PROJECT_ID, headerValues.get(0)); } + @Test + public void fromStream_Impersonation_providesToken_WithoutQuotaProject() throws IOException { + MockTokenServerTransportFactory transportFactoryForSource = + new MockTokenServerTransportFactory(); + transportFactoryForSource.transport.addServiceAccount( + ImpersonatedCredentialsTest.SA_CLIENT_EMAIL, ImpersonatedCredentialsTest.ACCESS_TOKEN); + + MockIAMCredentialsServiceTransportFactory transportFactory = + new MockIAMCredentialsServiceTransportFactory(); + transportFactory.transport.setTargetPrincipal( + ImpersonatedCredentialsTest.IMPERSONATED_CLIENT_EMAIL); + transportFactory.transport.setAccessToken(ImpersonatedCredentialsTest.ACCESS_TOKEN); + transportFactory.transport.setExpireTime(ImpersonatedCredentialsTest.getDefaultExpireTime()); + + InputStream impersonationCredentialsStream = + ImpersonatedCredentialsTest.writeImpersonationCredentialsStream( + ImpersonatedCredentialsTest.IMPERSONATION_URL, + ImpersonatedCredentialsTest.DELEGATES, null); + + ImpersonatedCredentials credentials = + (ImpersonatedCredentials) + GoogleCredentials.fromStream(impersonationCredentialsStream, transportFactoryForSource); + credentials.setTransportFactory(transportFactory); + + assertNotNull(credentials); + Map> metadata = credentials.getRequestMetadata(CALL_URI); + TestUtils.assertContainsBearerToken(metadata, ImpersonatedCredentialsTest.ACCESS_TOKEN); + + assertFalse(metadata.containsKey("x-goog-user-project")); + } + @Test public void createScoped_overloadCallsImplementation() { final AtomicReference> called = new AtomicReference<>(); From 815e39b93e9d0574a87e6246154fe9042fff3618 Mon Sep 17 00:00:00 2001 From: Chaoren Liu Date: Thu, 1 Apr 2021 14:18:25 -0400 Subject: [PATCH 07/22] Fix lint errors --- .../java/com/google/auth/oauth2/ImpersonatedCredentials.java | 3 +-- .../com/google/auth/oauth2/GoogleCredentialsTest.java | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java b/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java index f19059905..78d33aa8d 100644 --- a/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java @@ -304,8 +304,7 @@ public byte[] sign(byte[] toSign) { * @throws IOException if the credential cannot be created from the JSON. */ static ImpersonatedCredentials fromJson( - Map json, HttpTransportFactory transportFactory) - throws IOException { + Map json, HttpTransportFactory transportFactory) throws IOException { checkNotNull(json); checkNotNull(transportFactory); diff --git a/oauth2_http/javatests/com/google/auth/oauth2/GoogleCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/GoogleCredentialsTest.java index 8efb5cb5b..1d7e1422d 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/GoogleCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/GoogleCredentialsTest.java @@ -324,7 +324,8 @@ public void fromStream_Impersonation_providesToken_WithoutQuotaProject() throws InputStream impersonationCredentialsStream = ImpersonatedCredentialsTest.writeImpersonationCredentialsStream( ImpersonatedCredentialsTest.IMPERSONATION_URL, - ImpersonatedCredentialsTest.DELEGATES, null); + ImpersonatedCredentialsTest.DELEGATES, + null); ImpersonatedCredentials credentials = (ImpersonatedCredentials) From 0ee39aa51d2e272b5c85b5f59b6d3bd0408e98af Mon Sep 17 00:00:00 2001 From: Chaoren Liu Date: Fri, 2 Apr 2021 14:48:35 -0400 Subject: [PATCH 08/22] Handle ClassCastException in fromJson --- .../auth/oauth2/ImpersonatedCredentials.java | 30 ++++++++++++------- .../oauth2/ImpersonatedCredentialsTest.java | 20 +++++++++++++ 2 files changed, 40 insertions(+), 10 deletions(-) diff --git a/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java b/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java index 78d33aa8d..82583489e 100644 --- a/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java @@ -109,7 +109,8 @@ public class ImpersonatedCredentials extends GoogleCredentials private transient HttpTransportFactory transportFactory; /** - * @param sourceCredentials the source credential used as to acquire the impersonated credentials + * @param sourceCredentials the source credential used as to acquire the impersonated credentials. + * It should be either a user account credential or a service account credential. * @param targetPrincipal the service account to impersonate * @param delegates the chained list of delegates required to grant the final access_token. If * set, the sequence of identities must have "Service Account Token Creator" capability @@ -147,7 +148,8 @@ public static ImpersonatedCredentials create( } /** - * @param sourceCredentials the source credential used as to acquire the impersonated credentials + * @param sourceCredentials the source credential used as to acquire the impersonated credentials. + * It should be either a user account credential or a service account credential. * @param targetPrincipal the service account to impersonate * @param delegates the chained list of delegates required to grant the final access_token. If * set, the sequence of identities must have "Service Account Token Creator" capability @@ -190,7 +192,8 @@ public static ImpersonatedCredentials create( } /** - * @param sourceCredentials the source credential used as to acquire the impersonated credentials + * @param sourceCredentials the source credential used as to acquire the impersonated credentials. + * It should be either a user account credential or a service account credential. * @param targetPrincipal the service account to impersonate * @param delegates the chained list of delegates required to grant the final access_token. If * set, the sequence of identities must have "Service Account Token Creator" capability @@ -295,7 +298,7 @@ public byte[] sign(byte[] toSign) { /** * Returns impersonation account credentials defined by JSON using the format generated by gCloud. - * The source credentials in the JSON must be either user account credentials or service account + * The source credentials in the JSON should be either user account credentials or service account * credentials. * * @param json a map from the JSON representing the credentials @@ -309,14 +312,21 @@ static ImpersonatedCredentials fromJson( checkNotNull(json); checkNotNull(transportFactory); - String serviceAccountImpersonationUrl = (String) json.get("service_account_impersonation_url"); + String serviceAccountImpersonationUrl; + List delegates; + Map sourceCredentialsJson; + String sourceCredentialsType; + try { + serviceAccountImpersonationUrl = (String) json.get("service_account_impersonation_url"); + delegates = (List) json.get("delegates"); + sourceCredentialsJson = (Map) json.get("source_credentials"); + sourceCredentialsType = (String) sourceCredentialsJson.get("type"); + } catch (ClassCastException | NullPointerException e) { + throw new CredentialFormatException("An invalid input stream was provided.", e); + } String targetPrincipal = extractTargetPrincipal(serviceAccountImpersonationUrl); - List delegates = (List) json.get("delegates"); - Map sourceCredentialsJson = - (Map) json.get("source_credentials"); - String sourceCredentialsType = (String) sourceCredentialsJson.get("type"); - GoogleCredentials sourceCredentials = null; + GoogleCredentials sourceCredentials; if (GoogleCredentials.USER_FILE_TYPE.equals(sourceCredentialsType)) { sourceCredentials = UserCredentials.fromJson(sourceCredentialsJson, transportFactory); } else if (GoogleCredentials.SERVICE_ACCOUNT_FILE_TYPE.equals(sourceCredentialsType)) { diff --git a/oauth2_http/javatests/com/google/auth/oauth2/ImpersonatedCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/ImpersonatedCredentialsTest.java index ee35dd996..d6460a752 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/ImpersonatedCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/ImpersonatedCredentialsTest.java @@ -218,6 +218,20 @@ public void fromJson_ServiceAccountAsSource() throws IOException { assertTrue(sourceCredentials instanceof ServiceAccountCredentials); } + @Test() + public void fromJson_InvalidFormat() throws IOException { + GenericJson json = + buildInvalidCredentialsJson(); + MockIAMCredentialsServiceTransportFactory mtransportFactory = + new MockIAMCredentialsServiceTransportFactory(); + try { + ImpersonatedCredentials.fromJson(json, mtransportFactory); + fail("An exception should be thrown."); + } catch (CredentialFormatException e) { + assertEquals("An invalid input stream was provided.", e.getMessage()); + } + } + @Test() public void createScopedRequired_True() throws IOException { GoogleCredentials sourceCredentials = getSourceCredentials(); @@ -971,6 +985,12 @@ static GenericJson buildImpersonationCredentialsJson( return json; } + static GenericJson buildInvalidCredentialsJson() { + GenericJson json = new GenericJson(); + json.put("service_account_impersonation_url", "mock_url"); + return json; + } + static InputStream writeImpersonationCredentialsStream( String impersonationUrl, List delegates, String quotaProjectId) throws IOException { GenericJson json = From 35b347a3627f9cecff083945a7949161c066798a Mon Sep 17 00:00:00 2001 From: Chaoren Liu Date: Fri, 2 Apr 2021 14:51:06 -0400 Subject: [PATCH 09/22] Fix lint errors --- .../java/com/google/auth/oauth2/ImpersonatedCredentials.java | 2 +- .../com/google/auth/oauth2/ImpersonatedCredentialsTest.java | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java b/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java index 82583489e..31f437bf0 100644 --- a/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java @@ -321,7 +321,7 @@ static ImpersonatedCredentials fromJson( delegates = (List) json.get("delegates"); sourceCredentialsJson = (Map) json.get("source_credentials"); sourceCredentialsType = (String) sourceCredentialsJson.get("type"); - } catch (ClassCastException | NullPointerException e) { + } catch (ClassCastException | NullPointerException e) { throw new CredentialFormatException("An invalid input stream was provided.", e); } String targetPrincipal = extractTargetPrincipal(serviceAccountImpersonationUrl); diff --git a/oauth2_http/javatests/com/google/auth/oauth2/ImpersonatedCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/ImpersonatedCredentialsTest.java index d6460a752..59ef9f5ec 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/ImpersonatedCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/ImpersonatedCredentialsTest.java @@ -220,8 +220,7 @@ public void fromJson_ServiceAccountAsSource() throws IOException { @Test() public void fromJson_InvalidFormat() throws IOException { - GenericJson json = - buildInvalidCredentialsJson(); + GenericJson json = buildInvalidCredentialsJson(); MockIAMCredentialsServiceTransportFactory mtransportFactory = new MockIAMCredentialsServiceTransportFactory(); try { From 56ed2bb018c16867a1d7f803547e2922aae2e68e Mon Sep 17 00:00:00 2001 From: Chaoren Liu Date: Fri, 2 Apr 2021 15:05:54 -0400 Subject: [PATCH 10/22] minor refactoring --- .../java/com/google/auth/oauth2/ImpersonatedCredentials.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java b/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java index 31f437bf0..93b67f99f 100644 --- a/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java @@ -316,11 +316,13 @@ static ImpersonatedCredentials fromJson( List delegates; Map sourceCredentialsJson; String sourceCredentialsType; + String quotaProjectId; try { serviceAccountImpersonationUrl = (String) json.get("service_account_impersonation_url"); delegates = (List) json.get("delegates"); sourceCredentialsJson = (Map) json.get("source_credentials"); sourceCredentialsType = (String) sourceCredentialsJson.get("type"); + quotaProjectId = (String) json.get("quota_project_id"); } catch (ClassCastException | NullPointerException e) { throw new CredentialFormatException("An invalid input stream was provided.", e); } @@ -338,8 +340,6 @@ static ImpersonatedCredentials fromJson( "A credential of type %s is not supported as source credential for impersonation.", sourceCredentialsType)); } - // optional - String quotaProjectId = (String) json.get("quota_project_id"); return ImpersonatedCredentials.newBuilder() .setSourceCredentials(sourceCredentials) .setTargetPrincipal(targetPrincipal) From a36da6062a95f8e2caec551cc1dfff4fb326af8d Mon Sep 17 00:00:00 2001 From: Chaoren Liu Date: Fri, 2 Apr 2021 18:09:11 -0400 Subject: [PATCH 11/22] fix doc strings --- .../com/google/auth/oauth2/ImpersonatedCredentials.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java b/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java index 93b67f99f..9f3398937 100644 --- a/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java @@ -109,7 +109,7 @@ public class ImpersonatedCredentials extends GoogleCredentials private transient HttpTransportFactory transportFactory; /** - * @param sourceCredentials the source credential used as to acquire the impersonated credentials. + * @param sourceCredentials the source credential used to acquire the impersonated credentials * It should be either a user account credential or a service account credential. * @param targetPrincipal the service account to impersonate * @param delegates the chained list of delegates required to grant the final access_token. If @@ -148,7 +148,7 @@ public static ImpersonatedCredentials create( } /** - * @param sourceCredentials the source credential used as to acquire the impersonated credentials. + * @param sourceCredentials the source credential used to acquire the impersonated credentials * It should be either a user account credential or a service account credential. * @param targetPrincipal the service account to impersonate * @param delegates the chained list of delegates required to grant the final access_token. If @@ -192,7 +192,7 @@ public static ImpersonatedCredentials create( } /** - * @param sourceCredentials the source credential used as to acquire the impersonated credentials. + * @param sourceCredentials the source credential used to acquire the impersonated credentials * It should be either a user account credential or a service account credential. * @param targetPrincipal the service account to impersonate * @param delegates the chained list of delegates required to grant the final access_token. If From 1b3bcd64a1322d98ed6afe287495b0396a175f07 Mon Sep 17 00:00:00 2001 From: Chaoren Liu Date: Fri, 2 Apr 2021 18:11:39 -0400 Subject: [PATCH 12/22] fix lint errors --- .../google/auth/oauth2/ImpersonatedCredentials.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java b/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java index 9f3398937..5340af4cb 100644 --- a/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java @@ -109,8 +109,8 @@ public class ImpersonatedCredentials extends GoogleCredentials private transient HttpTransportFactory transportFactory; /** - * @param sourceCredentials the source credential used to acquire the impersonated credentials - * It should be either a user account credential or a service account credential. + * @param sourceCredentials the source credential used to acquire the impersonated credentials It + * should be either a user account credential or a service account credential. * @param targetPrincipal the service account to impersonate * @param delegates the chained list of delegates required to grant the final access_token. If * set, the sequence of identities must have "Service Account Token Creator" capability @@ -148,8 +148,8 @@ public static ImpersonatedCredentials create( } /** - * @param sourceCredentials the source credential used to acquire the impersonated credentials - * It should be either a user account credential or a service account credential. + * @param sourceCredentials the source credential used to acquire the impersonated credentials It + * should be either a user account credential or a service account credential. * @param targetPrincipal the service account to impersonate * @param delegates the chained list of delegates required to grant the final access_token. If * set, the sequence of identities must have "Service Account Token Creator" capability @@ -192,8 +192,8 @@ public static ImpersonatedCredentials create( } /** - * @param sourceCredentials the source credential used to acquire the impersonated credentials - * It should be either a user account credential or a service account credential. + * @param sourceCredentials the source credential used to acquire the impersonated credentials It + * should be either a user account credential or a service account credential. * @param targetPrincipal the service account to impersonate * @param delegates the chained list of delegates required to grant the final access_token. If * set, the sequence of identities must have "Service Account Token Creator" capability From 685ea43cfa16aef2506fcb2e0052b719116e2332 Mon Sep 17 00:00:00 2001 From: Chaoren Liu Date: Fri, 2 Apr 2021 23:55:17 -0400 Subject: [PATCH 13/22] delegates can be missing from the json file --- .../auth/oauth2/ImpersonatedCredentials.java | 6 +++-- .../oauth2/ImpersonatedCredentialsTest.java | 22 +++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java b/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java index 5340af4cb..971e1d788 100644 --- a/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java @@ -313,13 +313,15 @@ static ImpersonatedCredentials fromJson( checkNotNull(transportFactory); String serviceAccountImpersonationUrl; - List delegates; + List delegates = null; Map sourceCredentialsJson; String sourceCredentialsType; String quotaProjectId; try { serviceAccountImpersonationUrl = (String) json.get("service_account_impersonation_url"); - delegates = (List) json.get("delegates"); + if (json.containsKey("delegates")) { + delegates = (List) json.get("delegates"); + } sourceCredentialsJson = (Map) json.get("source_credentials"); sourceCredentialsType = (String) sourceCredentialsJson.get("type"); quotaProjectId = (String) json.get("quota_project_id"); diff --git a/oauth2_http/javatests/com/google/auth/oauth2/ImpersonatedCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/ImpersonatedCredentialsTest.java index 59ef9f5ec..e3e5e0771 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/ImpersonatedCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/ImpersonatedCredentialsTest.java @@ -202,6 +202,28 @@ public void fromJson_userAsSource_WithoutQuotaProjectId() throws IOException { assertTrue(sourceCredentials instanceof UserCredentials); } + public void fromJson_userAsSource_MissingDelegatesField() throws IOException { + GenericJson json = + buildImpersonationCredentialsJson( + IMPERSONATION_URL, + DELEGATES, + null, + USER_ACCOUNT_CLIENT_ID, + USER_ACCOUNT_CLIENT_SECRET, + REFRESH_TOKEN); + json.remove("delegates"); + MockIAMCredentialsServiceTransportFactory mtransportFactory = + new MockIAMCredentialsServiceTransportFactory(); + ImpersonatedCredentials credentials = ImpersonatedCredentials.fromJson(json, mtransportFactory); + assertEquals(IMPERSONATED_CLIENT_EMAIL, credentials.getAccount()); + assertNull(credentials.getQuotaProjectId()); + assertEquals(new ArrayList(), credentials.getDelegates()); + assertEquals(new ArrayList(), credentials.getScopes()); + assertEquals(credentials.getLifetime(), 3600); + GoogleCredentials sourceCredentials = credentials.getSourceCredentials(); + assertTrue(sourceCredentials instanceof UserCredentials); + } + @Test() public void fromJson_ServiceAccountAsSource() throws IOException { GenericJson json = From 3a20de449894b6bd2e5e4609e951c80b0c721339 Mon Sep 17 00:00:00 2001 From: Chaoren Liu Date: Fri, 2 Apr 2021 23:57:15 -0400 Subject: [PATCH 14/22] Mark test using @Test() --- .../com/google/auth/oauth2/ImpersonatedCredentialsTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/oauth2_http/javatests/com/google/auth/oauth2/ImpersonatedCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/ImpersonatedCredentialsTest.java index e3e5e0771..954e49eed 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/ImpersonatedCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/ImpersonatedCredentialsTest.java @@ -202,6 +202,7 @@ public void fromJson_userAsSource_WithoutQuotaProjectId() throws IOException { assertTrue(sourceCredentials instanceof UserCredentials); } + @Test() public void fromJson_userAsSource_MissingDelegatesField() throws IOException { GenericJson json = buildImpersonationCredentialsJson( From e3f4a66c80d109945ce5c2f7789ff874cc048768 Mon Sep 17 00:00:00 2001 From: Chaoren Liu Date: Fri, 23 Apr 2021 10:30:49 -0400 Subject: [PATCH 15/22] Remove redundant methods and handle exceptions --- .../java/com/google/auth/oauth2/GoogleCredentials.java | 3 +-- .../com/google/auth/oauth2/ImpersonatedCredentials.java | 9 +++------ .../google/auth/oauth2/ImpersonatedCredentialsTest.java | 8 ++++---- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/oauth2_http/java/com/google/auth/oauth2/GoogleCredentials.java b/oauth2_http/java/com/google/auth/oauth2/GoogleCredentials.java index b783acb48..af7cc8ecd 100644 --- a/oauth2_http/java/com/google/auth/oauth2/GoogleCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/GoogleCredentials.java @@ -54,7 +54,6 @@ public class GoogleCredentials extends OAuth2Credentials { static final String QUOTA_PROJECT_ID_HEADER_KEY = "x-goog-user-project"; static final String USER_FILE_TYPE = "authorized_user"; static final String SERVICE_ACCOUNT_FILE_TYPE = "service_account"; - static final String IMPERSONATED_SERVICE_ACCOUNT = "impersonated_service_account"; private static final DefaultCredentialsProvider defaultCredentialsProvider = new DefaultCredentialsProvider(); @@ -174,7 +173,7 @@ public static GoogleCredentials fromStream( if (ExternalAccountCredentials.EXTERNAL_ACCOUNT_FILE_TYPE.equals(fileType)) { return ExternalAccountCredentials.fromJson(fileContents, transportFactory); } - if (IMPERSONATED_SERVICE_ACCOUNT.equals(fileType)) { + if ("impersonated_service_account".equals(fileType)) { return ImpersonatedCredentials.fromJson(fileContents, transportFactory); } throw new IOException( diff --git a/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java b/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java index 971e1d788..e3527983b 100644 --- a/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java @@ -317,6 +317,7 @@ static ImpersonatedCredentials fromJson( Map sourceCredentialsJson; String sourceCredentialsType; String quotaProjectId; + String targetPrincipal; try { serviceAccountImpersonationUrl = (String) json.get("service_account_impersonation_url"); if (json.containsKey("delegates")) { @@ -325,10 +326,10 @@ static ImpersonatedCredentials fromJson( sourceCredentialsJson = (Map) json.get("source_credentials"); sourceCredentialsType = (String) sourceCredentialsJson.get("type"); quotaProjectId = (String) json.get("quota_project_id"); - } catch (ClassCastException | NullPointerException e) { + targetPrincipal = extractTargetPrincipal(serviceAccountImpersonationUrl); + } catch (ClassCastException | NullPointerException | IllegalArgumentException e) { throw new CredentialFormatException("An invalid input stream was provided.", e); } - String targetPrincipal = extractTargetPrincipal(serviceAccountImpersonationUrl); GoogleCredentials sourceCredentials; if (GoogleCredentials.USER_FILE_TYPE.equals(sourceCredentialsType)) { @@ -600,10 +601,6 @@ public Builder setQuotaProjectId(String quotaProjectId) { return this; } - public String getQuotaProjectId() { - return quotaProjectId; - } - public ImpersonatedCredentials build() { return new ImpersonatedCredentials(this); } diff --git a/oauth2_http/javatests/com/google/auth/oauth2/ImpersonatedCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/ImpersonatedCredentialsTest.java index 954e49eed..5daa03361 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/ImpersonatedCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/ImpersonatedCredentialsTest.java @@ -175,7 +175,7 @@ public void fromJson_userAsSource_WithQuotaProjectId() throws IOException { assertEquals(QUOTA_PROJECT_ID, credentials.getQuotaProjectId()); assertEquals(DELEGATES, credentials.getDelegates()); assertEquals(new ArrayList(), credentials.getScopes()); - assertEquals(credentials.getLifetime(), 3600); + assertEquals(3600, credentials.getLifetime()); GoogleCredentials sourceCredentials = credentials.getSourceCredentials(); assertTrue(sourceCredentials instanceof UserCredentials); } @@ -197,7 +197,7 @@ public void fromJson_userAsSource_WithoutQuotaProjectId() throws IOException { assertNull(credentials.getQuotaProjectId()); assertEquals(DELEGATES, credentials.getDelegates()); assertEquals(new ArrayList(), credentials.getScopes()); - assertEquals(credentials.getLifetime(), 3600); + assertEquals(3600, credentials.getLifetime()); GoogleCredentials sourceCredentials = credentials.getSourceCredentials(); assertTrue(sourceCredentials instanceof UserCredentials); } @@ -220,7 +220,7 @@ public void fromJson_userAsSource_MissingDelegatesField() throws IOException { assertNull(credentials.getQuotaProjectId()); assertEquals(new ArrayList(), credentials.getDelegates()); assertEquals(new ArrayList(), credentials.getScopes()); - assertEquals(credentials.getLifetime(), 3600); + assertEquals(3600, credentials.getLifetime()); GoogleCredentials sourceCredentials = credentials.getSourceCredentials(); assertTrue(sourceCredentials instanceof UserCredentials); } @@ -236,7 +236,7 @@ public void fromJson_ServiceAccountAsSource() throws IOException { assertEquals(QUOTA_PROJECT_ID, credentials.getQuotaProjectId()); assertEquals(DELEGATES, credentials.getDelegates()); assertEquals(new ArrayList(), credentials.getScopes()); - assertEquals(credentials.getLifetime(), 3600); + assertEquals(3600, credentials.getLifetime()); GoogleCredentials sourceCredentials = credentials.getSourceCredentials(); assertTrue(sourceCredentials instanceof ServiceAccountCredentials); } From 097245edeaefa609129b486ac8bf5136eeca844a Mon Sep 17 00:00:00 2001 From: Chaoren Liu Date: Fri, 23 Apr 2021 11:25:08 -0400 Subject: [PATCH 16/22] add an empty file --- empty | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 empty diff --git a/empty b/empty new file mode 100644 index 000000000..e69de29bb From 9a428d752a1d47da1ad668866bea0c7a234e1539 Mon Sep 17 00:00:00 2001 From: Chaoren Liu Date: Fri, 23 Apr 2021 11:25:43 -0400 Subject: [PATCH 17/22] remove an empty file --- empty | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 empty diff --git a/empty b/empty deleted file mode 100644 index e69de29bb..000000000 From 2febd2a87a0a282df9735ffb4b4f008e0ee3aad1 Mon Sep 17 00:00:00 2001 From: Chaoren Liu Date: Mon, 26 Apr 2021 13:57:36 -0400 Subject: [PATCH 18/22] Fix docstring and move one variable to inner scope. --- .../google/auth/oauth2/ImpersonatedCredentials.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java b/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java index e3527983b..454ae84b8 100644 --- a/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java @@ -109,7 +109,7 @@ public class ImpersonatedCredentials extends GoogleCredentials private transient HttpTransportFactory transportFactory; /** - * @param sourceCredentials the source credential used to acquire the impersonated credentials It + * @param sourceCredentials the source credential used to acquire the impersonated credentials. It * should be either a user account credential or a service account credential. * @param targetPrincipal the service account to impersonate * @param delegates the chained list of delegates required to grant the final access_token. If @@ -148,7 +148,7 @@ public static ImpersonatedCredentials create( } /** - * @param sourceCredentials the source credential used to acquire the impersonated credentials It + * @param sourceCredentials the source credential used to acquire the impersonated credentials. It * should be either a user account credential or a service account credential. * @param targetPrincipal the service account to impersonate * @param delegates the chained list of delegates required to grant the final access_token. If @@ -192,7 +192,7 @@ public static ImpersonatedCredentials create( } /** - * @param sourceCredentials the source credential used to acquire the impersonated credentials It + * @param sourceCredentials the source credential used to acquire the impersonated credentials. It * should be either a user account credential or a service account credential. * @param targetPrincipal the service account to impersonate * @param delegates the chained list of delegates required to grant the final access_token. If @@ -312,14 +312,14 @@ static ImpersonatedCredentials fromJson( checkNotNull(json); checkNotNull(transportFactory); - String serviceAccountImpersonationUrl; List delegates = null; Map sourceCredentialsJson; String sourceCredentialsType; String quotaProjectId; String targetPrincipal; try { - serviceAccountImpersonationUrl = (String) json.get("service_account_impersonation_url"); + String serviceAccountImpersonationUrl = + (String) json.get("service_account_impersonation_url"); if (json.containsKey("delegates")) { delegates = (List) json.get("delegates"); } From 84205288946e754d4dfe4cfbb43fe75dbeba61fe Mon Sep 17 00:00:00 2001 From: Chaoren Liu Date: Tue, 11 May 2021 00:23:34 -0400 Subject: [PATCH 19/22] Refactor ImpersonatedCredentialsTest --- .../oauth2/ImpersonatedCredentialsTest.java | 328 +++++++----------- 1 file changed, 128 insertions(+), 200 deletions(-) diff --git a/oauth2_http/javatests/com/google/auth/oauth2/ImpersonatedCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/ImpersonatedCredentialsTest.java index 5daa03361..b4dcaf03f 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/ImpersonatedCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/ImpersonatedCredentialsTest.java @@ -66,6 +66,7 @@ import java.util.Date; import java.util.List; import java.util.Map; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -141,6 +142,15 @@ public HttpTransport create() { } } + private GoogleCredentials sourceCredentials; + private MockIAMCredentialsServiceTransportFactory mockTransportFactory; + + @Before + public void setup() throws IOException { + sourceCredentials = getSourceCredentials(); + mockTransportFactory = new MockIAMCredentialsServiceTransportFactory(); + } + private GoogleCredentials getSourceCredentials() throws IOException { MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); PrivateKey privateKey = ServiceAccountCredentials.privateKeyFromPkcs8(SA_PRIVATE_KEY_PKCS8); @@ -168,9 +178,7 @@ public void fromJson_userAsSource_WithQuotaProjectId() throws IOException { USER_ACCOUNT_CLIENT_ID, USER_ACCOUNT_CLIENT_SECRET, REFRESH_TOKEN); - MockIAMCredentialsServiceTransportFactory mtransportFactory = - new MockIAMCredentialsServiceTransportFactory(); - ImpersonatedCredentials credentials = ImpersonatedCredentials.fromJson(json, mtransportFactory); + ImpersonatedCredentials credentials = ImpersonatedCredentials.fromJson(json, mockTransportFactory); assertEquals(IMPERSONATED_CLIENT_EMAIL, credentials.getAccount()); assertEquals(QUOTA_PROJECT_ID, credentials.getQuotaProjectId()); assertEquals(DELEGATES, credentials.getDelegates()); @@ -190,9 +198,7 @@ public void fromJson_userAsSource_WithoutQuotaProjectId() throws IOException { USER_ACCOUNT_CLIENT_ID, USER_ACCOUNT_CLIENT_SECRET, REFRESH_TOKEN); - MockIAMCredentialsServiceTransportFactory mtransportFactory = - new MockIAMCredentialsServiceTransportFactory(); - ImpersonatedCredentials credentials = ImpersonatedCredentials.fromJson(json, mtransportFactory); + ImpersonatedCredentials credentials = ImpersonatedCredentials.fromJson(json, mockTransportFactory); assertEquals(IMPERSONATED_CLIENT_EMAIL, credentials.getAccount()); assertNull(credentials.getQuotaProjectId()); assertEquals(DELEGATES, credentials.getDelegates()); @@ -213,9 +219,7 @@ public void fromJson_userAsSource_MissingDelegatesField() throws IOException { USER_ACCOUNT_CLIENT_SECRET, REFRESH_TOKEN); json.remove("delegates"); - MockIAMCredentialsServiceTransportFactory mtransportFactory = - new MockIAMCredentialsServiceTransportFactory(); - ImpersonatedCredentials credentials = ImpersonatedCredentials.fromJson(json, mtransportFactory); + ImpersonatedCredentials credentials = ImpersonatedCredentials.fromJson(json, mockTransportFactory); assertEquals(IMPERSONATED_CLIENT_EMAIL, credentials.getAccount()); assertNull(credentials.getQuotaProjectId()); assertEquals(new ArrayList(), credentials.getDelegates()); @@ -229,9 +233,7 @@ public void fromJson_userAsSource_MissingDelegatesField() throws IOException { public void fromJson_ServiceAccountAsSource() throws IOException { GenericJson json = buildImpersonationCredentialsJson(IMPERSONATION_URL, DELEGATES, QUOTA_PROJECT_ID); - MockIAMCredentialsServiceTransportFactory mtransportFactory = - new MockIAMCredentialsServiceTransportFactory(); - ImpersonatedCredentials credentials = ImpersonatedCredentials.fromJson(json, mtransportFactory); + ImpersonatedCredentials credentials = ImpersonatedCredentials.fromJson(json, mockTransportFactory); assertEquals(IMPERSONATED_CLIENT_EMAIL, credentials.getAccount()); assertEquals(QUOTA_PROJECT_ID, credentials.getQuotaProjectId()); assertEquals(DELEGATES, credentials.getDelegates()); @@ -244,10 +246,8 @@ public void fromJson_ServiceAccountAsSource() throws IOException { @Test() public void fromJson_InvalidFormat() throws IOException { GenericJson json = buildInvalidCredentialsJson(); - MockIAMCredentialsServiceTransportFactory mtransportFactory = - new MockIAMCredentialsServiceTransportFactory(); try { - ImpersonatedCredentials.fromJson(json, mtransportFactory); + ImpersonatedCredentials.fromJson(json, mockTransportFactory); fail("An exception should be thrown."); } catch (CredentialFormatException e) { assertEquals("An invalid input stream was provided.", e.getMessage()); @@ -255,10 +255,7 @@ public void fromJson_InvalidFormat() throws IOException { } @Test() - public void createScopedRequired_True() throws IOException { - GoogleCredentials sourceCredentials = getSourceCredentials(); - MockIAMCredentialsServiceTransportFactory mtransportFactory = - new MockIAMCredentialsServiceTransportFactory(); + public void createScopedRequired_True() { ImpersonatedCredentials targetCredentials = ImpersonatedCredentials.create( sourceCredentials, @@ -266,15 +263,12 @@ public void createScopedRequired_True() throws IOException { null, new ArrayList(), VALID_LIFETIME, - mtransportFactory); + mockTransportFactory); assertTrue(targetCredentials.createScopedRequired()); } @Test() - public void createScopedRequired_False() throws IOException { - GoogleCredentials sourceCredentials = getSourceCredentials(); - MockIAMCredentialsServiceTransportFactory mtransportFactory = - new MockIAMCredentialsServiceTransportFactory(); + public void createScopedRequired_False() { ImpersonatedCredentials targetCredentials = ImpersonatedCredentials.create( sourceCredentials, @@ -282,15 +276,12 @@ public void createScopedRequired_False() throws IOException { null, SCOPES, VALID_LIFETIME, - mtransportFactory); + mockTransportFactory); assertFalse(targetCredentials.createScopedRequired()); } @Test() - public void createScoped() throws IOException { - GoogleCredentials sourceCredentials = getSourceCredentials(); - MockIAMCredentialsServiceTransportFactory mtransportFactory = - new MockIAMCredentialsServiceTransportFactory(); + public void createScoped() { ImpersonatedCredentials targetCredentials = ImpersonatedCredentials.create( sourceCredentials, @@ -298,7 +289,7 @@ public void createScoped() throws IOException { DELEGATES, SCOPES, VALID_LIFETIME, - mtransportFactory, + mockTransportFactory, QUOTA_PROJECT_ID); ImpersonatedCredentials scoped_credentials = @@ -315,13 +306,10 @@ public void createScoped() throws IOException { @Test() public void refreshAccessToken_unauthorized() throws IOException { - GoogleCredentials sourceCredentials = getSourceCredentials(); String expectedMessage = "The caller does not have permission"; - MockIAMCredentialsServiceTransportFactory mtransportFactory = - new MockIAMCredentialsServiceTransportFactory(); - mtransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); - mtransportFactory.transport.setTokenResponseErrorCode(HttpStatusCodes.STATUS_CODE_UNAUTHORIZED); - mtransportFactory.transport.setTokenResponseErrorContent( + mockTransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); + mockTransportFactory.transport.setTokenResponseErrorCode(HttpStatusCodes.STATUS_CODE_UNAUTHORIZED); + mockTransportFactory.transport.setTokenResponseErrorContent( generateErrorJson( HttpStatusCodes.STATUS_CODE_UNAUTHORIZED, expectedMessage, "global", "forbidden")); ImpersonatedCredentials targetCredentials = @@ -331,7 +319,7 @@ public void refreshAccessToken_unauthorized() throws IOException { null, SCOPES, VALID_LIFETIME, - mtransportFactory); + mockTransportFactory); try { targetCredentials.refreshAccessToken().getTokenValue(); @@ -345,19 +333,16 @@ public void refreshAccessToken_unauthorized() throws IOException { @Test() public void refreshAccessToken_malformedTarget() throws IOException { - GoogleCredentials sourceCredentials = getSourceCredentials(); - MockIAMCredentialsServiceTransportFactory mtransportFactory = - new MockIAMCredentialsServiceTransportFactory(); String invalidTargetEmail = "foo"; String expectedMessage = "Request contains an invalid argument"; - mtransportFactory.transport.setTargetPrincipal(invalidTargetEmail); - mtransportFactory.transport.setTokenResponseErrorCode(HttpStatusCodes.STATUS_CODE_BAD_REQUEST); - mtransportFactory.transport.setTokenResponseErrorContent( + mockTransportFactory.transport.setTargetPrincipal(invalidTargetEmail); + mockTransportFactory.transport.setTokenResponseErrorCode(HttpStatusCodes.STATUS_CODE_BAD_REQUEST); + mockTransportFactory.transport.setTokenResponseErrorContent( generateErrorJson( HttpStatusCodes.STATUS_CODE_BAD_REQUEST, expectedMessage, "global", "badRequest")); ImpersonatedCredentials targetCredentials = ImpersonatedCredentials.create( - sourceCredentials, invalidTargetEmail, null, SCOPES, VALID_LIFETIME, mtransportFactory); + sourceCredentials, invalidTargetEmail, null, SCOPES, VALID_LIFETIME, mockTransportFactory); try { targetCredentials.refreshAccessToken().getTokenValue(); @@ -369,8 +354,7 @@ public void refreshAccessToken_malformedTarget() throws IOException { } @Test() - public void credential_with_zero_lifetime() throws IOException, IllegalStateException { - GoogleCredentials sourceCredentials = getSourceCredentials(); + public void credential_with_zero_lifetime() throws IllegalStateException { ImpersonatedCredentials targetCredentials = ImpersonatedCredentials.create( sourceCredentials, IMPERSONATED_CLIENT_EMAIL, null, SCOPES, 0); @@ -380,7 +364,6 @@ public void credential_with_zero_lifetime() throws IOException, IllegalStateExce @Test() public void credential_with_invalid_lifetime() throws IOException, IllegalStateException { - GoogleCredentials sourceCredentials = getSourceCredentials(); try { ImpersonatedCredentials targetCredentials = ImpersonatedCredentials.create( @@ -398,7 +381,6 @@ public void credential_with_invalid_lifetime() throws IOException, IllegalStateE @Test() public void credential_with_invalid_scope() throws IOException, IllegalStateException { - GoogleCredentials sourceCredentials = getSourceCredentials(); try { ImpersonatedCredentials targetCredentials = ImpersonatedCredentials.create( @@ -415,12 +397,9 @@ public void credential_with_invalid_scope() throws IOException, IllegalStateExce @Test() public void refreshAccessToken_success() throws IOException, IllegalStateException { - GoogleCredentials sourceCredentials = getSourceCredentials(); - MockIAMCredentialsServiceTransportFactory mtransportFactory = - new MockIAMCredentialsServiceTransportFactory(); - mtransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); - mtransportFactory.transport.setAccessToken(ACCESS_TOKEN); - mtransportFactory.transport.setExpireTime(getDefaultExpireTime()); + mockTransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); + mockTransportFactory.transport.setAccessToken(ACCESS_TOKEN); + mockTransportFactory.transport.setExpireTime(getDefaultExpireTime()); ImpersonatedCredentials targetCredentials = ImpersonatedCredentials.create( sourceCredentials, @@ -428,7 +407,7 @@ public void refreshAccessToken_success() throws IOException, IllegalStateExcepti null, SCOPES, VALID_LIFETIME, - mtransportFactory); + mockTransportFactory); assertEquals(ACCESS_TOKEN, targetCredentials.refreshAccessToken().getTokenValue()); } @@ -436,12 +415,9 @@ public void refreshAccessToken_success() throws IOException, IllegalStateExcepti @Test() public void getRequestMetadata_withQuotaProjectId() throws IOException, IllegalStateException { - GoogleCredentials sourceCredentials = getSourceCredentials(); - MockIAMCredentialsServiceTransportFactory mtransportFactory = - new MockIAMCredentialsServiceTransportFactory(); - mtransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); - mtransportFactory.transport.setAccessToken(ACCESS_TOKEN); - mtransportFactory.transport.setExpireTime(getDefaultExpireTime()); + mockTransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); + mockTransportFactory.transport.setAccessToken(ACCESS_TOKEN); + mockTransportFactory.transport.setExpireTime(getDefaultExpireTime()); ImpersonatedCredentials targetCredentials = ImpersonatedCredentials.create( sourceCredentials, @@ -449,7 +425,7 @@ public void getRequestMetadata_withQuotaProjectId() throws IOException, IllegalS null, SCOPES, VALID_LIFETIME, - mtransportFactory, + mockTransportFactory, QUOTA_PROJECT_ID); Map> metadata = targetCredentials.getRequestMetadata(); @@ -462,12 +438,9 @@ public void getRequestMetadata_withQuotaProjectId() throws IOException, IllegalS @Test() public void getRequestMetadata_withoutQuotaProjectId() throws IOException, IllegalStateException { - GoogleCredentials sourceCredentials = getSourceCredentials(); - MockIAMCredentialsServiceTransportFactory mtransportFactory = - new MockIAMCredentialsServiceTransportFactory(); - mtransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); - mtransportFactory.transport.setAccessToken(ACCESS_TOKEN); - mtransportFactory.transport.setExpireTime(getDefaultExpireTime()); + mockTransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); + mockTransportFactory.transport.setAccessToken(ACCESS_TOKEN); + mockTransportFactory.transport.setExpireTime(getDefaultExpireTime()); ImpersonatedCredentials targetCredentials = ImpersonatedCredentials.create( sourceCredentials, @@ -475,7 +448,7 @@ public void getRequestMetadata_withoutQuotaProjectId() throws IOException, Illeg null, SCOPES, VALID_LIFETIME, - mtransportFactory); + mockTransportFactory); Map> metadata = targetCredentials.getRequestMetadata(); assertFalse(metadata.containsKey("x-goog-user-project")); @@ -484,12 +457,9 @@ public void getRequestMetadata_withoutQuotaProjectId() throws IOException, Illeg @Test() public void refreshAccessToken_delegates_success() throws IOException, IllegalStateException { - GoogleCredentials sourceCredentials = getSourceCredentials(); - MockIAMCredentialsServiceTransportFactory mtransportFactory = - new MockIAMCredentialsServiceTransportFactory(); - mtransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); - mtransportFactory.transport.setAccessToken(ACCESS_TOKEN); - mtransportFactory.transport.setExpireTime(getDefaultExpireTime()); + mockTransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); + mockTransportFactory.transport.setAccessToken(ACCESS_TOKEN); + mockTransportFactory.transport.setExpireTime(getDefaultExpireTime()); List delegates = Arrays.asList("delegate-account@iam.gserviceaccount.com"); ImpersonatedCredentials targetCredentials = ImpersonatedCredentials.create( @@ -498,21 +468,18 @@ public void refreshAccessToken_delegates_success() throws IOException, IllegalSt delegates, SCOPES, VALID_LIFETIME, - mtransportFactory); + mockTransportFactory); assertEquals(ACCESS_TOKEN, targetCredentials.refreshAccessToken().getTokenValue()); } @Test() - public void refreshAccessToken_invalidDate() throws IOException, IllegalStateException { + public void refreshAccessToken_invalidDate() throws IllegalStateException { - GoogleCredentials sourceCredentials = getSourceCredentials(); String expectedMessage = "Unparseable date"; - MockIAMCredentialsServiceTransportFactory mtransportFactory = - new MockIAMCredentialsServiceTransportFactory(); - mtransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); - mtransportFactory.transport.setAccessToken("foo"); - mtransportFactory.transport.setExpireTime("1973-09-29T15:01:23"); + mockTransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); + mockTransportFactory.transport.setAccessToken("foo"); + mockTransportFactory.transport.setExpireTime("1973-09-29T15:01:23"); ImpersonatedCredentials targetCredentials = ImpersonatedCredentials.create( sourceCredentials, @@ -520,7 +487,7 @@ public void refreshAccessToken_invalidDate() throws IOException, IllegalStateExc null, SCOPES, VALID_LIFETIME, - mtransportFactory); + mockTransportFactory); try { targetCredentials.refreshAccessToken().getTokenValue(); @@ -531,13 +498,10 @@ public void refreshAccessToken_invalidDate() throws IOException, IllegalStateExc } @Test - public void getAccount_sameAs() throws IOException { - GoogleCredentials sourceCredentials = getSourceCredentials(); - MockIAMCredentialsServiceTransportFactory mtransportFactory = - new MockIAMCredentialsServiceTransportFactory(); - mtransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); - mtransportFactory.transport.setAccessToken(ACCESS_TOKEN); - mtransportFactory.transport.setExpireTime(getDefaultExpireTime()); + public void getAccount_sameAs() { + mockTransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); + mockTransportFactory.transport.setAccessToken(ACCESS_TOKEN); + mockTransportFactory.transport.setExpireTime(getDefaultExpireTime()); ImpersonatedCredentials targetCredentials = ImpersonatedCredentials.create( sourceCredentials, @@ -545,19 +509,16 @@ public void getAccount_sameAs() throws IOException { null, SCOPES, VALID_LIFETIME, - mtransportFactory); + mockTransportFactory); assertEquals(IMPERSONATED_CLIENT_EMAIL, targetCredentials.getAccount()); } @Test - public void sign_sameAs() throws IOException { - GoogleCredentials sourceCredentials = getSourceCredentials(); - MockIAMCredentialsServiceTransportFactory mtransportFactory = - new MockIAMCredentialsServiceTransportFactory(); - mtransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); - mtransportFactory.transport.setAccessToken(ACCESS_TOKEN); - mtransportFactory.transport.setExpireTime(getDefaultExpireTime()); + public void sign_sameAs() { + mockTransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); + mockTransportFactory.transport.setAccessToken(ACCESS_TOKEN); + mockTransportFactory.transport.setExpireTime(getDefaultExpireTime()); ImpersonatedCredentials targetCredentials = ImpersonatedCredentials.create( sourceCredentials, @@ -565,24 +526,21 @@ public void sign_sameAs() throws IOException { null, SCOPES, VALID_LIFETIME, - mtransportFactory); + mockTransportFactory); byte[] expectedSignature = {0xD, 0xE, 0xA, 0xD}; - mtransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); - mtransportFactory.transport.setSignedBlob(expectedSignature); + mockTransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); + mockTransportFactory.transport.setSignedBlob(expectedSignature); assertArrayEquals(expectedSignature, targetCredentials.sign(expectedSignature)); } @Test public void sign_requestIncludesDelegates() throws IOException { - GoogleCredentials sourceCredentials = getSourceCredentials(); - MockIAMCredentialsServiceTransportFactory mtransportFactory = - new MockIAMCredentialsServiceTransportFactory(); - mtransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); - mtransportFactory.transport.setAccessToken(ACCESS_TOKEN); - mtransportFactory.transport.setExpireTime(getDefaultExpireTime()); + mockTransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); + mockTransportFactory.transport.setAccessToken(ACCESS_TOKEN); + mockTransportFactory.transport.setExpireTime(getDefaultExpireTime()); ImpersonatedCredentials targetCredentials = ImpersonatedCredentials.create( sourceCredentials, @@ -590,16 +548,16 @@ public void sign_requestIncludesDelegates() throws IOException { ImmutableList.of("delegate@example.com"), SCOPES, VALID_LIFETIME, - mtransportFactory); + mockTransportFactory); byte[] expectedSignature = {0xD, 0xE, 0xA, 0xD}; - mtransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); - mtransportFactory.transport.setSignedBlob(expectedSignature); + mockTransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); + mockTransportFactory.transport.setSignedBlob(expectedSignature); assertArrayEquals(expectedSignature, targetCredentials.sign(expectedSignature)); - MockLowLevelHttpRequest request = mtransportFactory.transport.getRequest(); + MockLowLevelHttpRequest request = mockTransportFactory.transport.getRequest(); GenericJson body = JSON_FACTORY .createJsonParser(request.getContentAsString()) @@ -610,22 +568,18 @@ public void sign_requestIncludesDelegates() throws IOException { } @Test - public void sign_usesSourceCredentials() throws IOException { - Date expiry = new Date(); + public void sign_usesSourceCredentials() { Calendar c = Calendar.getInstance(); - c.setTime(expiry); c.add(Calendar.DATE, 1); - expiry = c.getTime(); + Date expiry = c.getTime(); GoogleCredentials sourceCredentials = new GoogleCredentials.Builder() .setAccessToken(new AccessToken("source-token", expiry)) .build(); - MockIAMCredentialsServiceTransportFactory mtransportFactory = - new MockIAMCredentialsServiceTransportFactory(); - mtransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); - mtransportFactory.transport.setAccessToken(ACCESS_TOKEN); - mtransportFactory.transport.setExpireTime(getDefaultExpireTime()); + mockTransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); + mockTransportFactory.transport.setAccessToken(ACCESS_TOKEN); + mockTransportFactory.transport.setExpireTime(getDefaultExpireTime()); ImpersonatedCredentials targetCredentials = ImpersonatedCredentials.create( sourceCredentials, @@ -633,27 +587,24 @@ public void sign_usesSourceCredentials() throws IOException { ImmutableList.of("delegate@example.com"), SCOPES, VALID_LIFETIME, - mtransportFactory); + mockTransportFactory); byte[] expectedSignature = {0xD, 0xE, 0xA, 0xD}; - mtransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); - mtransportFactory.transport.setSignedBlob(expectedSignature); + mockTransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); + mockTransportFactory.transport.setSignedBlob(expectedSignature); assertArrayEquals(expectedSignature, targetCredentials.sign(expectedSignature)); - MockLowLevelHttpRequest request = mtransportFactory.transport.getRequest(); + MockLowLevelHttpRequest request = mockTransportFactory.transport.getRequest(); assertEquals("Bearer source-token", request.getFirstHeaderValue("Authorization")); } @Test - public void sign_accessDenied_throws() throws IOException { - GoogleCredentials sourceCredentials = getSourceCredentials(); - MockIAMCredentialsServiceTransportFactory mtransportFactory = - new MockIAMCredentialsServiceTransportFactory(); - mtransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); - mtransportFactory.transport.setAccessToken(ACCESS_TOKEN); - mtransportFactory.transport.setExpireTime(getDefaultExpireTime()); + public void sign_accessDenied_throws() { + mockTransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); + mockTransportFactory.transport.setAccessToken(ACCESS_TOKEN); + mockTransportFactory.transport.setExpireTime(getDefaultExpireTime()); ImpersonatedCredentials targetCredentials = ImpersonatedCredentials.create( sourceCredentials, @@ -661,13 +612,13 @@ public void sign_accessDenied_throws() throws IOException { null, SCOPES, VALID_LIFETIME, - mtransportFactory); + mockTransportFactory); byte[] expectedSignature = {0xD, 0xE, 0xA, 0xD}; - mtransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); - mtransportFactory.transport.setSignedBlob(expectedSignature); - mtransportFactory.transport.setErrorResponseCodeAndMessage( + mockTransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); + mockTransportFactory.transport.setSignedBlob(expectedSignature); + mockTransportFactory.transport.setErrorResponseCodeAndMessage( HttpStatusCodes.STATUS_CODE_FORBIDDEN, "Sign Error"); try { @@ -682,13 +633,10 @@ public void sign_accessDenied_throws() throws IOException { } @Test - public void sign_serverError_throws() throws IOException { - GoogleCredentials sourceCredentials = getSourceCredentials(); - MockIAMCredentialsServiceTransportFactory mtransportFactory = - new MockIAMCredentialsServiceTransportFactory(); - mtransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); - mtransportFactory.transport.setAccessToken(ACCESS_TOKEN); - mtransportFactory.transport.setExpireTime(getDefaultExpireTime()); + public void sign_serverError_throws() { + mockTransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); + mockTransportFactory.transport.setAccessToken(ACCESS_TOKEN); + mockTransportFactory.transport.setExpireTime(getDefaultExpireTime()); ImpersonatedCredentials targetCredentials = ImpersonatedCredentials.create( sourceCredentials, @@ -696,13 +644,13 @@ public void sign_serverError_throws() throws IOException { null, SCOPES, VALID_LIFETIME, - mtransportFactory); + mockTransportFactory); byte[] expectedSignature = {0xD, 0xE, 0xA, 0xD}; - mtransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); - mtransportFactory.transport.setSignedBlob(expectedSignature); - mtransportFactory.transport.setErrorResponseCodeAndMessage( + mockTransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); + mockTransportFactory.transport.setSignedBlob(expectedSignature); + mockTransportFactory.transport.setErrorResponseCodeAndMessage( HttpStatusCodes.STATUS_CODE_SERVER_ERROR, "Sign Error"); try { @@ -718,12 +666,9 @@ public void sign_serverError_throws() throws IOException { @Test public void idTokenWithAudience_sameAs() throws IOException { - GoogleCredentials sourceCredentials = getSourceCredentials(); - MockIAMCredentialsServiceTransportFactory mtransportFactory = - new MockIAMCredentialsServiceTransportFactory(); - mtransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); - mtransportFactory.transport.setAccessToken(ACCESS_TOKEN); - mtransportFactory.transport.setExpireTime(getDefaultExpireTime()); + mockTransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); + mockTransportFactory.transport.setAccessToken(ACCESS_TOKEN); + mockTransportFactory.transport.setExpireTime(getDefaultExpireTime()); ImpersonatedCredentials targetCredentials = ImpersonatedCredentials.create( @@ -732,9 +677,9 @@ public void idTokenWithAudience_sameAs() throws IOException { null, SCOPES, VALID_LIFETIME, - mtransportFactory); + mockTransportFactory); - mtransportFactory.transport.setIdToken(STANDARD_ID_TOKEN); + mockTransportFactory.transport.setIdToken(STANDARD_ID_TOKEN); String targetAudience = "https://foo.bar"; IdTokenCredentials tokenCredential = @@ -752,12 +697,9 @@ public void idTokenWithAudience_sameAs() throws IOException { @Test public void idTokenWithAudience_withEmail() throws IOException { - GoogleCredentials sourceCredentials = getSourceCredentials(); - MockIAMCredentialsServiceTransportFactory mtransportFactory = - new MockIAMCredentialsServiceTransportFactory(); - mtransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); - mtransportFactory.transport.setAccessToken(ACCESS_TOKEN); - mtransportFactory.transport.setExpireTime(getDefaultExpireTime()); + mockTransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); + mockTransportFactory.transport.setAccessToken(ACCESS_TOKEN); + mockTransportFactory.transport.setExpireTime(getDefaultExpireTime()); ImpersonatedCredentials targetCredentials = ImpersonatedCredentials.create( @@ -766,9 +708,9 @@ public void idTokenWithAudience_withEmail() throws IOException { null, SCOPES, VALID_LIFETIME, - mtransportFactory); + mockTransportFactory); - mtransportFactory.transport.setIdToken(TOKEN_WITH_EMAIL); + mockTransportFactory.transport.setIdToken(TOKEN_WITH_EMAIL); String targetAudience = "https://foo.bar"; IdTokenCredentials tokenCredential = @@ -784,13 +726,10 @@ public void idTokenWithAudience_withEmail() throws IOException { } @Test - public void idToken_withServerError() throws IOException { - GoogleCredentials sourceCredentials = getSourceCredentials(); - MockIAMCredentialsServiceTransportFactory mtransportFactory = - new MockIAMCredentialsServiceTransportFactory(); - mtransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); - mtransportFactory.transport.setAccessToken(ACCESS_TOKEN); - mtransportFactory.transport.setExpireTime(getDefaultExpireTime()); + public void idToken_withServerError() { + mockTransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); + mockTransportFactory.transport.setAccessToken(ACCESS_TOKEN); + mockTransportFactory.transport.setExpireTime(getDefaultExpireTime()); ImpersonatedCredentials targetCredentials = ImpersonatedCredentials.create( @@ -799,10 +738,10 @@ public void idToken_withServerError() throws IOException { null, SCOPES, VALID_LIFETIME, - mtransportFactory); + mockTransportFactory); - mtransportFactory.transport.setIdToken(STANDARD_ID_TOKEN); - mtransportFactory.transport.setErrorResponseCodeAndMessage( + mockTransportFactory.transport.setIdToken(STANDARD_ID_TOKEN); + mockTransportFactory.transport.setErrorResponseCodeAndMessage( HttpStatusCodes.STATUS_CODE_SERVER_ERROR, "Internal Server Error"); String targetAudience = "https://foo.bar"; @@ -819,13 +758,10 @@ public void idToken_withServerError() throws IOException { } @Test - public void idToken_withOtherError() throws IOException { - GoogleCredentials sourceCredentials = getSourceCredentials(); - MockIAMCredentialsServiceTransportFactory mtransportFactory = - new MockIAMCredentialsServiceTransportFactory(); - mtransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); - mtransportFactory.transport.setAccessToken(ACCESS_TOKEN); - mtransportFactory.transport.setExpireTime(getDefaultExpireTime()); + public void idToken_withOtherError() { + mockTransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); + mockTransportFactory.transport.setAccessToken(ACCESS_TOKEN); + mockTransportFactory.transport.setExpireTime(getDefaultExpireTime()); ImpersonatedCredentials targetCredentials = ImpersonatedCredentials.create( @@ -834,10 +770,10 @@ public void idToken_withOtherError() throws IOException { null, SCOPES, VALID_LIFETIME, - mtransportFactory); + mockTransportFactory); - mtransportFactory.transport.setIdToken(STANDARD_ID_TOKEN); - mtransportFactory.transport.setErrorResponseCodeAndMessage( + mockTransportFactory.transport.setIdToken(STANDARD_ID_TOKEN); + mockTransportFactory.transport.setErrorResponseCodeAndMessage( HttpStatusCodes.STATUS_CODE_MOVED_PERMANENTLY, "Redirect"); String targetAudience = "https://foo.bar"; @@ -855,12 +791,9 @@ public void idToken_withOtherError() throws IOException { @Test public void hashCode_equals() throws IOException { - GoogleCredentials sourceCredentials = getSourceCredentials(); - MockIAMCredentialsServiceTransportFactory mtransportFactory = - new MockIAMCredentialsServiceTransportFactory(); - mtransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); - mtransportFactory.transport.setAccessToken(ACCESS_TOKEN); - mtransportFactory.transport.setExpireTime(getDefaultExpireTime()); + mockTransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); + mockTransportFactory.transport.setAccessToken(ACCESS_TOKEN); + mockTransportFactory.transport.setExpireTime(getDefaultExpireTime()); ImpersonatedCredentials credentials = ImpersonatedCredentials.create( @@ -869,7 +802,7 @@ public void hashCode_equals() throws IOException { null, SCOPES, VALID_LIFETIME, - mtransportFactory); + mockTransportFactory); ImpersonatedCredentials otherCredentials = ImpersonatedCredentials.create( @@ -878,7 +811,7 @@ public void hashCode_equals() throws IOException { null, SCOPES, VALID_LIFETIME, - mtransportFactory); + mockTransportFactory); assertEquals(credentials.hashCode(), otherCredentials.hashCode()); } @@ -886,12 +819,9 @@ public void hashCode_equals() throws IOException { @Test public void serialize() throws IOException, ClassNotFoundException { - GoogleCredentials sourceCredentials = getSourceCredentials(); - MockIAMCredentialsServiceTransportFactory mtransportFactory = - new MockIAMCredentialsServiceTransportFactory(); - mtransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); - mtransportFactory.transport.setAccessToken(ACCESS_TOKEN); - mtransportFactory.transport.setExpireTime(getDefaultExpireTime()); + mockTransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); + mockTransportFactory.transport.setAccessToken(ACCESS_TOKEN); + mockTransportFactory.transport.setExpireTime(getDefaultExpireTime()); ImpersonatedCredentials targetCredentials = ImpersonatedCredentials.create( @@ -900,7 +830,7 @@ public void serialize() throws IOException, ClassNotFoundException { null, SCOPES, VALID_LIFETIME, - mtransportFactory); + mockTransportFactory); GoogleCredentials deserializedCredentials = serializeAndDeserialize(targetCredentials); assertEquals(targetCredentials, deserializedCredentials); assertEquals(targetCredentials.hashCode(), deserializedCredentials.hashCode()); @@ -909,9 +839,7 @@ public void serialize() throws IOException, ClassNotFoundException { } public static String getDefaultExpireTime() { - Date currentDate = new Date(); Calendar c = Calendar.getInstance(); - c.setTime(currentDate); c.add(Calendar.SECOND, VALID_LIFETIME); return new SimpleDateFormat(RFC3339).format(c.getTime()); } From d34dae2b2b70f85f480b0498d340d7485f37e302 Mon Sep 17 00:00:00 2001 From: Chaoren Liu Date: Tue, 11 May 2021 00:32:56 -0400 Subject: [PATCH 20/22] Reformat the ImpersonatedCredentialsTest --- .../oauth2/ImpersonatedCredentialsTest.java | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/oauth2_http/javatests/com/google/auth/oauth2/ImpersonatedCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/ImpersonatedCredentialsTest.java index b4dcaf03f..8d834abde 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/ImpersonatedCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/ImpersonatedCredentialsTest.java @@ -178,7 +178,8 @@ public void fromJson_userAsSource_WithQuotaProjectId() throws IOException { USER_ACCOUNT_CLIENT_ID, USER_ACCOUNT_CLIENT_SECRET, REFRESH_TOKEN); - ImpersonatedCredentials credentials = ImpersonatedCredentials.fromJson(json, mockTransportFactory); + ImpersonatedCredentials credentials = + ImpersonatedCredentials.fromJson(json, mockTransportFactory); assertEquals(IMPERSONATED_CLIENT_EMAIL, credentials.getAccount()); assertEquals(QUOTA_PROJECT_ID, credentials.getQuotaProjectId()); assertEquals(DELEGATES, credentials.getDelegates()); @@ -198,7 +199,8 @@ public void fromJson_userAsSource_WithoutQuotaProjectId() throws IOException { USER_ACCOUNT_CLIENT_ID, USER_ACCOUNT_CLIENT_SECRET, REFRESH_TOKEN); - ImpersonatedCredentials credentials = ImpersonatedCredentials.fromJson(json, mockTransportFactory); + ImpersonatedCredentials credentials = + ImpersonatedCredentials.fromJson(json, mockTransportFactory); assertEquals(IMPERSONATED_CLIENT_EMAIL, credentials.getAccount()); assertNull(credentials.getQuotaProjectId()); assertEquals(DELEGATES, credentials.getDelegates()); @@ -219,7 +221,8 @@ public void fromJson_userAsSource_MissingDelegatesField() throws IOException { USER_ACCOUNT_CLIENT_SECRET, REFRESH_TOKEN); json.remove("delegates"); - ImpersonatedCredentials credentials = ImpersonatedCredentials.fromJson(json, mockTransportFactory); + ImpersonatedCredentials credentials = + ImpersonatedCredentials.fromJson(json, mockTransportFactory); assertEquals(IMPERSONATED_CLIENT_EMAIL, credentials.getAccount()); assertNull(credentials.getQuotaProjectId()); assertEquals(new ArrayList(), credentials.getDelegates()); @@ -233,7 +236,8 @@ public void fromJson_userAsSource_MissingDelegatesField() throws IOException { public void fromJson_ServiceAccountAsSource() throws IOException { GenericJson json = buildImpersonationCredentialsJson(IMPERSONATION_URL, DELEGATES, QUOTA_PROJECT_ID); - ImpersonatedCredentials credentials = ImpersonatedCredentials.fromJson(json, mockTransportFactory); + ImpersonatedCredentials credentials = + ImpersonatedCredentials.fromJson(json, mockTransportFactory); assertEquals(IMPERSONATED_CLIENT_EMAIL, credentials.getAccount()); assertEquals(QUOTA_PROJECT_ID, credentials.getQuotaProjectId()); assertEquals(DELEGATES, credentials.getDelegates()); @@ -308,7 +312,8 @@ public void refreshAccessToken_unauthorized() throws IOException { String expectedMessage = "The caller does not have permission"; mockTransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); - mockTransportFactory.transport.setTokenResponseErrorCode(HttpStatusCodes.STATUS_CODE_UNAUTHORIZED); + mockTransportFactory.transport.setTokenResponseErrorCode( + HttpStatusCodes.STATUS_CODE_UNAUTHORIZED); mockTransportFactory.transport.setTokenResponseErrorContent( generateErrorJson( HttpStatusCodes.STATUS_CODE_UNAUTHORIZED, expectedMessage, "global", "forbidden")); @@ -336,13 +341,19 @@ public void refreshAccessToken_malformedTarget() throws IOException { String invalidTargetEmail = "foo"; String expectedMessage = "Request contains an invalid argument"; mockTransportFactory.transport.setTargetPrincipal(invalidTargetEmail); - mockTransportFactory.transport.setTokenResponseErrorCode(HttpStatusCodes.STATUS_CODE_BAD_REQUEST); + mockTransportFactory.transport.setTokenResponseErrorCode( + HttpStatusCodes.STATUS_CODE_BAD_REQUEST); mockTransportFactory.transport.setTokenResponseErrorContent( generateErrorJson( HttpStatusCodes.STATUS_CODE_BAD_REQUEST, expectedMessage, "global", "badRequest")); ImpersonatedCredentials targetCredentials = ImpersonatedCredentials.create( - sourceCredentials, invalidTargetEmail, null, SCOPES, VALID_LIFETIME, mockTransportFactory); + sourceCredentials, + invalidTargetEmail, + null, + SCOPES, + VALID_LIFETIME, + mockTransportFactory); try { targetCredentials.refreshAccessToken().getTokenValue(); From 4ea84e9d99c6822e6d2bdb8a26f869c6842ad342 Mon Sep 17 00:00:00 2001 From: Chaoren Liu Date: Wed, 12 May 2021 15:30:35 -0400 Subject: [PATCH 21/22] Remove redundant checks in tests --- .../javatests/com/google/auth/oauth2/GoogleCredentialsTest.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/oauth2_http/javatests/com/google/auth/oauth2/GoogleCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/GoogleCredentialsTest.java index 1d7e1422d..36774de58 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/GoogleCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/GoogleCredentialsTest.java @@ -297,7 +297,6 @@ public void fromStream_Impersonation_providesToken_WithQuotaProject() throws IOE GoogleCredentials.fromStream(impersonationCredentialsStream, transportFactoryForSource); credentials.setTransportFactory(transportFactory); - assertNotNull(credentials); Map> metadata = credentials.getRequestMetadata(CALL_URI); TestUtils.assertContainsBearerToken(metadata, ImpersonatedCredentialsTest.ACCESS_TOKEN); @@ -332,7 +331,6 @@ public void fromStream_Impersonation_providesToken_WithoutQuotaProject() throws GoogleCredentials.fromStream(impersonationCredentialsStream, transportFactoryForSource); credentials.setTransportFactory(transportFactory); - assertNotNull(credentials); Map> metadata = credentials.getRequestMetadata(CALL_URI); TestUtils.assertContainsBearerToken(metadata, ImpersonatedCredentialsTest.ACCESS_TOKEN); From 55a0c6195a82d62ca97a7b9de3e4fa67d3a2db53 Mon Sep 17 00:00:00 2001 From: Chaoren Liu Date: Wed, 12 May 2021 16:49:24 -0400 Subject: [PATCH 22/22] Use VisibleForTesting annotation to limit visibility --- .../com/google/auth/oauth2/ImpersonatedCredentials.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java b/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java index 454ae84b8..700ad2117 100644 --- a/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java @@ -46,6 +46,7 @@ import com.google.auth.ServiceAccountSigner; import com.google.auth.http.HttpCredentialsAdapter; import com.google.auth.http.HttpTransportFactory; +import com.google.common.annotations.VisibleForTesting; import com.google.common.base.MoreObjects; import com.google.common.collect.ImmutableMap; import java.io.IOException; @@ -256,11 +257,13 @@ public String getQuotaProjectId() { return this.quotaProjectId; } - public List getDelegates() { + @VisibleForTesting + List getDelegates() { return delegates; } - public List getScopes() { + @VisibleForTesting + List getScopes() { return scopes; }