Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,9 @@ public static class Builder extends ServiceOptions.Builder<BigQuery, BigQueryOpt
private Tracer openTelemetryTracer;
private ResultRetryAlgorithm<?> resultRetryAlgorithm;

private Builder() {}
private Builder() {
setUseJwtAccessWithScope(false);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For BQ and Storage, are there any internal tracking tickets that we can link for these exceptions (e.g. default false)? IIRC, these are more so temporary and not permanent as we want this for default. I think would be helpful for future context

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good suggestion, I added tracking tickets to the PR description.

}

private Builder(BigQueryOptions options) {
super(options);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,11 @@ void dataFormatOptionsSetterHasPrecedence() {

assertTrue(options.getDataFormatOptions().useInt64Timestamp());
}

@Test
void testUseJwtAccessWithScope_defaultsToFalse() {
BigQueryOptions options = BigQueryOptions.newBuilder().setProjectId("project-id").build();

assertFalse(options.getUseJwtAccessWithScope());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,9 @@ public DefaultStorageRpcFactory() {
public abstract static class Builder
extends ServiceOptions.Builder<Storage, StorageOptions, Builder> {

Builder() {}
Builder() {
setUseJwtAccessWithScope(false);
}

Builder(StorageOptions options) {
super(options);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,15 @@ public void grpc() throws Exception {
() -> assertThat(rebuilt.hashCode()).isEqualTo(base.hashCode()));
}

@Test
public void useJwtAccessWithScope_defaultsToFalse() {
HttpStorageOptions httpOptions = HttpStorageOptions.http().build();
GrpcStorageOptions grpcOptions = GrpcStorageOptions.grpc().build();

assertThat(httpOptions.getUseJwtAccessWithScope()).isFalse();
assertThat(grpcOptions.getUseJwtAccessWithScope()).isFalse();
}

private static class MyStorageRetryStrategy implements StorageRetryStrategy {

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ public abstract class ServiceOptions<
private final TransportOptions transportOptions;
private final HeaderProvider headerProvider;
private final String quotaProjectId;
private final boolean useJwtAccessWithScope;

private transient ServiceRpcFactory<OptionsT> serviceRpcFactory;
private transient ServiceFactory<ServiceT, OptionsT> serviceFactory;
Expand Down Expand Up @@ -140,6 +141,7 @@ public abstract static class Builder<
private HeaderProvider headerProvider;
private String clientLibToken = ServiceOptions.getGoogApiClientLibName();
private String quotaProjectId;
private boolean useJwtAccessWithScope = true;

private ApiTracerFactory apiTracerFactory;

Expand All @@ -159,6 +161,7 @@ protected Builder(ServiceOptions<ServiceT, OptionsT> options) {
transportOptions = options.transportOptions;
clientLibToken = options.clientLibToken;
quotaProjectId = options.quotaProjectId;
useJwtAccessWithScope = options.useJwtAccessWithScope;
apiTracerFactory = options.apiTracerFactory;
}

Expand Down Expand Up @@ -313,6 +316,18 @@ public B setQuotaProjectId(String quotaProjectId) {
return self();
}

/**
* Sets the configuration determining whether self-signed JWT with scopes are used for service
* account credentials.
*
* @param useJwtAccessWithScope whether to use self-signed JWT with scopes
* @return the builder
*/
public B setUseJwtAccessWithScope(final boolean useJwtAccessWithScope) {
this.useJwtAccessWithScope = useJwtAccessWithScope;
return self();
}

/**
* Sets the {@link ApiTracerFactory}. It will be used to create an {@link ApiTracer} that is
* annotated throughout the lifecycle of an RPC operation.
Expand Down Expand Up @@ -365,6 +380,7 @@ protected ServiceOptions(
builder.quotaProjectId != null
? builder.quotaProjectId
: getValueFromCredentialsFile(getCredentialsPath(), "quota_project_id");
useJwtAccessWithScope = builder.useJwtAccessWithScope;
apiTracerFactory = builder.apiTracerFactory;
}

Expand Down Expand Up @@ -650,6 +666,11 @@ public Credentials getScopedCredentials() {
&& ((GoogleCredentials) credentials).createScopedRequired()) {
credentialsToReturn = ((GoogleCredentials) credentials).createScoped(getScopes());
}
if (credentialsToReturn instanceof ServiceAccountCredentials) {
credentialsToReturn =
((ServiceAccountCredentials) credentialsToReturn)
.createWithUseJwtAccessWithScope(getUseJwtAccessWithScope());
}
return credentialsToReturn;
}

Expand Down Expand Up @@ -823,6 +844,15 @@ public String getQuotaProjectId() {
return quotaProjectId;
}

/**
* Returns true when self-signed JWT with scopes are used for service account credentials.
*
* @return true when self-signed JWT with scopes are used
*/
public boolean getUseJwtAccessWithScope() {
return useJwtAccessWithScope;
}

/**
* Returns the resolved host for the Service to connect to Google Cloud
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -630,6 +630,46 @@ void testIsValidUniverseDomain_userUniverseDomainConfig_nonGDUCredentials() thro
assertThat(options.hasValidUniverseDomain()).isTrue();
}

@Test
void testGetScopedCredentials_useJwtAccessWithScope_enablesByDefault() {
TestServiceOptions options =
TestServiceOptions.newBuilder()
.setProjectId("project-id")
.setCredentials(credentials)
.build();
com.google.auth.Credentials scoped = options.getScopedCredentials();
assertThat(scoped).isInstanceOf(ServiceAccountCredentials.class);
assertThat(((ServiceAccountCredentials) scoped).getUseJwtAccessWithScope()).isTrue();
}

@Test
void testGetScopedCredentials_useJwtAccessWithScope_canBeDisabled() {
TestServiceOptions options =
new TestServiceOptions.Builder()
.setProjectId("project-id")
.setCredentials(credentials)
.setUseJwtAccessWithScope(false)
.build();
com.google.auth.Credentials scoped = options.getScopedCredentials();
assertThat(scoped).isInstanceOf(ServiceAccountCredentials.class);
assertThat(((ServiceAccountCredentials) scoped).getUseJwtAccessWithScope()).isFalse();
}

@Test
void testGetScopedCredentials_useJwtAccessWithScope_overridesCredentials() {
ServiceAccountCredentials trueCredentials =
((ServiceAccountCredentials) credentials).createWithUseJwtAccessWithScope(true);
TestServiceOptions options =
new TestServiceOptions.Builder()
.setProjectId("project-id")
.setCredentials(trueCredentials)
.setUseJwtAccessWithScope(false)
.build();
com.google.auth.Credentials scoped = options.getScopedCredentials();
assertThat(scoped).isInstanceOf(ServiceAccountCredentials.class);
assertThat(((ServiceAccountCredentials) scoped).getUseJwtAccessWithScope()).isFalse();
}

private HttpResponse createHttpResponseWithHeader(final Multimap<String, String> headers)
throws Exception {
HttpTransport mockHttpTransport =
Expand Down
Loading