Skip to content

Commit 5c890f8

Browse files
fix(bqjdbc): add Google Driver scope to all credential types (#12847)
b/500748880 --------- Co-authored-by: cloud-java-bot <cloud-java-bot@google.com>
1 parent cc1e996 commit 5c890f8

5 files changed

Lines changed: 90 additions & 148 deletions

File tree

java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryConnection.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ public class BigQueryConnection extends BigQueryNoOpsConnection {
139139
Long listenerPoolSize;
140140
String partnerToken;
141141
DatabaseMetaData databaseMetaData;
142+
Boolean reqGoogleDriveScope;
142143

143144
BigQueryConnection(String url) throws IOException {
144145
this(url, DataSource.fromUrl(url));
@@ -171,9 +172,15 @@ public class BigQueryConnection extends BigQueryNoOpsConnection {
171172
this.overrideProperties.put(
172173
BigQueryJdbcUrlUtility.UNIVERSE_DOMAIN_OVERRIDE_PROPERTY_NAME, this.universeDomain);
173174
}
175+
176+
this.reqGoogleDriveScope =
177+
BigQueryJdbcUrlUtility.convertIntToBoolean(
178+
String.valueOf(ds.getRequestGoogleDriveScope()),
179+
BigQueryJdbcUrlUtility.REQUEST_GOOGLE_DRIVE_SCOPE_PROPERTY_NAME);
180+
174181
this.credentials =
175182
BigQueryJdbcOAuthUtility.getCredentials(
176-
authProperties, overrideProperties, this.connectionClassName);
183+
authProperties, overrideProperties, this.reqGoogleDriveScope, this.connectionClassName);
177184
String defaultDatasetString = ds.getDefaultDataset();
178185
if (defaultDatasetString == null || defaultDatasetString.trim().isEmpty()) {
179186
this.defaultDataset = null;

java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcOAuthUtility.java

Lines changed: 51 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,13 @@ final class BigQueryJdbcOAuthUtility {
8080
+ "Thank you for using JDBC Driver for Google BigQuery!\n"
8181
+ "You may now close the window.</body></html>";
8282

83+
static final String BIGQUERY_SCOPE = "https://www.googleapis.com/auth/bigquery";
84+
static final String DRIVE_READONLY_SCOPE = "https://www.googleapis.com/auth/drive.readonly";
85+
86+
static final List<String> DEFAULT_BIGQUERY_SCOPES = Arrays.asList(BIGQUERY_SCOPE);
87+
static final List<String> BIGQUERY_WITH_DRIVE_SCOPES =
88+
Arrays.asList(BIGQUERY_SCOPE, DRIVE_READONLY_SCOPE);
89+
8390
private static final int USER_AUTH_TIMEOUT_MS = 120000;
8491
private static final BigQueryJdbcCustomLogger LOG =
8592
new BigQueryJdbcCustomLogger(BigQueryJdbcOAuthUtility.class.getName());
@@ -117,6 +124,7 @@ static Map<String, String> parseOAuthProperties(DataSource ds, String callerClas
117124
throw new IllegalArgumentException(OAUTH_TYPE_ERROR_MESSAGE);
118125
}
119126
oauthProperties.put(BigQueryJdbcUrlUtility.OAUTH_TYPE_PROPERTY_NAME, String.valueOf(authType));
127+
120128
switch (authType) {
121129
case GOOGLE_SERVICE_ACCOUNT:
122130
// For using a Google Service Account (OAuth Type 0)
@@ -144,11 +152,6 @@ static Map<String, String> parseOAuthProperties(DataSource ds, String callerClas
144152
BigQueryJdbcUrlUtility.OAUTH_CLIENT_ID_PROPERTY_NAME, ds.getOAuthClientId());
145153
oauthProperties.put(
146154
BigQueryJdbcUrlUtility.OAUTH_CLIENT_SECRET_PROPERTY_NAME, ds.getOAuthClientSecret());
147-
int reqGoogleDriveScope = ds.getRequestGoogleDriveScope();
148-
oauthProperties.put(
149-
BigQueryJdbcUrlUtility.REQUEST_GOOGLE_DRIVE_SCOPE_PROPERTY_NAME,
150-
String.valueOf(reqGoogleDriveScope));
151-
LOG.fine("RequestGoogleDriveScope parsed.");
152155
break;
153156
case PRE_GENERATED_TOKEN:
154157
String refreshToken = ds.getOAuthRefreshToken();
@@ -239,7 +242,7 @@ static Map<String, String> parseOAuthProperties(DataSource ds, String callerClas
239242
BigQueryJdbcUrlUtility.OAUTH_SA_IMPERSONATION_SCOPES_PROPERTY_NAME,
240243
ds.getOAuthSAImpersonationScopes() != null
241244
? ds.getOAuthSAImpersonationScopes()
242-
: BigQueryJdbcUrlUtility.DEFAULT_OAUTH_SA_IMPERSONATION_SCOPES_VALUE);
245+
: BIGQUERY_SCOPE);
243246
oauthProperties.put(
244247
BigQueryJdbcUrlUtility.OAUTH_SA_IMPERSONATION_TOKEN_LIFETIME_PROPERTY_NAME,
245248
ds.getOAuthSAImpersonationTokenLifetime() != null
@@ -258,6 +261,7 @@ static Map<String, String> parseOAuthProperties(DataSource ds, String callerClas
258261
static GoogleCredentials getCredentials(
259262
Map<String, String> authProperties,
260263
Map<String, String> overrideProperties,
264+
Boolean reqGoogleDriveScopeBool,
261265
String callerClassName) {
262266
LOG.finest("++enter++\t" + callerClassName);
263267

@@ -280,15 +284,19 @@ static GoogleCredentials getCredentials(
280284
break;
281285
case APPLICATION_DEFAULT_CREDENTIALS:
282286
// This auth method doesn't support service account impersonation
283-
return getApplicationDefaultCredentials(callerClassName);
287+
288+
credentials = getApplicationDefaultCredentials(callerClassName);
289+
break;
284290
case EXTERNAL_ACCOUNT_AUTH:
285291
// This auth method doesn't support service account impersonation
286-
return getExternalAccountAuthCredentials(authProperties, callerClassName);
292+
credentials = getExternalAccountAuthCredentials(authProperties, callerClassName);
293+
break;
287294
default:
288295
throw new IllegalStateException(OAUTH_TYPE_ERROR_MESSAGE);
289296
}
290297

291-
return getServiceAccountImpersonatedCredentials(credentials, authProperties);
298+
return getServiceAccountImpersonatedCredentials(
299+
credentials, reqGoogleDriveScopeBool, authProperties);
292300
}
293301

294302
private static boolean isFileExists(String filename) {
@@ -388,29 +396,10 @@ static UserAuthorizer getUserAuthorizer(
388396
String callerClassName)
389397
throws URISyntaxException {
390398
LOG.finest("++enter++\t" + callerClassName);
399+
391400
List<String> scopes = new ArrayList<>();
392401
scopes.add("https://www.googleapis.com/auth/bigquery");
393402

394-
// Add Google Drive scope conditionally
395-
if (authProperties.containsKey(
396-
BigQueryJdbcUrlUtility.REQUEST_GOOGLE_DRIVE_SCOPE_PROPERTY_NAME)) {
397-
try {
398-
int driveScopeValue =
399-
Integer.parseInt(
400-
authProperties.get(
401-
BigQueryJdbcUrlUtility.REQUEST_GOOGLE_DRIVE_SCOPE_PROPERTY_NAME));
402-
if (driveScopeValue == 1) {
403-
scopes.add("https://www.googleapis.com/auth/drive.readonly");
404-
LOG.fine("Added Google Drive read-only scope. Caller: " + callerClassName);
405-
}
406-
} catch (NumberFormatException e) {
407-
LOG.severe(
408-
"Invalid value for RequestGoogleDriveScope, defaulting to not request Drive scope."
409-
+ " Caller: "
410-
+ callerClassName);
411-
}
412-
}
413-
414403
List<String> responseTypes = new ArrayList<>();
415404
responseTypes.add("code");
416405

@@ -500,14 +489,18 @@ private static GoogleCredentials getPreGeneratedAccessTokenCredentials(
500489
builder.setUniverseDomain(
501490
overrideProperties.get(BigQueryJdbcUrlUtility.UNIVERSE_DOMAIN_OVERRIDE_PROPERTY_NAME));
502491
}
492+
503493
LOG.info("Connection established. Auth Method: Pre-generated Access Token.");
504-
return builder
505-
.setAccessToken(
506-
AccessToken.newBuilder()
507-
.setTokenValue(
508-
authProperties.get(BigQueryJdbcUrlUtility.OAUTH_ACCESS_TOKEN_PROPERTY_NAME))
509-
.build())
510-
.build();
494+
GoogleCredentials credentials =
495+
builder
496+
.setAccessToken(
497+
AccessToken.newBuilder()
498+
.setTokenValue(
499+
authProperties.get(BigQueryJdbcUrlUtility.OAUTH_ACCESS_TOKEN_PROPERTY_NAME))
500+
.build())
501+
.build();
502+
503+
return credentials;
511504
}
512505

513506
static GoogleCredentials getPreGeneratedTokensCredentials(
@@ -552,6 +545,7 @@ static UserCredentials getPreGeneratedRefreshTokenCredentials(
552545
userCredentialsBuilder.setUniverseDomain(
553546
overrideProperties.get(BigQueryJdbcUrlUtility.UNIVERSE_DOMAIN_OVERRIDE_PROPERTY_NAME));
554547
}
548+
555549
LOG.info("Connection established. Auth Method: Pre-generated Refresh Token.");
556550
return userCredentialsBuilder.build();
557551
}
@@ -571,6 +565,7 @@ private static GoogleCredentials getApplicationDefaultCredentials(String callerC
571565
LOG.info(
572566
"Connection established. Auth Method: Application Default Credentials, Principal: %s.",
573567
principal);
568+
574569
return credentials;
575570
} catch (IOException exception) {
576571
// TODO throw exception
@@ -634,13 +629,19 @@ private static GoogleCredentials getExternalAccountAuthCredentials(
634629
// This function checks if connection string contains configuration for
635630
// credentials impersonation. If not, it returns regular credentials object.
636631
// If impersonated service account is provided, returns Credentials object
637-
// accomodating this information.
632+
// accommodating this information.
638633
private static GoogleCredentials getServiceAccountImpersonatedCredentials(
639-
GoogleCredentials credentials, Map<String, String> authProperties) {
634+
GoogleCredentials credentials,
635+
Boolean reqGoogleDriveScopeBool,
636+
Map<String, String> authProperties) {
640637

641638
String impersonationEmail =
642639
authProperties.get(BigQueryJdbcUrlUtility.OAUTH_SA_IMPERSONATION_EMAIL_PROPERTY_NAME);
643640
if (impersonationEmail == null || impersonationEmail.isEmpty()) {
641+
if (reqGoogleDriveScopeBool) {
642+
credentials = credentials.createScoped(BIGQUERY_WITH_DRIVE_SCOPES);
643+
LOG.fine("Added Google Drive read-only scope to GoogleCredentials.");
644+
}
644645
return credentials;
645646
}
646647

@@ -653,10 +654,18 @@ private static GoogleCredentials getServiceAccountImpersonatedCredentials(
653654

654655
// Scopes has a default value, so it should never be null
655656
List<String> impersonationScopes =
656-
Arrays.asList(
657-
authProperties
658-
.get(BigQueryJdbcUrlUtility.OAUTH_SA_IMPERSONATION_SCOPES_PROPERTY_NAME)
659-
.split(","));
657+
new java.util.ArrayList<>(
658+
Arrays.asList(
659+
authProperties
660+
.get(BigQueryJdbcUrlUtility.OAUTH_SA_IMPERSONATION_SCOPES_PROPERTY_NAME)
661+
.split(",")));
662+
663+
if (reqGoogleDriveScopeBool) {
664+
if (!impersonationScopes.contains(DRIVE_READONLY_SCOPE)) {
665+
impersonationScopes.add(DRIVE_READONLY_SCOPE);
666+
LOG.fine("Added Google Drive read-only scope to impersonation scopes.");
667+
}
668+
}
660669

661670
// Token lifetime has a default value, so it should never be null
662671
String impersonationLifetime =

java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcUrlUtility.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,7 @@ protected boolean removeEldestEntry(Map.Entry<String, Map<String, String>> eldes
7070
static final String HTAPI_ACTIVATION_RATIO_PROPERTY_NAME = "HighThroughputActivationRatio";
7171
static final String KMS_KEY_NAME_PROPERTY_NAME = "KMSKeyName";
7272
static final String QUERY_PROPERTIES_NAME = "QueryProperties";
73-
static final int DEFAULT_HTAPI_ACTIVATION_RATIO_VALUE =
74-
2; // TODO: to adjust this value before private preview based on performance testing.
73+
static final int DEFAULT_HTAPI_ACTIVATION_RATIO_VALUE = 2;
7574
static final String HTAPI_MIN_TABLE_SIZE_PROPERTY_NAME = "HighThroughputMinTableSize";
7675
static final int DEFAULT_HTAPI_MIN_TABLE_SIZE_VALUE = 100;
7776
static final int DEFAULT_OAUTH_TYPE_VALUE = -1;
@@ -86,8 +85,6 @@ protected boolean removeEldestEntry(Map.Entry<String, Map<String, String>> eldes
8685
static final String DEFAULT_OAUTH_SA_IMPERSONATION_CHAIN_VALUE = null;
8786
static final String OAUTH_SA_IMPERSONATION_SCOPES_PROPERTY_NAME =
8887
"ServiceAccountImpersonationScopes";
89-
static final String DEFAULT_OAUTH_SA_IMPERSONATION_SCOPES_VALUE =
90-
"https://www.googleapis.com/auth/bigquery";
9188
static final String OAUTH_SA_IMPERSONATION_TOKEN_LIFETIME_PROPERTY_NAME =
9289
"ServiceAccountImpersonationTokenLifetime";
9390
static final String DEFAULT_OAUTH_SA_IMPERSONATION_TOKEN_LIFETIME_VALUE = "3600";

java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryConnectionTest.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,4 +413,28 @@ public void testBigQueryJobCreationMode_default() throws Exception {
413413
bq.getOptions().getDefaultJobCreationMode(), JobCreationMode.JOB_CREATION_OPTIONAL);
414414
}
415415
}
416+
417+
@Test
418+
public void testWithDriveScopeTrue() throws Exception {
419+
String url = BASE_URL + "RequestGoogleDriveScope=1;";
420+
try (BigQueryConnection connection = new BigQueryConnection(url)) {
421+
assertTrue(connection.reqGoogleDriveScope);
422+
}
423+
}
424+
425+
@Test
426+
public void testWithDriveScopeFalse() throws Exception {
427+
String url = BASE_URL + "RequestGoogleDriveScope=0;";
428+
try (BigQueryConnection connection = new BigQueryConnection(url)) {
429+
assertFalse(connection.reqGoogleDriveScope);
430+
}
431+
}
432+
433+
@Test
434+
public void testWithDriveScopeDefault() throws Exception {
435+
String url = BASE_URL;
436+
try (BigQueryConnection connection = new BigQueryConnection(url)) {
437+
assertFalse(connection.reqGoogleDriveScope);
438+
}
439+
}
416440
}

0 commit comments

Comments
 (0)