Skip to content

Commit

Permalink
feat: Introduce Environment Variable for Quota Project Id (#1082)
Browse files Browse the repository at this point in the history
* feat: Introduce Environment Variable for Quota Project Id

* add back constructr

* move env logic to GoogleCredentials

* static to non-static

* EnvironmentProvider in builder

* move env logic back to adc

* null check

* add functional test

* Adding env var to cfg

* Adding env var to cfg

* clean up cfgs

* clirr exemption and builder constructor

* lint

* update classname

* clean up

* formatting

* updating tests

* fix fn test

* copyright 2022

* add back a FT

* allow null quota project

* update javadoc

* nits
  • Loading branch information
sai-sunder-s committed Nov 30, 2022
1 parent 517e450 commit 040acef
Show file tree
Hide file tree
Showing 12 changed files with 221 additions and 117 deletions.
5 changes: 5 additions & 0 deletions .kokoro/nightly/integration.cfg
Expand Up @@ -45,3 +45,8 @@ env_vars: {
key: "GOOGLE_EXTERNAL_ACCOUNT_ALLOW_EXECUTABLES"
value: "1"
}

env_vars: {
key: "GOOGLE_CLOUD_QUOTA_PROJECT"
value: "gcloud-devel"
}
5 changes: 5 additions & 0 deletions .kokoro/presubmit/integration.cfg
Expand Up @@ -41,3 +41,8 @@ env_vars: {
key: "GOOGLE_EXTERNAL_ACCOUNT_ALLOW_EXECUTABLES"
value: "1"
}

env_vars: {
key: "GOOGLE_CLOUD_QUOTA_PROJECT"
value: "gcloud-devel"
}
9 changes: 9 additions & 0 deletions oauth2_http/clirr-ignored-differences.xml
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- see http://www.mojohaus.org/clirr-maven-plugin/examples/ignored-differences.html -->
<differences>
<difference>
<differenceType>6001</differenceType>
<className>com/google/auth/oauth2/ExternalAccountCredentials$Builder</className>
<field>quotaProjectId</field>
</difference>
</differences>
Expand Up @@ -53,8 +53,11 @@
* overriding the state and environment for testing purposes.
*/
class DefaultCredentialsProvider {

static final DefaultCredentialsProvider DEFAULT = new DefaultCredentialsProvider();
static final String CREDENTIAL_ENV_VAR = "GOOGLE_APPLICATION_CREDENTIALS";
static final String QUOTA_PROJECT_ENV_VAR = "GOOGLE_CLOUD_QUOTA_PROJECT";

static final String WELL_KNOWN_CREDENTIALS_FILE = "application_default_credentials.json";
static final String CLOUDSDK_CONFIG_DIRECTORY = "gcloud";
static final String HELP_PERMALINK =
Expand Down Expand Up @@ -214,6 +217,14 @@ private final GoogleCredentials getDefaultCredentialsUnsynchronized(
credentials = tryGetComputeCredentials(transportFactory);
}

if (credentials != null) {
String quotaFromEnv = getEnv(QUOTA_PROJECT_ENV_VAR);

if (quotaFromEnv != null && quotaFromEnv.trim().length() > 0) {
credentials = credentials.createWithQuotaProject(quotaFromEnv);
}
}

return credentials;
}

Expand Down
Expand Up @@ -64,8 +64,7 @@
* <p>Handles initializing external credentials, calls to the Security Token Service, and service
* account impersonation.
*/
public abstract class ExternalAccountCredentials extends GoogleCredentials
implements QuotaProjectIdProvider {
public abstract class ExternalAccountCredentials extends GoogleCredentials {

/** Base credential source class. Dictates the retrieval method of the external credential. */
abstract static class CredentialSource {
Expand All @@ -91,7 +90,6 @@ abstract static class CredentialSource {

@Nullable private final String tokenInfoUrl;
@Nullable private final String serviceAccountImpersonationUrl;
@Nullable private final String quotaProjectId;
@Nullable private final String clientId;
@Nullable private final String clientSecret;

Expand Down Expand Up @@ -194,6 +192,7 @@ protected ExternalAccountCredentials(
@Nullable String clientSecret,
@Nullable Collection<String> scopes,
@Nullable EnvironmentProvider environmentProvider) {
super(/* accessToken= */ null, quotaProjectId);
this.transportFactory =
MoreObjects.firstNonNull(
transportFactory,
Expand All @@ -205,7 +204,6 @@ protected ExternalAccountCredentials(
this.credentialSource = checkNotNull(credentialSource);
this.tokenInfoUrl = tokenInfoUrl;
this.serviceAccountImpersonationUrl = serviceAccountImpersonationUrl;
this.quotaProjectId = quotaProjectId;
this.clientId = clientId;
this.clientSecret = clientSecret;
this.scopes =
Expand All @@ -231,6 +229,7 @@ protected ExternalAccountCredentials(
* @param builder the {@code Builder} object used to construct the credentials.
*/
protected ExternalAccountCredentials(ExternalAccountCredentials.Builder builder) {
super(builder);
this.transportFactory =
MoreObjects.firstNonNull(
builder.transportFactory,
Expand All @@ -242,7 +241,6 @@ protected ExternalAccountCredentials(ExternalAccountCredentials.Builder builder)
this.credentialSource = checkNotNull(builder.credentialSource);
this.tokenInfoUrl = builder.tokenInfoUrl;
this.serviceAccountImpersonationUrl = builder.serviceAccountImpersonationUrl;
this.quotaProjectId = builder.quotaProjectId;
this.clientId = builder.clientId;
this.clientSecret = builder.clientSecret;
this.scopes =
Expand Down Expand Up @@ -550,12 +548,6 @@ public String getServiceAccountEmail() {
return ImpersonatedCredentials.extractTargetPrincipal(serviceAccountImpersonationUrl);
}

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

@Nullable
public String getClientId() {
return clientId;
Expand Down Expand Up @@ -721,7 +713,6 @@ public abstract static class Builder extends GoogleCredentials.Builder {
protected HttpTransportFactory transportFactory;

@Nullable protected String serviceAccountImpersonationUrl;
@Nullable protected String quotaProjectId;
@Nullable protected String clientId;
@Nullable protected String clientSecret;
@Nullable protected Collection<String> scopes;
Expand All @@ -731,14 +722,14 @@ public abstract static class Builder extends GoogleCredentials.Builder {
protected Builder() {}

protected Builder(ExternalAccountCredentials credentials) {
super(credentials);
this.transportFactory = credentials.transportFactory;
this.audience = credentials.audience;
this.subjectTokenType = credentials.subjectTokenType;
this.tokenUrl = credentials.tokenUrl;
this.tokenInfoUrl = credentials.tokenInfoUrl;
this.serviceAccountImpersonationUrl = credentials.serviceAccountImpersonationUrl;
this.credentialSource = credentials.credentialSource;
this.quotaProjectId = credentials.quotaProjectId;
this.clientId = credentials.clientId;
this.clientSecret = credentials.clientSecret;
this.scopes = credentials.scopes;
Expand Down Expand Up @@ -836,7 +827,7 @@ public Builder setTokenInfoUrl(String tokenInfoUrl) {
* @return this {@code Builder} object
*/
public Builder setQuotaProjectId(String quotaProjectId) {
this.quotaProjectId = quotaProjectId;
super.setQuotaProjectId(quotaProjectId);
return this;
}

Expand Down
58 changes: 54 additions & 4 deletions oauth2_http/java/com/google/auth/oauth2/GoogleCredentials.java
Expand Up @@ -46,16 +46,19 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;

/** Base type for credentials for authorizing calls to Google APIs using OAuth2. */
public class GoogleCredentials extends OAuth2Credentials {
public class GoogleCredentials extends OAuth2Credentials implements QuotaProjectIdProvider {

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

protected final String quotaProjectId;

private static final DefaultCredentialsProvider defaultCredentialsProvider =
new DefaultCredentialsProvider();

Expand Down Expand Up @@ -184,6 +187,16 @@ public static GoogleCredentials fromStream(
fileType, USER_FILE_TYPE, SERVICE_ACCOUNT_FILE_TYPE));
}

/**
* Creates a credential with the provided quota project.
*
* @param quotaProject the quota project to set on the credential
* @return credential with the provided quota project
*/
public GoogleCredentials createWithQuotaProject(String quotaProject) {
return this.toBuilder().setQuotaProjectId(quotaProject).build();
}

/**
* Adds quota project ID to requestMetadata if present.
*
Expand All @@ -200,9 +213,24 @@ static Map<String, List<String>> addQuotaProjectIdToRequestMetadata(
return Collections.unmodifiableMap(newRequestMetadata);
}

@Override
protected Map<String, List<String>> getAdditionalHeaders() {
Map<String, List<String>> headers = super.getAdditionalHeaders();
String quotaProjectId = this.getQuotaProjectId();
if (quotaProjectId != null) {
return addQuotaProjectIdToRequestMetadata(quotaProjectId, headers);
}
return headers;
}

/** Default constructor. */
protected GoogleCredentials() {
this(null);
this(new Builder());
}

protected GoogleCredentials(AccessToken accessToken, String quotaProjectId) {
super(accessToken);
this.quotaProjectId = quotaProjectId;
}

/**
Expand All @@ -211,7 +239,11 @@ protected GoogleCredentials() {
* @param accessToken initial or temporary access token
*/
public GoogleCredentials(AccessToken accessToken) {
super(accessToken);
this(accessToken, null);
}

protected GoogleCredentials(Builder builder) {
this(builder.getAccessToken(), builder.getQuotaProjectId());
}

/**
Expand All @@ -222,6 +254,7 @@ public GoogleCredentials(AccessToken accessToken) {
protected GoogleCredentials(
AccessToken accessToken, Duration refreshMargin, Duration expirationMargin) {
super(accessToken, refreshMargin, expirationMargin);
this.quotaProjectId = null;
}

public static Builder newBuilder() {
Expand All @@ -232,6 +265,11 @@ public Builder toBuilder() {
return new Builder(this);
}

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

/**
* Indicates whether the credentials require scopes to be specified via a call to {@link
* GoogleCredentials#createScoped} before use.
Expand Down Expand Up @@ -300,14 +338,26 @@ public GoogleCredentials createDelegated(String user) {
}

public static class Builder extends OAuth2Credentials.Builder {
@Nullable protected String quotaProjectId;

protected Builder() {}

protected Builder(GoogleCredentials credentials) {
setAccessToken(credentials.getAccessToken());
this.quotaProjectId = credentials.quotaProjectId;
}

public GoogleCredentials build() {
return new GoogleCredentials(getAccessToken());
return new GoogleCredentials(this);
}

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

public String getQuotaProjectId() {
return this.quotaProjectId;
}

@Override
Expand Down
Expand Up @@ -89,7 +89,7 @@
* </pre>
*/
public class ImpersonatedCredentials extends GoogleCredentials
implements ServiceAccountSigner, IdTokenProvider, QuotaProjectIdProvider {
implements ServiceAccountSigner, IdTokenProvider {

private static final long serialVersionUID = -2133257318957488431L;
private static final String RFC3339 = "yyyy-MM-dd'T'HH:mm:ssX";
Expand All @@ -105,7 +105,6 @@ public class ImpersonatedCredentials extends GoogleCredentials
private List<String> delegates;
private List<String> scopes;
private int lifetime;
private String quotaProjectId;
private String iamEndpointOverride;
private final String transportFactoryClassName;

Expand Down Expand Up @@ -304,11 +303,6 @@ public String getAccount() {
return this.targetPrincipal;
}

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

@VisibleForTesting
String getIamEndpointOverride() {
return this.iamEndpointOverride;
Expand Down Expand Up @@ -451,16 +445,8 @@ public ImpersonatedCredentials createWithCustomCalendar(Calendar calendar) {
.build();
}

@Override
protected Map<String, List<String>> getAdditionalHeaders() {
Map<String, List<String>> headers = super.getAdditionalHeaders();
if (quotaProjectId != null) {
return addQuotaProjectIdToRequestMetadata(quotaProjectId, headers);
}
return headers;
}

private ImpersonatedCredentials(Builder builder) {
super(builder);
this.sourceCredentials = builder.getSourceCredentials();
this.targetPrincipal = builder.getTargetPrincipal();
this.delegates = builder.getDelegates();
Expand All @@ -470,7 +456,6 @@ private ImpersonatedCredentials(Builder builder) {
firstNonNull(
builder.getHttpTransportFactory(),
getFromServiceLoader(HttpTransportFactory.class, OAuth2Utils.HTTP_TRANSPORT_FACTORY));
this.quotaProjectId = builder.quotaProjectId;
this.iamEndpointOverride = builder.iamEndpointOverride;
this.transportFactoryClassName = this.transportFactory.getClass().getName();
this.calendar = builder.getCalendar();
Expand Down Expand Up @@ -628,7 +613,6 @@ public static class Builder extends GoogleCredentials.Builder {
private List<String> scopes;
private int lifetime = DEFAULT_LIFETIME_IN_SECONDS;
private HttpTransportFactory transportFactory;
private String quotaProjectId;
private String iamEndpointOverride;
private Calendar calendar = Calendar.getInstance();

Expand Down Expand Up @@ -694,7 +678,7 @@ public HttpTransportFactory getHttpTransportFactory() {
}

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

Expand Down

0 comments on commit 040acef

Please sign in to comment.