Skip to content

Commit

Permalink
feat: support reading in quotaProjectId for billing (#383)
Browse files Browse the repository at this point in the history
  • Loading branch information
codyoss committed Dec 13, 2019
1 parent 7267f3a commit f38c3c8
Show file tree
Hide file tree
Showing 11 changed files with 566 additions and 264 deletions.
22 changes: 22 additions & 0 deletions oauth2_http/java/com/google/auth/oauth2/GoogleCredentials.java
Expand Up @@ -40,11 +40,17 @@
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/** 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";

Expand Down Expand Up @@ -166,6 +172,22 @@ 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
*/
static Map<String, List<String>> addQuotaProjectIdToRequestMetadata(
String quotaProjectId, Map<String, List<String>> requestMetadata) {
Preconditions.checkNotNull(requestMetadata);
Map<String, List<String>> newRequestMetadata = new HashMap<>(requestMetadata);
if (quotaProjectId != null && !requestMetadata.containsKey(QUOTA_PROJECT_ID_HEADER_KEY)) {
newRequestMetadata.put(
QUOTA_PROJECT_ID_HEADER_KEY, Collections.singletonList(quotaProjectId));
}
return Collections.unmodifiableMap(newRequestMetadata);
}

/** Default constructor. */
protected GoogleCredentials() {
this(null);
Expand Down
@@ -0,0 +1,38 @@
/*
* Copyright 2019, 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;

/** Interface for {@link GoogleCredentials} that return a quota project ID. */
public interface QuotaProjectIdProvider {
/** @return the quota project ID used for quota and billing purposes */
String getQuotaProjectId();
}
Expand Up @@ -87,7 +87,7 @@
* <p>By default uses a JSON Web Token (JWT) to fetch access tokens.
*/
public class ServiceAccountCredentials extends GoogleCredentials
implements ServiceAccountSigner, IdTokenProvider, JwtProvider {
implements ServiceAccountSigner, IdTokenProvider, JwtProvider, QuotaProjectIdProvider {

private static final long serialVersionUID = 7807543542681217978L;
private static final String GRANT_TYPE = "urn:ietf:params:oauth:grant-type:jwt-bearer";
Expand All @@ -102,6 +102,7 @@ public class ServiceAccountCredentials extends GoogleCredentials
private final String transportFactoryClassName;
private final URI tokenServerUri;
private final Collection<String> scopes;
private final String quotaProjectId;

private transient HttpTransportFactory transportFactory;

Expand All @@ -119,6 +120,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,
Expand All @@ -129,7 +132,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);
Expand All @@ -143,6 +147,7 @@ public class ServiceAccountCredentials extends GoogleCredentials
this.tokenServerUri = (tokenServerUri == null) ? OAuth2Utils.TOKEN_SERVER_URI : tokenServerUri;
this.serviceAccountUser = serviceAccountUser;
this.projectId = projectId;
this.quotaProjectId = quotaProjectId;
}

/**
Expand All @@ -163,6 +168,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 quotaProjectId = (String) json.get("quota_project_id");
URI tokenServerUriFromCreds = null;
try {
if (tokenServerUriStringFromCreds != null) {
Expand All @@ -189,7 +195,8 @@ static ServiceAccountCredentials fromJson(
transportFactory,
tokenServerUriFromCreds,
null,
projectId);
projectId,
quotaProjectId);
}

/**
Expand All @@ -212,7 +219,7 @@ public static ServiceAccountCredentials fromPkcs8(
Collection<String> scopes)
throws IOException {
return fromPkcs8(
clientId, clientEmail, privateKeyPkcs8, privateKeyId, scopes, null, null, null);
clientId, clientEmail, privateKeyPkcs8, privateKeyId, scopes, null, null, null, null, null);
}

/**
Expand Down Expand Up @@ -248,6 +255,8 @@ public static ServiceAccountCredentials fromPkcs8(
scopes,
transportFactory,
tokenServerUri,
null,
null,
null);
}

Expand Down Expand Up @@ -288,6 +297,7 @@ public static ServiceAccountCredentials fromPkcs8(
transportFactory,
tokenServerUri,
serviceAccountUser,
null,
null);
}

Expand All @@ -300,7 +310,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(
Expand All @@ -312,7 +323,8 @@ static ServiceAccountCredentials fromPkcs8(
transportFactory,
tokenServerUri,
serviceAccountUser,
projectId);
projectId,
quotaProject);
}

/** Helper to convert from a PKCS#8 String to an RSA private key */
Expand Down Expand Up @@ -452,7 +464,7 @@ public boolean isRequired(HttpResponse response) {
*/
@Beta
@Override
public IdToken idTokenWithAudience(String targetAudience, List<IdTokenProvider.Option> options)
public IdToken idTokenWithAudience(String targetAudience, List<Option> options)
throws IOException {

JsonFactory jsonFactory = OAuth2Utils.JSON_FACTORY;
Expand Down Expand Up @@ -499,7 +511,8 @@ public GoogleCredentials createScoped(Collection<String> newScopes) {
transportFactory,
tokenServerUri,
serviceAccountUser,
projectId);
projectId,
quotaProjectId);
}

@Override
Expand All @@ -513,7 +526,8 @@ public GoogleCredentials createDelegated(String user) {
transportFactory,
tokenServerUri,
user,
projectId);
projectId,
quotaProjectId);
}

public final String getClientId() {
Expand Down Expand Up @@ -584,6 +598,12 @@ public JwtCredentials jwtWithClaims(JwtClaims newClaims) {
.build();
}

@Override
public Map<String, List<String>> getRequestMetadata(URI uri) throws IOException {
Map<String, List<String>> requestMetadata = super.getRequestMetadata(uri);
return addQuotaProjectIdToRequestMetadata(quotaProjectId, requestMetadata);
}

@Override
public int hashCode() {
return Objects.hash(
Expand All @@ -593,7 +613,8 @@ public int hashCode() {
privateKeyId,
transportFactoryClassName,
tokenServerUri,
scopes);
scopes,
quotaProjectId);
}

@Override
Expand All @@ -606,6 +627,7 @@ public String toString() {
.add("tokenServerUri", tokenServerUri)
.add("scopes", scopes)
.add("serviceAccountUser", serviceAccountUser)
.add("quotaProjectId", quotaProjectId)
.toString();
}

Expand All @@ -621,7 +643,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)
Expand Down Expand Up @@ -702,6 +725,11 @@ public Builder toBuilder() {
return new Builder(this);
}

@Override
public String getQuotaProjectId() {
return quotaProjectId;
}

public static class Builder extends GoogleCredentials.Builder {

private String clientId;
Expand All @@ -713,6 +741,7 @@ public static class Builder extends GoogleCredentials.Builder {
private URI tokenServerUri;
private Collection<String> scopes;
private HttpTransportFactory transportFactory;
private String quotaProjectId;

protected Builder() {}

Expand All @@ -726,6 +755,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) {
Expand Down Expand Up @@ -773,6 +803,11 @@ public Builder setHttpTransportFactory(HttpTransportFactory transportFactory) {
return this;
}

public Builder setQuotaProjectId(String quotaProjectId) {
this.quotaProjectId = quotaProjectId;
return this;
}

public String getClientId() {
return clientId;
}
Expand Down Expand Up @@ -809,6 +844,10 @@ public HttpTransportFactory getHttpTransportFactory() {
return transportFactory;
}

public String getQuotaProjectId() {
return quotaProjectId;
}

public ServiceAccountCredentials build() {
return new ServiceAccountCredentials(
clientId,
Expand All @@ -819,7 +858,8 @@ public ServiceAccountCredentials build() {
transportFactory,
tokenServerUri,
serviceAccountUser,
projectId);
projectId,
quotaProjectId);
}
}
}

0 comments on commit f38c3c8

Please sign in to comment.