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 1 commit
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: 21 additions & 1 deletion oauth2_http/java/com/google/auth/oauth2/GoogleCredentials.java
Expand Up @@ -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.*;
codyoss marked this conversation as resolved.
Show resolved Hide resolved

/** 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 +168,24 @@ 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
codyoss marked this conversation as resolved.
Show resolved Hide resolved
* requestMetadata
*/
static Map<String, List<String>> addQuotaProjectIdToRequestMetadata(
String quotaProjectId, Map<String, List<String>> requestMetadata) {
Preconditions.checkNotNull(requestMetadata, "requestMetadata");
chingor13 marked this conversation as resolved.
Show resolved Hide resolved
if (quotaProjectId != null && !requestMetadata.containsKey(QUOTA_PROJECT_ID_HEADER_KEY)) {
Map<String, List<String>> 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);
Expand Down
102 changes: 87 additions & 15 deletions oauth2_http/java/com/google/auth/oauth2/ServiceAccountCredentials.java
Expand Up @@ -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.*;
codyoss marked this conversation as resolved.
Show resolved Hide resolved

/**
* OAuth2 credentials representing a Service Account for calling Google APIs.
Expand All @@ -102,6 +98,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 +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.
codyoss marked this conversation as resolved.
Show resolved Hide resolved
* @param quotaProjectId The project used for quota and billing purposes. May be null.
*/
ServiceAccountCredentials(
String clientId,
Expand All @@ -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);
Expand All @@ -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;
}

/**
Expand All @@ -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");
chingor13 marked this conversation as resolved.
Show resolved Hide resolved
URI tokenServerUriFromCreds = null;
try {
if (tokenServerUriStringFromCreds != null) {
Expand All @@ -189,7 +191,8 @@ static ServiceAccountCredentials fromJson(
transportFactory,
tokenServerUriFromCreds,
null,
projectId);
projectId,
quotaProject);
}

/**
Expand All @@ -212,7 +215,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);
}

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

Expand Down Expand Up @@ -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.
codyoss marked this conversation as resolved.
Show resolved Hide resolved
* @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(
codyoss marked this conversation as resolved.
Show resolved Hide resolved
String clientId,
String clientEmail,
String privateKeyPkcs8,
String privateKeyId,
Collection<String> 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,
Expand All @@ -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(
Expand All @@ -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 */
Expand Down Expand Up @@ -499,7 +548,8 @@ public GoogleCredentials createScoped(Collection<String> newScopes) {
transportFactory,
tokenServerUri,
serviceAccountUser,
projectId);
projectId,
quotaProjectId);
}

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

public final String getClientId() {
Expand Down Expand Up @@ -584,6 +635,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 +650,8 @@ public int hashCode() {
privateKeyId,
transportFactoryClassName,
tokenServerUri,
scopes);
scopes,
quotaProjectId);
}

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

Expand All @@ -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)
Expand Down Expand Up @@ -713,6 +773,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 +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) {
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -809,6 +876,10 @@ public HttpTransportFactory getHttpTransportFactory() {
return transportFactory;
}

public String getQuotaProjectId() {
return quotaProjectId;
}

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