Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support reading in quotaProjectId for billing #383

Merged
merged 10 commits into from Dec 13, 2019
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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);
}
}
}