From c9c1f7b1283269c46125ca896639eadaf5916d09 Mon Sep 17 00:00:00 2001 From: Cody Oss Date: Mon, 9 Dec 2019 10:11:16 -0700 Subject: [PATCH] feat: support reading in quotaProjectId for billing --- .../google/auth/oauth2/GoogleCredentials.java | 19 +++- .../oauth2/ServiceAccountCredentials.java | 102 +++++++++++++++--- .../ServiceAccountJwtAccessCredentials.java | 62 +++++++++-- .../google/auth/oauth2/UserCredentials.java | 41 +++++-- .../DefaultCredentialsProviderTest.java | 18 ++-- .../auth/oauth2/GoogleCredentialsTest.java | 9 +- .../oauth2/ServiceAccountCredentialsTest.java | 42 ++++++-- ...erviceAccountJwtAccessCredentialsTest.java | 23 +++- .../auth/oauth2/UserCredentialsTest.java | 73 +++++++++++-- 9 files changed, 325 insertions(+), 64 deletions(-) diff --git a/oauth2_http/java/com/google/auth/oauth2/GoogleCredentials.java b/oauth2_http/java/com/google/auth/oauth2/GoogleCredentials.java index e7f275b1b..43c24f461 100644 --- a/oauth2_http/java/com/google/auth/oauth2/GoogleCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/GoogleCredentials.java @@ -39,12 +39,14 @@ import com.google.common.collect.ImmutableList; import java.io.IOException; import java.io.InputStream; -import java.util.Collection; +import java.util.*; /** Base type for credentials for authorizing calls to Google APIs using OAuth2. */ public class GoogleCredentials extends OAuth2Credentials { private static final long serialVersionUID = -1522852442442473691L; + 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"; @@ -166,6 +168,21 @@ public static GoogleCredentials fromStream( fileType, USER_FILE_TYPE, SERVICE_ACCOUNT_FILE_TYPE)); } + /** + * Adds quota project ID to requestMetadata if present. + * + * @return a new map with quotaProjectId added if needed, or else returns the original requestMetadata + */ + static Map> addQuotaProjectIdToRequestMetadata(String quotaProjectId, Map> requestMetadata) { + Preconditions.checkNotNull(requestMetadata, "requestMetadata"); + if (quotaProjectId != null && !requestMetadata.containsKey(QUOTA_PROJECT_ID_HEADER_KEY)) { + Map> newRequestMetadata = new HashMap<>(requestMetadata); + newRequestMetadata.put(QUOTA_PROJECT_ID_HEADER_KEY, Collections.singletonList(quotaProjectId)); + return Collections.unmodifiableMap(newRequestMetadata); + } + return requestMetadata; + } + /** Default constructor. */ protected GoogleCredentials() { this(null); diff --git a/oauth2_http/java/com/google/auth/oauth2/ServiceAccountCredentials.java b/oauth2_http/java/com/google/auth/oauth2/ServiceAccountCredentials.java index 1aac83a60..edc227634 100644 --- a/oauth2_http/java/com/google/auth/oauth2/ServiceAccountCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/ServiceAccountCredentials.java @@ -75,11 +75,7 @@ import java.security.SignatureException; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; -import java.util.Collection; -import java.util.Date; -import java.util.List; -import java.util.Map; -import java.util.Objects; +import java.util.*; /** * OAuth2 credentials representing a Service Account for calling Google APIs. @@ -102,6 +98,7 @@ public class ServiceAccountCredentials extends GoogleCredentials private final String transportFactoryClassName; private final URI tokenServerUri; private final Collection scopes; + private final String quotaProjectId; private transient HttpTransportFactory transportFactory; @@ -119,6 +116,8 @@ public class ServiceAccountCredentials extends GoogleCredentials * @param tokenServerUri URI of the end point that provides tokens. * @param serviceAccountUser Email of the user account to impersonate, if delegating domain-wide * authority to the service account. + * @param projectId The project used for billing. + * @param quotaProjectId The project used for quota and billing purposes. May be null. */ ServiceAccountCredentials( String clientId, @@ -129,7 +128,8 @@ public class ServiceAccountCredentials extends GoogleCredentials HttpTransportFactory transportFactory, URI tokenServerUri, String serviceAccountUser, - String projectId) { + String projectId, + String quotaProjectId) { this.clientId = clientId; this.clientEmail = Preconditions.checkNotNull(clientEmail); this.privateKey = Preconditions.checkNotNull(privateKey); @@ -143,6 +143,7 @@ public class ServiceAccountCredentials extends GoogleCredentials this.tokenServerUri = (tokenServerUri == null) ? OAuth2Utils.TOKEN_SERVER_URI : tokenServerUri; this.serviceAccountUser = serviceAccountUser; this.projectId = projectId; + this.quotaProjectId = quotaProjectId; } /** @@ -163,6 +164,7 @@ static ServiceAccountCredentials fromJson( String privateKeyId = (String) json.get("private_key_id"); String projectId = (String) json.get("project_id"); String tokenServerUriStringFromCreds = (String) json.get("token_uri"); + String quotaProject = (String) json.get("quota_project_id"); URI tokenServerUriFromCreds = null; try { if (tokenServerUriStringFromCreds != null) { @@ -189,7 +191,8 @@ static ServiceAccountCredentials fromJson( transportFactory, tokenServerUriFromCreds, null, - projectId); + projectId, + quotaProject); } /** @@ -212,7 +215,7 @@ public static ServiceAccountCredentials fromPkcs8( Collection scopes) throws IOException { return fromPkcs8( - clientId, clientEmail, privateKeyPkcs8, privateKeyId, scopes, null, null, null); + clientId, clientEmail, privateKeyPkcs8, privateKeyId, scopes, null, null, null, null); } /** @@ -248,6 +251,7 @@ public static ServiceAccountCredentials fromPkcs8( scopes, transportFactory, tokenServerUri, + null, null); } @@ -291,6 +295,49 @@ public static ServiceAccountCredentials fromPkcs8( null); } + /** + * Factory with minimum identifying information and custom transport using PKCS#8 for the private + * key. + * + * @param clientId Client ID of the service account from the console. May be null. + * @param clientEmail Client email address of the service account from the console. + * @param privateKeyPkcs8 RSA private key object for the service account in PKCS#8 format. + * @param privateKeyId Private key identifier for the service account. May be null. + * @param scopes Scope strings for the APIs to be called. May be null or an empty collection, + * which results in a credential that must have createScoped called before use. + * @param transportFactory HTTP transport factory, creates the transport used to get access + * tokens. + * @param tokenServerUri URI of the end point that provides tokens. + * @param serviceAccountUser The email of the user account to impersonate, if delegating + * domain-wide authority to the service account. + * @param quotaProjectId The project used for quota and billing purposes. May be null. + * @return New ServiceAccountCredentials created from a private key. + * @throws IOException if the credential cannot be created from the private key. + */ + public static ServiceAccountCredentials fromPkcs8( + String clientId, + String clientEmail, + String privateKeyPkcs8, + String privateKeyId, + Collection scopes, + HttpTransportFactory transportFactory, + URI tokenServerUri, + String serviceAccountUser, + String quotaProjectId) + throws IOException { + return fromPkcs8( + clientId, + clientEmail, + privateKeyPkcs8, + privateKeyId, + scopes, + transportFactory, + tokenServerUri, + serviceAccountUser, + null, + quotaProjectId); + } + static ServiceAccountCredentials fromPkcs8( String clientId, String clientEmail, @@ -300,7 +347,8 @@ static ServiceAccountCredentials fromPkcs8( HttpTransportFactory transportFactory, URI tokenServerUri, String serviceAccountUser, - String projectId) + String projectId, + String quotaProject) throws IOException { PrivateKey privateKey = privateKeyFromPkcs8(privateKeyPkcs8); return new ServiceAccountCredentials( @@ -312,7 +360,8 @@ static ServiceAccountCredentials fromPkcs8( transportFactory, tokenServerUri, serviceAccountUser, - projectId); + projectId, + quotaProject); } /** Helper to convert from a PKCS#8 String to an RSA private key */ @@ -499,7 +548,8 @@ public GoogleCredentials createScoped(Collection newScopes) { transportFactory, tokenServerUri, serviceAccountUser, - projectId); + projectId, + quotaProjectId); } @Override @@ -513,7 +563,8 @@ public GoogleCredentials createDelegated(String user) { transportFactory, tokenServerUri, user, - projectId); + projectId, + quotaProjectId); } public final String getClientId() { @@ -584,6 +635,12 @@ public JwtCredentials jwtWithClaims(JwtClaims newClaims) { .build(); } + @Override + public Map> getRequestMetadata(URI uri) throws IOException { + Map> requestMetadata = super.getRequestMetadata(uri); + return addQuotaProjectIdToRequestMetadata(quotaProjectId, requestMetadata); + } + @Override public int hashCode() { return Objects.hash( @@ -593,7 +650,8 @@ public int hashCode() { privateKeyId, transportFactoryClassName, tokenServerUri, - scopes); + scopes, + quotaProjectId); } @Override @@ -606,6 +664,7 @@ public String toString() { .add("tokenServerUri", tokenServerUri) .add("scopes", scopes) .add("serviceAccountUser", serviceAccountUser) + .add("quotaProjectId", quotaProjectId) .toString(); } @@ -621,7 +680,8 @@ public boolean equals(Object obj) { && Objects.equals(this.privateKeyId, other.privateKeyId) && Objects.equals(this.transportFactoryClassName, other.transportFactoryClassName) && Objects.equals(this.tokenServerUri, other.tokenServerUri) - && Objects.equals(this.scopes, other.scopes); + && Objects.equals(this.scopes, other.scopes) + && Objects.equals(this.quotaProjectId, other.quotaProjectId); } String createAssertion(JsonFactory jsonFactory, long currentTime, String audience) @@ -713,6 +773,7 @@ public static class Builder extends GoogleCredentials.Builder { private URI tokenServerUri; private Collection scopes; private HttpTransportFactory transportFactory; + private String quotaProjectId; protected Builder() {} @@ -726,6 +787,7 @@ protected Builder(ServiceAccountCredentials credentials) { this.tokenServerUri = credentials.tokenServerUri; this.serviceAccountUser = credentials.serviceAccountUser; this.projectId = credentials.projectId; + this.quotaProjectId = credentials.quotaProjectId; } public Builder setClientId(String clientId) { @@ -773,6 +835,11 @@ public Builder setHttpTransportFactory(HttpTransportFactory transportFactory) { return this; } + public Builder setQuotaProjectId(String quotaProjectId) { + this.quotaProjectId = quotaProjectId; + return this; + } + public String getClientId() { return clientId; } @@ -809,6 +876,10 @@ public HttpTransportFactory getHttpTransportFactory() { return transportFactory; } + public String getQuotaProjectId() { + return quotaProjectId; + } + public ServiceAccountCredentials build() { return new ServiceAccountCredentials( clientId, @@ -819,7 +890,8 @@ public ServiceAccountCredentials build() { transportFactory, tokenServerUri, serviceAccountUser, - projectId); + projectId, + quotaProjectId); } } } diff --git a/oauth2_http/java/com/google/auth/oauth2/ServiceAccountJwtAccessCredentials.java b/oauth2_http/java/com/google/auth/oauth2/ServiceAccountJwtAccessCredentials.java index 12aa73775..b33a7bd3f 100644 --- a/oauth2_http/java/com/google/auth/oauth2/ServiceAccountJwtAccessCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/ServiceAccountJwtAccessCredentials.java @@ -32,6 +32,7 @@ package com.google.auth.oauth2; import static com.google.auth.oauth2.GoogleCredentials.SERVICE_ACCOUNT_FILE_TYPE; +import static com.google.auth.oauth2.GoogleCredentials.addQuotaProjectIdToRequestMetadata; import com.google.api.client.json.GenericJson; import com.google.api.client.json.JsonFactory; @@ -84,6 +85,7 @@ public class ServiceAccountJwtAccessCredentials extends Credentials private final PrivateKey privateKey; private final String privateKeyId; private final URI defaultAudience; + private final String quotaProjectId; private transient LoadingCache credentialsCache; @@ -103,7 +105,7 @@ public class ServiceAccountJwtAccessCredentials extends Credentials @Deprecated public ServiceAccountJwtAccessCredentials( String clientId, String clientEmail, PrivateKey privateKey, String privateKeyId) { - this(clientId, clientEmail, privateKey, privateKeyId, null); + this(clientId, clientEmail, privateKey, privateKeyId, null, null); } /** @@ -120,13 +122,15 @@ private ServiceAccountJwtAccessCredentials( String clientEmail, PrivateKey privateKey, String privateKeyId, - URI defaultAudience) { + URI defaultAudience, + String quotaProjectId) { this.clientId = clientId; this.clientEmail = Preconditions.checkNotNull(clientEmail); this.privateKey = Preconditions.checkNotNull(privateKey); this.privateKeyId = privateKeyId; this.defaultAudience = defaultAudience; this.credentialsCache = createCache(); + this.quotaProjectId = quotaProjectId; } /** @@ -156,6 +160,7 @@ static ServiceAccountJwtAccessCredentials fromJson(Map json, URI String clientEmail = (String) json.get("client_email"); String privateKeyPkcs8 = (String) json.get("private_key"); String privateKeyId = (String) json.get("private_key_id"); + String quoataProjectId = (String) json.get("quota_project_id"); if (clientId == null || clientEmail == null || privateKeyPkcs8 == null @@ -164,7 +169,7 @@ static ServiceAccountJwtAccessCredentials fromJson(Map json, URI "Error reading service account credential from JSON, " + "expecting 'client_id', 'client_email', 'private_key' and 'private_key_id'."); } - return fromPkcs8(clientId, clientEmail, privateKeyPkcs8, privateKeyId, defaultAudience); + return fromPkcs8(clientId, clientEmail, privateKeyPkcs8, privateKeyId, defaultAudience, quoataProjectId); } /** @@ -180,7 +185,7 @@ static ServiceAccountJwtAccessCredentials fromJson(Map json, URI public static ServiceAccountJwtAccessCredentials fromPkcs8( String clientId, String clientEmail, String privateKeyPkcs8, String privateKeyId) throws IOException { - return fromPkcs8(clientId, clientEmail, privateKeyPkcs8, privateKeyId, null); + return fromPkcs8(clientId, clientEmail, privateKeyPkcs8, privateKeyId, null, null); } /** @@ -203,7 +208,32 @@ public static ServiceAccountJwtAccessCredentials fromPkcs8( throws IOException { PrivateKey privateKey = ServiceAccountCredentials.privateKeyFromPkcs8(privateKeyPkcs8); return new ServiceAccountJwtAccessCredentials( - clientId, clientEmail, privateKey, privateKeyId, defaultAudience); + clientId, clientEmail, privateKey, privateKeyId, defaultAudience, null); + } + + /** + * Factory using PKCS#8 for the private key. + * + * @param clientId Client ID of the service account from the console. May be null. + * @param clientEmail Client email address of the service account from the console. + * @param privateKeyPkcs8 RSA private key object for the service account in PKCS#8 format. + * @param privateKeyId Private key identifier for the service account. May be null. + * @param defaultAudience Audience to use if not provided by transport. May be null. + * @param quotaProjectId The project used for quota and billing purposes. May be null. + * @return New ServiceAccountJwtAcceessCredentials created from a private key. + * @throws IOException if the credential cannot be created from the private key. + */ + public static ServiceAccountJwtAccessCredentials fromPkcs8( + String clientId, + String clientEmail, + String privateKeyPkcs8, + String privateKeyId, + URI defaultAudience, + String quotaProjectId) + throws IOException { + PrivateKey privateKey = ServiceAccountCredentials.privateKeyFromPkcs8(privateKeyPkcs8); + return new ServiceAccountJwtAccessCredentials( + clientId, clientEmail, privateKey, privateKeyId, defaultAudience, quotaProjectId); } /** @@ -344,7 +374,8 @@ public Map> getRequestMetadata(URI uri) throws IOException .setSubject(clientEmail) .build(); JwtCredentials credentials = credentialsCache.get(defaultClaims); - return credentials.getRequestMetadata(uri); + Map> requestMetadata = credentials.getRequestMetadata(uri); + return addQuotaProjectIdToRequestMetadata(quotaProjectId, requestMetadata); } catch (ExecutionException e) { Throwables.propagateIfPossible(e.getCause(), IOException.class); // Should never happen @@ -400,7 +431,7 @@ public byte[] sign(byte[] toSign) { @Override public int hashCode() { - return Objects.hash(clientId, clientEmail, privateKey, privateKeyId, defaultAudience); + return Objects.hash(clientId, clientEmail, privateKey, privateKeyId, defaultAudience, quotaProjectId); } @Override @@ -410,6 +441,7 @@ public String toString() { .add("clientEmail", clientEmail) .add("privateKeyId", privateKeyId) .add("defaultAudience", defaultAudience) + .add("quotaProjectId", quotaProjectId) .toString(); } @@ -423,7 +455,8 @@ public boolean equals(Object obj) { && Objects.equals(this.clientEmail, other.clientEmail) && Objects.equals(this.privateKey, other.privateKey) && Objects.equals(this.privateKeyId, other.privateKeyId) - && Objects.equals(this.defaultAudience, other.defaultAudience); + && Objects.equals(this.defaultAudience, other.defaultAudience) + && Objects.equals(this.quotaProjectId, other.quotaProjectId); } private void readObject(ObjectInputStream input) throws IOException, ClassNotFoundException { @@ -447,6 +480,7 @@ public static class Builder { private PrivateKey privateKey; private String privateKeyId; private URI defaultAudience; + private String quotaProjectId; protected Builder() {} @@ -456,6 +490,7 @@ protected Builder(ServiceAccountJwtAccessCredentials credentials) { this.privateKey = credentials.privateKey; this.privateKeyId = credentials.privateKeyId; this.defaultAudience = credentials.defaultAudience; + this.quotaProjectId = credentials.quotaProjectId; } public Builder setClientId(String clientId) { @@ -483,6 +518,11 @@ public Builder setDefaultAudience(URI defaultAudience) { return this; } + public Builder setQuotaProjectId(String quotaProjectId) { + this.quotaProjectId = quotaProjectId; + return this; + } + public String getClientId() { return clientId; } @@ -503,9 +543,13 @@ public URI getDefaultAudience() { return defaultAudience; } + public String getQuotaProjectId() { + return quotaProjectId; + } + public ServiceAccountJwtAccessCredentials build() { return new ServiceAccountJwtAccessCredentials( - clientId, clientEmail, privateKey, privateKeyId, defaultAudience); + clientId, clientEmail, privateKey, privateKeyId, defaultAudience, quotaProjectId); } } } diff --git a/oauth2_http/java/com/google/auth/oauth2/UserCredentials.java b/oauth2_http/java/com/google/auth/oauth2/UserCredentials.java index e2f3fce84..09197e541 100644 --- a/oauth2_http/java/com/google/auth/oauth2/UserCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/UserCredentials.java @@ -52,9 +52,7 @@ import java.io.InputStream; import java.io.ObjectInputStream; import java.net.URI; -import java.util.Date; -import java.util.Map; -import java.util.Objects; +import java.util.*; /** OAuth2 Credentials representing a user's identity and consent. */ public class UserCredentials extends GoogleCredentials { @@ -68,6 +66,7 @@ public class UserCredentials extends GoogleCredentials { private final String refreshToken; private final URI tokenServerUri; private final String transportFactoryClassName; + private final String quotaProjectId; private transient HttpTransportFactory transportFactory; @@ -88,7 +87,8 @@ private UserCredentials( String refreshToken, AccessToken accessToken, HttpTransportFactory transportFactory, - URI tokenServerUri) { + URI tokenServerUri, + String quotaProjectId) { super(accessToken); this.clientId = Preconditions.checkNotNull(clientId); this.clientSecret = Preconditions.checkNotNull(clientSecret); @@ -99,6 +99,7 @@ private UserCredentials( getFromServiceLoader(HttpTransportFactory.class, OAuth2Utils.HTTP_TRANSPORT_FACTORY)); this.tokenServerUri = (tokenServerUri == null) ? OAuth2Utils.TOKEN_SERVER_URI : tokenServerUri; this.transportFactoryClassName = this.transportFactory.getClass().getName(); + this.quotaProjectId = quotaProjectId; Preconditions.checkState( accessToken != null || refreshToken != null, "Either accessToken or refreshToken must not be null"); @@ -118,6 +119,7 @@ static UserCredentials fromJson(Map json, HttpTransportFactory t String clientId = (String) json.get("client_id"); String clientSecret = (String) json.get("client_secret"); String refreshToken = (String) json.get("refresh_token"); + String quotaProjectId = (String) json.get("quota_project_id"); if (clientId == null || clientSecret == null || refreshToken == null) { throw new IOException( "Error reading user credential from JSON, " @@ -130,6 +132,7 @@ static UserCredentials fromJson(Map json, HttpTransportFactory t .setAccessToken(null) .setHttpTransportFactory(transportFactory) .setTokenServerUri(null) + .setQuotaProjectId(quotaProjectId) .build(); } @@ -252,6 +255,10 @@ private InputStream getUserCredentialsStream() throws IOException { if (clientSecret != null) { json.put("client_secret", clientSecret); } + if (quotaProjectId != null) { + json.put("quota_project", clientSecret); + + } json.setFactory(JSON_FACTORY); String text = json.toPrettyString(); return new ByteArrayInputStream(text.getBytes(UTF_8)); @@ -267,6 +274,12 @@ public void save(String filePath) throws IOException { OAuth2Utils.writeInputStreamToFile(getUserCredentialsStream(), filePath); } + @Override + public Map> getRequestMetadata(URI uri) throws IOException { + Map> requestMetadata = super.getRequestMetadata(uri); + return addQuotaProjectIdToRequestMetadata(quotaProjectId, requestMetadata); + } + @Override public int hashCode() { return Objects.hash( @@ -275,7 +288,8 @@ public int hashCode() { clientSecret, refreshToken, tokenServerUri, - transportFactoryClassName); + transportFactoryClassName, + quotaProjectId); } @Override @@ -287,6 +301,7 @@ public String toString() { .add("refreshToken", refreshToken) .add("tokenServerUri", tokenServerUri) .add("transportFactoryClassName", transportFactoryClassName) + .add("quotaProjectId", quotaProjectId) .toString(); } @@ -301,7 +316,8 @@ public boolean equals(Object obj) { && Objects.equals(this.clientSecret, other.clientSecret) && Objects.equals(this.refreshToken, other.refreshToken) && Objects.equals(this.tokenServerUri, other.tokenServerUri) - && Objects.equals(this.transportFactoryClassName, other.transportFactoryClassName); + && Objects.equals(this.transportFactoryClassName, other.transportFactoryClassName) + && Objects.equals(this.quotaProjectId, other.quotaProjectId); } private void readObject(ObjectInputStream input) throws IOException, ClassNotFoundException { @@ -324,6 +340,7 @@ public static class Builder extends GoogleCredentials.Builder { private String refreshToken; private URI tokenServerUri; private HttpTransportFactory transportFactory; + private String quotaProjectId; protected Builder() {} @@ -333,6 +350,7 @@ protected Builder(UserCredentials credentials) { this.refreshToken = credentials.refreshToken; this.transportFactory = credentials.transportFactory; this.tokenServerUri = credentials.tokenServerUri; + this.quotaProjectId = credentials.quotaProjectId; } public Builder setClientId(String clientId) { @@ -365,6 +383,11 @@ public Builder setAccessToken(AccessToken token) { return this; } + public Builder setQuotaProjectId(String quotaProjectId) { + this.quotaProjectId = quotaProjectId; + return this; + } + public String getClientId() { return clientId; } @@ -385,9 +408,13 @@ public HttpTransportFactory getHttpTransportFactory() { return transportFactory; } + public String getQuotaProjectId() { + return quotaProjectId; + } + public UserCredentials build() { return new UserCredentials( - clientId, clientSecret, refreshToken, getAccessToken(), transportFactory, tokenServerUri); + clientId, clientSecret, refreshToken, getAccessToken(), transportFactory, tokenServerUri, quotaProjectId); } } } diff --git a/oauth2_http/javatests/com/google/auth/oauth2/DefaultCredentialsProviderTest.java b/oauth2_http/javatests/com/google/auth/oauth2/DefaultCredentialsProviderTest.java index 00a3d0387..6a6eec7e6 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/DefaultCredentialsProviderTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/DefaultCredentialsProviderTest.java @@ -88,6 +88,8 @@ public class DefaultCredentialsProviderTest { ServiceAccountCredentialsTest.SA_PRIVATE_KEY_PKCS8; private static final Collection SCOPES = Collections.singletonList("dummy.scope"); private static final URI CALL_URI = URI.create("http://googleapis.com/testapi/v1/foo"); + private static final String QUOTA_PROJECT = "sample-quota-project-id"; + static class MockRequestCountingTransportFactory implements HttpTransportFactory { @@ -132,7 +134,7 @@ public void getDefaultCredentials_noCredentialsSandbox_throwsNonSecurity() throw public void getDefaultCredentials_envValidSandbox_throwsNonSecurity() throws Exception { MockHttpTransportFactory transportFactory = new MockHttpTransportFactory(); InputStream userStream = - UserCredentialsTest.writeUserStream(USER_CLIENT_ID, USER_CLIENT_SECRET, REFRESH_TOKEN); + UserCredentialsTest.writeUserStream(USER_CLIENT_ID, USER_CLIENT_SECRET, REFRESH_TOKEN, QUOTA_PROJECT); TestDefaultCredentialsProvider testProvider = new TestDefaultCredentialsProvider(); testProvider.setFileSandbox(true); String userPath = tempFilePath("user.json"); @@ -314,7 +316,7 @@ public void getDefaultCredentials_envServiceAccount_providesToken() throws IOExc @Test public void getDefaultCredentials_envUser_providesToken() throws IOException { InputStream userStream = - UserCredentialsTest.writeUserStream(USER_CLIENT_ID, USER_CLIENT_SECRET, REFRESH_TOKEN); + UserCredentialsTest.writeUserStream(USER_CLIENT_ID, USER_CLIENT_SECRET, REFRESH_TOKEN, QUOTA_PROJECT); TestDefaultCredentialsProvider testProvider = new TestDefaultCredentialsProvider(); String userPath = tempFilePath("user.json"); testProvider.addFile(userPath, userStream); @@ -361,7 +363,7 @@ public void getDefaultCredentials_envGceMetadataHost_setsTokenServerUrl() { public void getDefaultCredentials_wellKnownFileEnv_providesToken() throws IOException { File cloudConfigDir = getTempDirectory(); InputStream userStream = - UserCredentialsTest.writeUserStream(USER_CLIENT_ID, USER_CLIENT_SECRET, REFRESH_TOKEN); + UserCredentialsTest.writeUserStream(USER_CLIENT_ID, USER_CLIENT_SECRET, REFRESH_TOKEN, QUOTA_PROJECT); File wellKnownFile = new File(cloudConfigDir, DefaultCredentialsProvider.WELL_KNOWN_CREDENTIALS_FILE); TestDefaultCredentialsProvider testProvider = new TestDefaultCredentialsProvider(); @@ -377,7 +379,7 @@ public void getDefaultCredentials_wellKnownFileNonWindows_providesToken() throws File configDir = new File(homeDir, ".config"); File cloudConfigDir = new File(configDir, DefaultCredentialsProvider.CLOUDSDK_CONFIG_DIRECTORY); InputStream userStream = - UserCredentialsTest.writeUserStream(USER_CLIENT_ID, USER_CLIENT_SECRET, REFRESH_TOKEN); + UserCredentialsTest.writeUserStream(USER_CLIENT_ID, USER_CLIENT_SECRET, REFRESH_TOKEN, QUOTA_PROJECT); File wellKnownFile = new File(cloudConfigDir, DefaultCredentialsProvider.WELL_KNOWN_CREDENTIALS_FILE); TestDefaultCredentialsProvider testProvider = new TestDefaultCredentialsProvider(); @@ -393,7 +395,7 @@ public void getDefaultCredentials_wellKnownFileWindows_providesToken() throws IO File homeDir = getTempDirectory(); File cloudConfigDir = new File(homeDir, DefaultCredentialsProvider.CLOUDSDK_CONFIG_DIRECTORY); InputStream userStream = - UserCredentialsTest.writeUserStream(USER_CLIENT_ID, USER_CLIENT_SECRET, REFRESH_TOKEN); + UserCredentialsTest.writeUserStream(USER_CLIENT_ID, USER_CLIENT_SECRET, REFRESH_TOKEN, QUOTA_PROJECT); File wellKnownFile = new File(cloudConfigDir, DefaultCredentialsProvider.WELL_KNOWN_CREDENTIALS_FILE); TestDefaultCredentialsProvider testProvider = new TestDefaultCredentialsProvider(); @@ -413,7 +415,7 @@ public void getDefaultCredentials_envAndWellKnownFile_envPrecedence() throws IOE TestDefaultCredentialsProvider testProvider = new TestDefaultCredentialsProvider(); InputStream envStream = - UserCredentialsTest.writeUserStream(USER_CLIENT_ID, USER_CLIENT_SECRET, refreshTokenEnv); + UserCredentialsTest.writeUserStream(USER_CLIENT_ID, USER_CLIENT_SECRET, refreshTokenEnv, QUOTA_PROJECT); String envPath = tempFilePath("env.json"); testProvider.setEnv(DefaultCredentialsProvider.CREDENTIAL_ENV_VAR, envPath); testProvider.addFile(envPath, envStream); @@ -422,7 +424,7 @@ public void getDefaultCredentials_envAndWellKnownFile_envPrecedence() throws IOE File configDir = new File(homeDir, ".config"); File cloudConfigDir = new File(configDir, DefaultCredentialsProvider.CLOUDSDK_CONFIG_DIRECTORY); InputStream wkfStream = - UserCredentialsTest.writeUserStream(USER_CLIENT_ID, USER_CLIENT_SECRET, refreshTokenWkf); + UserCredentialsTest.writeUserStream(USER_CLIENT_ID, USER_CLIENT_SECRET, refreshTokenWkf, QUOTA_PROJECT); File wellKnownFile = new File(cloudConfigDir, DefaultCredentialsProvider.WELL_KNOWN_CREDENTIALS_FILE); testProvider.setProperty("os.name", "linux"); @@ -480,7 +482,7 @@ private LogRecord getCredentialsAndReturnLogMessage(boolean suppressWarning) thr File configDir = new File(homeDir, ".config"); File cloudConfigDir = new File(configDir, DefaultCredentialsProvider.CLOUDSDK_CONFIG_DIRECTORY); InputStream userStream = - UserCredentialsTest.writeUserStream(GCLOUDSDK_CLIENT_ID, USER_CLIENT_SECRET, REFRESH_TOKEN); + UserCredentialsTest.writeUserStream(GCLOUDSDK_CLIENT_ID, USER_CLIENT_SECRET, REFRESH_TOKEN, QUOTA_PROJECT); File wellKnownFile = new File(cloudConfigDir, DefaultCredentialsProvider.WELL_KNOWN_CREDENTIALS_FILE); TestDefaultCredentialsProvider testProvider = new TestDefaultCredentialsProvider(); diff --git a/oauth2_http/javatests/com/google/auth/oauth2/GoogleCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/GoogleCredentialsTest.java index 99d0bc9aa..1a939fd92 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/GoogleCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/GoogleCredentialsTest.java @@ -73,6 +73,7 @@ public class GoogleCredentialsTest { private static final HttpTransportFactory DUMMY_TRANSPORT_FACTORY = new MockTokenServerTransportFactory(); private static final URI CALL_URI = URI.create("http://googleapis.com/testapi/v1/foo"); + private static final String QUOTA_PROJECT = "sample-quota-project-id"; private static final Collection SCOPES = Collections.unmodifiableCollection(Arrays.asList("scope1", "scope2")); @@ -188,7 +189,7 @@ public void fromStream_user_providesToken() throws IOException { transportFactory.transport.addClient(USER_CLIENT_ID, USER_CLIENT_SECRET); transportFactory.transport.addRefreshToken(REFRESH_TOKEN, ACCESS_TOKEN); InputStream userStream = - UserCredentialsTest.writeUserStream(USER_CLIENT_ID, USER_CLIENT_SECRET, REFRESH_TOKEN); + UserCredentialsTest.writeUserStream(USER_CLIENT_ID, USER_CLIENT_SECRET, REFRESH_TOKEN, null); GoogleCredentials credentials = GoogleCredentials.fromStream(userStream, transportFactory); @@ -200,7 +201,7 @@ public void fromStream_user_providesToken() throws IOException { @Test public void fromStream_userNoClientId_throws() throws IOException { InputStream userStream = - UserCredentialsTest.writeUserStream(null, USER_CLIENT_SECRET, REFRESH_TOKEN); + UserCredentialsTest.writeUserStream(null, USER_CLIENT_SECRET, REFRESH_TOKEN, QUOTA_PROJECT); testFromStreamException(userStream, "client_id"); } @@ -208,7 +209,7 @@ public void fromStream_userNoClientId_throws() throws IOException { @Test public void fromStream_userNoClientSecret_throws() throws IOException { InputStream userStream = - UserCredentialsTest.writeUserStream(USER_CLIENT_ID, null, REFRESH_TOKEN); + UserCredentialsTest.writeUserStream(USER_CLIENT_ID, null, REFRESH_TOKEN, QUOTA_PROJECT); testFromStreamException(userStream, "client_secret"); } @@ -216,7 +217,7 @@ public void fromStream_userNoClientSecret_throws() throws IOException { @Test public void fromStream_userNoRefreshToken_throws() throws IOException { InputStream userStream = - UserCredentialsTest.writeUserStream(USER_CLIENT_ID, USER_CLIENT_SECRET, null); + UserCredentialsTest.writeUserStream(USER_CLIENT_ID, USER_CLIENT_SECRET, null, QUOTA_PROJECT); testFromStreamException(userStream, "refresh_token"); } diff --git a/oauth2_http/javatests/com/google/auth/oauth2/ServiceAccountCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/ServiceAccountCredentialsTest.java index be12fc7f1..1a236f6e3 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/ServiceAccountCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/ServiceAccountCredentialsTest.java @@ -108,6 +108,7 @@ public class ServiceAccountCredentialsTest extends BaseSerializationTest { + "CJhenAiOiIxMDIxMDE1NTA4MzQyMDA3MDg1NjgiLCJleHAiOjE1NjQ0NzUwNTEsImlhdCI6MTU2NDQ3MTQ1MSwi" + "aXNzIjoiaHR0cHM6Ly9hY2NvdW50cy5nb29nbGUuY29tIiwic3ViIjoiMTAyMTAxNTUwODM0MjAwNzA4NTY4In0" + ".redacted"; + private static final String QUOTA_PROJECT = "sample-quota-project-id"; @Test public void createdScoped_clones() throws IOException { @@ -338,7 +339,7 @@ public void fromJSON_getProjectId() throws IOException { transportFactory.transport.addServiceAccount(SA_CLIENT_EMAIL, ACCESS_TOKEN); GenericJson json = writeServiceAccountJson( - SA_CLIENT_ID, SA_CLIENT_EMAIL, SA_PRIVATE_KEY_PKCS8, SA_PRIVATE_KEY_ID, PROJECT_ID); + SA_CLIENT_ID, SA_CLIENT_EMAIL, SA_PRIVATE_KEY_PKCS8, SA_PRIVATE_KEY_ID, PROJECT_ID, null); ServiceAccountCredentials credentials = ServiceAccountCredentials.fromJson(json, transportFactory); @@ -351,7 +352,7 @@ public void fromJSON_getProjectIdNull() throws IOException { transportFactory.transport.addServiceAccount(SA_CLIENT_EMAIL, ACCESS_TOKEN); GenericJson json = writeServiceAccountJson( - SA_CLIENT_ID, SA_CLIENT_EMAIL, SA_PRIVATE_KEY_PKCS8, SA_PRIVATE_KEY_ID, null); + SA_CLIENT_ID, SA_CLIENT_EMAIL, SA_PRIVATE_KEY_PKCS8, SA_PRIVATE_KEY_ID, null, null); ServiceAccountCredentials credentials = ServiceAccountCredentials.fromJson(json, transportFactory); @@ -364,7 +365,7 @@ public void fromJSON_hasAccessToken() throws IOException { transportFactory.transport.addServiceAccount(SA_CLIENT_EMAIL, ACCESS_TOKEN); GenericJson json = writeServiceAccountJson( - SA_CLIENT_ID, SA_CLIENT_EMAIL, SA_PRIVATE_KEY_PKCS8, SA_PRIVATE_KEY_ID, PROJECT_ID); + SA_CLIENT_ID, SA_CLIENT_EMAIL, SA_PRIVATE_KEY_PKCS8, SA_PRIVATE_KEY_ID, PROJECT_ID, null); GoogleCredentials credentials = ServiceAccountCredentials.fromJson(json, transportFactory); @@ -380,13 +381,28 @@ public void fromJSON_tokenServerUri() throws IOException { transportFactory.transport.addServiceAccount(SA_CLIENT_EMAIL, ACCESS_TOKEN); GenericJson json = writeServiceAccountJson( - SA_CLIENT_ID, SA_CLIENT_EMAIL, SA_PRIVATE_KEY_PKCS8, SA_PRIVATE_KEY_ID, PROJECT_ID); + SA_CLIENT_ID, SA_CLIENT_EMAIL, SA_PRIVATE_KEY_PKCS8, SA_PRIVATE_KEY_ID, PROJECT_ID, null); json.put("token_uri", tokenServerUri); ServiceAccountCredentials credentials = ServiceAccountCredentials.fromJson(json, transportFactory); assertEquals(URI.create(tokenServerUri), credentials.getTokenServerUri()); } + @Test + public void fromJson_hasQuotaProjectId() throws IOException { + MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); + transportFactory.transport.addServiceAccount(SA_CLIENT_EMAIL, ACCESS_TOKEN); + GenericJson json = + writeServiceAccountJson( + SA_CLIENT_ID, SA_CLIENT_EMAIL, SA_PRIVATE_KEY_PKCS8, SA_PRIVATE_KEY_ID, PROJECT_ID, QUOTA_PROJECT); + GoogleCredentials credentials = ServiceAccountCredentials.fromJson(json, transportFactory); + credentials = credentials.createScoped(SCOPES); + Map> metadata = credentials.getRequestMetadata(CALL_URI); + + assertTrue(metadata.containsKey(GoogleCredentials.QUOTA_PROJECT_ID_HEADER_KEY)); + assertEquals(metadata.get(GoogleCredentials.QUOTA_PROJECT_ID_HEADER_KEY), Collections.singletonList(QUOTA_PROJECT)); + } + @Test public void getRequestMetadata_hasAccessToken() throws IOException { MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); @@ -876,18 +892,22 @@ public void toString_containsFields() throws IOException { SCOPES, transportFactory, tokenServer, - SERVICE_ACCOUNT_USER); + SERVICE_ACCOUNT_USER, + QUOTA_PROJECT + ); String expectedToString = String.format( "ServiceAccountCredentials{clientId=%s, clientEmail=%s, privateKeyId=%s, " - + "transportFactoryClassName=%s, tokenServerUri=%s, scopes=%s, serviceAccountUser=%s}", + + "transportFactoryClassName=%s, tokenServerUri=%s, scopes=%s, serviceAccountUser=%s, " + + "quotaProjectId=%s}", SA_CLIENT_ID, SA_CLIENT_EMAIL, SA_PRIVATE_KEY_ID, MockTokenServerTransportFactory.class.getName(), tokenServer, SCOPES, - SERVICE_ACCOUNT_USER); + SERVICE_ACCOUNT_USER, + QUOTA_PROJECT); assertEquals(expectedToString, credentials.toString()); } @@ -1015,7 +1035,8 @@ static GenericJson writeServiceAccountJson( String clientEmail, String privateKeyPkcs8, String privateKeyId, - String projectId) { + String projectId, + String quotaProjectId) { GenericJson json = new GenericJson(); if (clientId != null) { json.put("client_id", clientId); @@ -1032,6 +1053,9 @@ static GenericJson writeServiceAccountJson( if (projectId != null) { json.put("project_id", projectId); } + if (quotaProjectId != null) { + json.put("quota_project_id", quotaProjectId); + } json.put("type", GoogleCredentials.SERVICE_ACCOUNT_FILE_TYPE); return json; } @@ -1040,7 +1064,7 @@ static InputStream writeServiceAccountStream( String clientId, String clientEmail, String privateKeyPkcs8, String privateKeyId) throws IOException { GenericJson json = - writeServiceAccountJson(clientId, clientEmail, privateKeyPkcs8, privateKeyId, null); + writeServiceAccountJson(clientId, clientEmail, privateKeyPkcs8, privateKeyId, null, null); return TestUtils.jsonToInputStream(json); } diff --git a/oauth2_http/javatests/com/google/auth/oauth2/ServiceAccountJwtAccessCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/ServiceAccountJwtAccessCredentialsTest.java index 5435148c6..faf7d3008 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/ServiceAccountJwtAccessCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/ServiceAccountJwtAccessCredentialsTest.java @@ -57,6 +57,7 @@ import java.security.PrivateKey; import java.security.Signature; import java.security.SignatureException; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; @@ -90,6 +91,7 @@ public class ServiceAccountJwtAccessCredentialsTest extends BaseSerializationTes ServiceAccountJwtAccessCredentials.JWT_ACCESS_PREFIX; private static final URI CALL_URI = URI.create("http://googleapis.com/testapi/v1/foo"); private static final JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance(); + private static final String QUOTA_PROJECT = "sample-quota-project-id"; @Test public void constructor_allParameters_constructs() throws IOException { @@ -396,6 +398,24 @@ public void getRequestMetadata_async_cached() throws IOException { assertEquals(callback1.metadata, callback2.metadata); } + @Test + public void getRequestMetadata_contains_quotaProjectId() throws IOException { + PrivateKey privateKey = ServiceAccountCredentials.privateKeyFromPkcs8(SA_PRIVATE_KEY_PKCS8); + Credentials credentials = + ServiceAccountJwtAccessCredentials.newBuilder() + .setClientId(SA_CLIENT_ID) + .setClientEmail(SA_CLIENT_EMAIL) + .setPrivateKey(privateKey) + .setPrivateKeyId(SA_PRIVATE_KEY_ID) + .setDefaultAudience(CALL_URI) + .setQuotaProjectId(QUOTA_PROJECT) + .build(); + + Map> metadata = credentials.getRequestMetadata(CALL_URI); + assertTrue(metadata.containsKey(GoogleCredentials.QUOTA_PROJECT_ID_HEADER_KEY)); + assertEquals(metadata.get(GoogleCredentials.QUOTA_PROJECT_ID_HEADER_KEY), Collections.singletonList(QUOTA_PROJECT)); + } + @Test public void getAccount_sameAs() throws IOException { PrivateKey privateKey = ServiceAccountCredentials.privateKeyFromPkcs8(SA_PRIVATE_KEY_PKCS8); @@ -554,11 +574,12 @@ public void toString_containsFields() throws IOException { .setPrivateKey(privateKey) .setPrivateKeyId(SA_PRIVATE_KEY_ID) .setDefaultAudience(CALL_URI) + .setQuotaProjectId(QUOTA_PROJECT) .build(); String expectedToString = String.format( "ServiceAccountJwtAccessCredentials{clientId=%s, clientEmail=%s, privateKeyId=%s, " - + "defaultAudience=%s}", + + "defaultAudience=%s, quotaProjectId=%s}", SA_CLIENT_ID, SA_CLIENT_EMAIL, SA_PRIVATE_KEY_ID, CALL_URI); assertEquals(expectedToString, credentials.toString()); } diff --git a/oauth2_http/javatests/com/google/auth/oauth2/UserCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/UserCredentialsTest.java index f34fa6c87..a7c22948a 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/UserCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/UserCredentialsTest.java @@ -68,6 +68,7 @@ public class UserCredentialsTest extends BaseSerializationTest { private static final String CLIENT_ID = "ya29.1.AADtN_UtlxN3PuGAxrN2XQnZTVRvDyVWnYq4I6dws"; private static final String REFRESH_TOKEN = "1/Tl6awhpFjkMkSJoj1xsli0H2eL5YsMgU_NKPY2TyGWY"; private static final String ACCESS_TOKEN = "1/MkSJoj1xsli0AccessToken_NKPY2"; + private static final String QUOTA_PROJECT = "sample-quota-project-id"; private static final Collection SCOPES = Collections.singletonList("dummy.scope"); private static final URI CALL_URI = URI.create("http://googleapis.com/testapi/v1/foo"); @@ -114,7 +115,7 @@ public void fromJson_hasAccessToken() throws IOException { MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); transportFactory.transport.addClient(CLIENT_ID, CLIENT_SECRET); transportFactory.transport.addRefreshToken(REFRESH_TOKEN, ACCESS_TOKEN); - GenericJson json = writeUserJson(CLIENT_ID, CLIENT_SECRET, REFRESH_TOKEN); + GenericJson json = writeUserJson(CLIENT_ID, CLIENT_SECRET, REFRESH_TOKEN, null); GoogleCredentials credentials = UserCredentials.fromJson(json, transportFactory); @@ -122,6 +123,20 @@ public void fromJson_hasAccessToken() throws IOException { TestUtils.assertContainsBearerToken(metadata, ACCESS_TOKEN); } + @Test + public void fromJson_hasQuotaProjectId() throws IOException { + MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); + transportFactory.transport.addClient(CLIENT_ID, CLIENT_SECRET); + transportFactory.transport.addRefreshToken(REFRESH_TOKEN, ACCESS_TOKEN); + GenericJson json = writeUserJson(CLIENT_ID, CLIENT_SECRET, REFRESH_TOKEN, QUOTA_PROJECT); + + GoogleCredentials credentials = UserCredentials.fromJson(json, transportFactory); + + Map> metadata = credentials.getRequestMetadata(CALL_URI); + assertTrue(metadata.containsKey(GoogleCredentials.QUOTA_PROJECT_ID_HEADER_KEY)); + assertEquals(metadata.get(GoogleCredentials.QUOTA_PROJECT_ID_HEADER_KEY), Collections.singletonList(QUOTA_PROJECT)); + } + @Test public void getRequestMetadata_initialToken_hasAccessToken() throws IOException { MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); @@ -213,6 +228,7 @@ public void equals_true() throws IOException { .setAccessToken(accessToken) .setHttpTransportFactory(transportFactory) .setTokenServerUri(tokenServer) + .setQuotaProjectId(QUOTA_PROJECT) .build(); UserCredentials otherCredentials = UserCredentials.newBuilder() @@ -222,6 +238,7 @@ public void equals_true() throws IOException { .setAccessToken(accessToken) .setHttpTransportFactory(transportFactory) .setTokenServerUri(tokenServer) + .setQuotaProjectId(QUOTA_PROJECT) .build(); assertTrue(credentials.equals(otherCredentials)); assertTrue(otherCredentials.equals(credentials)); @@ -392,6 +409,35 @@ public void equals_false_tokenServer() throws IOException { assertFalse(otherCredentials.equals(credentials)); } + @Test + public void equals_false_quotaProjectId() throws IOException { + final String quotaProject1 = "sample-id-1"; + final String quotaProject2 = "sample-id-2"; + AccessToken accessToken = new AccessToken(ACCESS_TOKEN, null); + MockHttpTransportFactory httpTransportFactory = new MockHttpTransportFactory(); + UserCredentials credentials = + UserCredentials.newBuilder() + .setClientId(CLIENT_ID) + .setClientSecret(CLIENT_SECRET) + .setRefreshToken(REFRESH_TOKEN) + .setAccessToken(accessToken) + .setHttpTransportFactory(httpTransportFactory) + .setQuotaProjectId(quotaProject1) + .build(); + UserCredentials otherCredentials = + UserCredentials.newBuilder() + .setClientId(CLIENT_ID) + .setClientSecret(CLIENT_SECRET) + .setRefreshToken(REFRESH_TOKEN) + .setAccessToken(accessToken) + .setHttpTransportFactory(httpTransportFactory) + .setQuotaProjectId(quotaProject2) + .build(); + assertFalse(credentials.equals(otherCredentials)); + assertFalse(otherCredentials.equals(credentials)); + } + + @Test public void toString_containsFields() throws IOException { AccessToken accessToken = new AccessToken(ACCESS_TOKEN, null); @@ -405,12 +451,13 @@ public void toString_containsFields() throws IOException { .setAccessToken(accessToken) .setHttpTransportFactory(transportFactory) .setTokenServerUri(tokenServer) + .setQuotaProjectId(QUOTA_PROJECT) .build(); String expectedToString = String.format( "UserCredentials{requestMetadata=%s, temporaryAccess=%s, clientId=%s, refreshToken=%s, " - + "tokenServerUri=%s, transportFactoryClassName=%s}", + + "tokenServerUri=%s, transportFactoryClassName=%s, quotaProjectId=%s}", ImmutableMap.of( AuthHttpConstants.AUTHORIZATION, ImmutableList.of(OAuth2Utils.BEARER_PREFIX + accessToken.getTokenValue())), @@ -418,7 +465,8 @@ public void toString_containsFields() throws IOException { CLIENT_ID, REFRESH_TOKEN, tokenServer, - MockTokenServerTransportFactory.class.getName()); + MockTokenServerTransportFactory.class.getName(), + QUOTA_PROJECT); assertEquals(expectedToString, credentials.toString()); } @@ -435,6 +483,7 @@ public void hashCode_equals() throws IOException { .setAccessToken(accessToken) .setHttpTransportFactory(transportFactory) .setTokenServerUri(tokenServer) + .setQuotaProjectId(QUOTA_PROJECT) .build(); UserCredentials otherCredentials = UserCredentials.newBuilder() @@ -444,6 +493,7 @@ public void hashCode_equals() throws IOException { .setAccessToken(accessToken) .setHttpTransportFactory(transportFactory) .setTokenServerUri(tokenServer) + .setQuotaProjectId(QUOTA_PROJECT) .build(); assertEquals(credentials.hashCode(), otherCredentials.hashCode()); } @@ -496,7 +546,7 @@ public void fromStream_user_providesToken() throws IOException { MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); transportFactory.transport.addClient(CLIENT_ID, CLIENT_SECRET); transportFactory.transport.addRefreshToken(REFRESH_TOKEN, ACCESS_TOKEN); - InputStream userStream = writeUserStream(CLIENT_ID, CLIENT_SECRET, REFRESH_TOKEN); + InputStream userStream = writeUserStream(CLIENT_ID, CLIENT_SECRET, REFRESH_TOKEN, QUOTA_PROJECT); UserCredentials credentials = UserCredentials.fromStream(userStream, transportFactory); @@ -507,21 +557,21 @@ public void fromStream_user_providesToken() throws IOException { @Test public void fromStream_userNoClientId_throws() throws IOException { - InputStream userStream = writeUserStream(null, CLIENT_SECRET, REFRESH_TOKEN); + InputStream userStream = writeUserStream(null, CLIENT_SECRET, REFRESH_TOKEN, QUOTA_PROJECT); testFromStreamException(userStream, "client_id"); } @Test public void fromStream_userNoClientSecret_throws() throws IOException { - InputStream userStream = writeUserStream(CLIENT_ID, null, REFRESH_TOKEN); + InputStream userStream = writeUserStream(CLIENT_ID, null, REFRESH_TOKEN, QUOTA_PROJECT); testFromStreamException(userStream, "client_secret"); } @Test public void fromStream_userNoRefreshToken_throws() throws IOException { - InputStream userStream = writeUserStream(CLIENT_ID, CLIENT_SECRET, null); + InputStream userStream = writeUserStream(CLIENT_ID, CLIENT_SECRET, null, QUOTA_PROJECT); testFromStreamException(userStream, "refresh_token"); } @@ -566,7 +616,7 @@ public void saveAndRestoreUserCredential_saveAndRestored_throws() throws IOExcep assertEquals(userCredentials.getRefreshToken(), restoredCredentials.getRefreshToken()); } - static GenericJson writeUserJson(String clientId, String clientSecret, String refreshToken) { + static GenericJson writeUserJson(String clientId, String clientSecret, String refreshToken, String quotaProjectId) { GenericJson json = new GenericJson(); if (clientId != null) { json.put("client_id", clientId); @@ -577,13 +627,16 @@ static GenericJson writeUserJson(String clientId, String clientSecret, String re if (refreshToken != null) { json.put("refresh_token", refreshToken); } + if (quotaProjectId != null) { + json.put("quota_project_id", quotaProjectId); + } json.put("type", GoogleCredentials.USER_FILE_TYPE); return json; } - static InputStream writeUserStream(String clientId, String clientSecret, String refreshToken) + static InputStream writeUserStream(String clientId, String clientSecret, String refreshToken, String quotaProjectId) throws IOException { - GenericJson json = writeUserJson(clientId, clientSecret, refreshToken); + GenericJson json = writeUserJson(clientId, clientSecret, refreshToken, quotaProjectId); return TestUtils.jsonToInputStream(json); }