Skip to content
Open
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 @@ -80,6 +80,12 @@ final class BigQueryJdbcOAuthUtility {
+ "Thank you for using JDBC Driver for Google BigQuery!\n"
+ "You may now close the window.</body></html>";

static final String BIGQUERY_SCOPE = "https://www.googleapis.com/auth/bigquery";
static final String DRIVE_READONLY_SCOPE = "https://www.googleapis.com/auth/drive.readonly";

static final List<String> DEFAULT_SCOPES = Arrays.asList(BIGQUERY_SCOPE);
static final List<String> DRIVE_SCOPES = Arrays.asList(BIGQUERY_SCOPE, DRIVE_READONLY_SCOPE);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: BIGQUERY_WITH_DRIVE_SCOPES?


private static final int USER_AUTH_TIMEOUT_MS = 120000;
private static final BigQueryJdbcCustomLogger LOG =
new BigQueryJdbcCustomLogger(BigQueryJdbcOAuthUtility.class.getName());
Expand Down Expand Up @@ -117,6 +123,19 @@ static Map<String, String> parseOAuthProperties(DataSource ds, String callerClas
throw new IllegalArgumentException(OAUTH_TYPE_ERROR_MESSAGE);
}
oauthProperties.put(BigQueryJdbcUrlUtility.OAUTH_TYPE_PROPERTY_NAME, String.valueOf(authType));

Integer reqGoogleDriveScope = ds.getRequestGoogleDriveScope();
if (reqGoogleDriveScope != null) {
Boolean reqGoogleDriveScopeBool =
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I also don't like using magic string & comparing it for bool property.. Should we use retrieve bool at Connection layer from ds & pass bool directly to getCredentials()?

BigQueryJdbcUrlUtility.convertIntToBoolean(
String.valueOf(reqGoogleDriveScope),
BigQueryJdbcUrlUtility.REQUEST_GOOGLE_DRIVE_SCOPE_PROPERTY_NAME);
oauthProperties.put(
BigQueryJdbcUrlUtility.REQUEST_GOOGLE_DRIVE_SCOPE_PROPERTY_NAME,
String.valueOf(reqGoogleDriveScopeBool));
LOG.fine("RequestGoogleDriveScope parsed.");
}

switch (authType) {
case GOOGLE_SERVICE_ACCOUNT:
// For using a Google Service Account (OAuth Type 0)
Expand Down Expand Up @@ -144,11 +163,6 @@ static Map<String, String> parseOAuthProperties(DataSource ds, String callerClas
BigQueryJdbcUrlUtility.OAUTH_CLIENT_ID_PROPERTY_NAME, ds.getOAuthClientId());
oauthProperties.put(
BigQueryJdbcUrlUtility.OAUTH_CLIENT_SECRET_PROPERTY_NAME, ds.getOAuthClientSecret());
int reqGoogleDriveScope = ds.getRequestGoogleDriveScope();
oauthProperties.put(
BigQueryJdbcUrlUtility.REQUEST_GOOGLE_DRIVE_SCOPE_PROPERTY_NAME,
String.valueOf(reqGoogleDriveScope));
LOG.fine("RequestGoogleDriveScope parsed.");
break;
case PRE_GENERATED_TOKEN:
String refreshToken = ds.getOAuthRefreshToken();
Expand Down Expand Up @@ -239,7 +253,7 @@ static Map<String, String> parseOAuthProperties(DataSource ds, String callerClas
BigQueryJdbcUrlUtility.OAUTH_SA_IMPERSONATION_SCOPES_PROPERTY_NAME,
ds.getOAuthSAImpersonationScopes() != null
? ds.getOAuthSAImpersonationScopes()
: BigQueryJdbcUrlUtility.DEFAULT_OAUTH_SA_IMPERSONATION_SCOPES_VALUE);
: BIGQUERY_SCOPE);
oauthProperties.put(
BigQueryJdbcUrlUtility.OAUTH_SA_IMPERSONATION_TOKEN_LIFETIME_PROPERTY_NAME,
ds.getOAuthSAImpersonationTokenLifetime() != null
Expand Down Expand Up @@ -280,7 +294,7 @@ static GoogleCredentials getCredentials(
break;
case APPLICATION_DEFAULT_CREDENTIALS:
// This auth method doesn't support service account impersonation
return getApplicationDefaultCredentials(callerClassName);
return getApplicationDefaultCredentials(authProperties, callerClassName);
case EXTERNAL_ACCOUNT_AUTH:
// This auth method doesn't support service account impersonation
return getExternalAccountAuthCredentials(authProperties, callerClassName);
Expand Down Expand Up @@ -373,6 +387,13 @@ private static GoogleCredentials getGoogleServiceAccountCredentials(
builder.setUniverseDomain(
overrideProperties.get(BigQueryJdbcUrlUtility.UNIVERSE_DOMAIN_OVERRIDE_PROPERTY_NAME));
}
if ("true"
.equals(
authProperties.get(
BigQueryJdbcUrlUtility.REQUEST_GOOGLE_DRIVE_SCOPE_PROPERTY_NAME))) {
builder.setScopes(DRIVE_SCOPES);
LOG.fine("Added Google Drive read-only scope to Service Account builder.");
}
} catch (URISyntaxException | IOException e) {
LOG.severe("Validation failure for Service Account credentials.");
throw new BigQueryJdbcRuntimeException(e);
Expand All @@ -388,28 +409,6 @@ static UserAuthorizer getUserAuthorizer(
String callerClassName)
throws URISyntaxException {
LOG.finest("++enter++\t" + callerClassName);
List<String> scopes = new ArrayList<>();
scopes.add("https://www.googleapis.com/auth/bigquery");

// Add Google Drive scope conditionally
if (authProperties.containsKey(
BigQueryJdbcUrlUtility.REQUEST_GOOGLE_DRIVE_SCOPE_PROPERTY_NAME)) {
try {
int driveScopeValue =
Integer.parseInt(
authProperties.get(
BigQueryJdbcUrlUtility.REQUEST_GOOGLE_DRIVE_SCOPE_PROPERTY_NAME));
if (driveScopeValue == 1) {
scopes.add("https://www.googleapis.com/auth/drive.readonly");
LOG.fine("Added Google Drive read-only scope. Caller: " + callerClassName);
}
} catch (NumberFormatException e) {
LOG.severe(
"Invalid value for RequestGoogleDriveScope, defaulting to not request Drive scope."
+ " Caller: "
+ callerClassName);
}
}

List<String> responseTypes = new ArrayList<>();
responseTypes.add("code");
Expand All @@ -421,13 +420,22 @@ static UserAuthorizer getUserAuthorizer(
UserAuthorizer.Builder userAuthorizerBuilder =
UserAuthorizer.newBuilder()
.setClientId(clientId)
.setScopes(scopes)
.setCallbackUri(URI.create("http://localhost:" + port));

if (overrideProperties.containsKey(BigQueryJdbcUrlUtility.OAUTH2_TOKEN_URI_PROPERTY_NAME)) {
userAuthorizerBuilder.setTokenServerUri(
new URI(overrideProperties.get(BigQueryJdbcUrlUtility.OAUTH2_TOKEN_URI_PROPERTY_NAME)));
}
List<String> scopes = new java.util.ArrayList<>(DEFAULT_SCOPES);

if ("true"
.equals(
authProperties.get(BigQueryJdbcUrlUtility.REQUEST_GOOGLE_DRIVE_SCOPE_PROPERTY_NAME))) {
scopes.add(DRIVE_READONLY_SCOPE);
LOG.fine("Added Google Drive read-only scope to User Account builder.");
}

userAuthorizerBuilder.setScopes(scopes);

return userAuthorizerBuilder.build();
}
Expand Down Expand Up @@ -500,14 +508,24 @@ private static GoogleCredentials getPreGeneratedAccessTokenCredentials(
builder.setUniverseDomain(
overrideProperties.get(BigQueryJdbcUrlUtility.UNIVERSE_DOMAIN_OVERRIDE_PROPERTY_NAME));
}

LOG.info("Connection established. Auth Method: Pre-generated Access Token.");
return builder
.setAccessToken(
AccessToken.newBuilder()
.setTokenValue(
authProperties.get(BigQueryJdbcUrlUtility.OAUTH_ACCESS_TOKEN_PROPERTY_NAME))
.build())
.build();
GoogleCredentials credentials =
builder
.setAccessToken(
AccessToken.newBuilder()
.setTokenValue(
authProperties.get(BigQueryJdbcUrlUtility.OAUTH_ACCESS_TOKEN_PROPERTY_NAME))
.build())
.build();

if ("true"
.equals(
authProperties.get(BigQueryJdbcUrlUtility.REQUEST_GOOGLE_DRIVE_SCOPE_PROPERTY_NAME))) {
credentials = credentials.createScoped(DRIVE_SCOPES);
}

return credentials;
}

static GoogleCredentials getPreGeneratedTokensCredentials(
Expand Down Expand Up @@ -552,11 +570,20 @@ static UserCredentials getPreGeneratedRefreshTokenCredentials(
userCredentialsBuilder.setUniverseDomain(
overrideProperties.get(BigQueryJdbcUrlUtility.UNIVERSE_DOMAIN_OVERRIDE_PROPERTY_NAME));
}

UserCredentials userCredentials = userCredentialsBuilder.build();

if ("true"
.equals(
authProperties.get(BigQueryJdbcUrlUtility.REQUEST_GOOGLE_DRIVE_SCOPE_PROPERTY_NAME))) {
userCredentials = (UserCredentials) userCredentials.createScoped(DRIVE_SCOPES);
}
LOG.info("Connection established. Auth Method: Pre-generated Refresh Token.");
return userCredentialsBuilder.build();
return userCredentials;
}

private static GoogleCredentials getApplicationDefaultCredentials(String callerClassName) {
private static GoogleCredentials getApplicationDefaultCredentials(
Map<String, String> authProperties, String callerClassName) {
LOG.finest("++enter++\t" + callerClassName);
try {
GoogleCredentials credentials = GoogleCredentials.getApplicationDefault();
Expand All @@ -571,6 +598,15 @@ private static GoogleCredentials getApplicationDefaultCredentials(String callerC
LOG.info(
"Connection established. Auth Method: Application Default Credentials, Principal: %s.",
principal);

if ("true"
.equals(
authProperties.get(
BigQueryJdbcUrlUtility.REQUEST_GOOGLE_DRIVE_SCOPE_PROPERTY_NAME))) {
credentials = credentials.createScoped(DRIVE_SCOPES);
LOG.fine("Added Google Drive read-only scope to ADC credentials.");
}

return credentials;
} catch (IOException exception) {
// TODO throw exception
Expand Down Expand Up @@ -616,16 +652,28 @@ private static GoogleCredentials getExternalAccountAuthCredentials(
}
}

GoogleCredentials credentials;
if (credentialsPath != null) {
return ExternalAccountCredentials.fromStream(
Files.newInputStream(Paths.get(credentialsPath)));
credentials =
ExternalAccountCredentials.fromStream(Files.newInputStream(Paths.get(credentialsPath)));
} else if (jsonObject != null) {
return ExternalAccountCredentials.fromStream(
new ByteArrayInputStream(jsonObject.toString().getBytes()));
credentials =
ExternalAccountCredentials.fromStream(
new ByteArrayInputStream(jsonObject.toString().getBytes()));
} else {
throw new IllegalArgumentException(
"Insufficient info provided for external authentication");
}

if ("true"
.equals(
authProperties.get(
BigQueryJdbcUrlUtility.REQUEST_GOOGLE_DRIVE_SCOPE_PROPERTY_NAME))) {
credentials = credentials.createScoped(DRIVE_SCOPES);
LOG.fine("Added Google Drive read-only scope to External Account credentials.");
}

return credentials;
} catch (IOException e) {
throw new BigQueryJdbcRuntimeException(e);
}
Expand All @@ -634,7 +682,7 @@ private static GoogleCredentials getExternalAccountAuthCredentials(
// This function checks if connection string contains configuration for
// credentials impersonation. If not, it returns regular credentials object.
// If impersonated service account is provided, returns Credentials object
// accomodating this information.
// accommodating this information.
private static GoogleCredentials getServiceAccountImpersonatedCredentials(
GoogleCredentials credentials, Map<String, String> authProperties) {

Expand All @@ -653,10 +701,20 @@ private static GoogleCredentials getServiceAccountImpersonatedCredentials(

// Scopes has a default value, so it should never be null
List<String> impersonationScopes =
Arrays.asList(
authProperties
.get(BigQueryJdbcUrlUtility.OAUTH_SA_IMPERSONATION_SCOPES_PROPERTY_NAME)
.split(","));
new java.util.ArrayList<>(
Arrays.asList(
authProperties
.get(BigQueryJdbcUrlUtility.OAUTH_SA_IMPERSONATION_SCOPES_PROPERTY_NAME)
.split(",")));

if ("true"
.equals(
authProperties.get(BigQueryJdbcUrlUtility.REQUEST_GOOGLE_DRIVE_SCOPE_PROPERTY_NAME))) {
if (!impersonationScopes.contains(DRIVE_READONLY_SCOPE)) {
impersonationScopes.add(DRIVE_READONLY_SCOPE);
LOG.fine("Added Google Drive read-only scope to impersonation scopes.");
}
}

// Token lifetime has a default value, so it should never be null
String impersonationLifetime =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,7 @@ protected boolean removeEldestEntry(Map.Entry<String, Map<String, String>> eldes
static final String HTAPI_ACTIVATION_RATIO_PROPERTY_NAME = "HighThroughputActivationRatio";
static final String KMS_KEY_NAME_PROPERTY_NAME = "KMSKeyName";
static final String QUERY_PROPERTIES_NAME = "QueryProperties";
static final int DEFAULT_HTAPI_ACTIVATION_RATIO_VALUE =
2; // TODO: to adjust this value before private preview based on performance testing.
static final int DEFAULT_HTAPI_ACTIVATION_RATIO_VALUE = 2;
static final String HTAPI_MIN_TABLE_SIZE_PROPERTY_NAME = "HighThroughputMinTableSize";
static final int DEFAULT_HTAPI_MIN_TABLE_SIZE_VALUE = 100;
static final int DEFAULT_OAUTH_TYPE_VALUE = -1;
Expand All @@ -86,8 +85,6 @@ protected boolean removeEldestEntry(Map.Entry<String, Map<String, String>> eldes
static final String DEFAULT_OAUTH_SA_IMPERSONATION_CHAIN_VALUE = null;
static final String OAUTH_SA_IMPERSONATION_SCOPES_PROPERTY_NAME =
"ServiceAccountImpersonationScopes";
static final String DEFAULT_OAUTH_SA_IMPERSONATION_SCOPES_VALUE =
"https://www.googleapis.com/auth/bigquery";
static final String OAUTH_SA_IMPERSONATION_TOKEN_LIFETIME_PROPERTY_NAME =
"ServiceAccountImpersonationTokenLifetime";
static final String DEFAULT_OAUTH_SA_IMPERSONATION_TOKEN_LIFETIME_VALUE = "3600";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,7 @@ public void testParseOAuthProperties_UserAccount_RequestDriveScopeEnabled() {
"redactedClientSecret",
properties.get(BigQueryJdbcUrlUtility.OAUTH_CLIENT_SECRET_PROPERTY_NAME));
assertEquals(
"1", properties.get(BigQueryJdbcUrlUtility.REQUEST_GOOGLE_DRIVE_SCOPE_PROPERTY_NAME));
"true", properties.get(BigQueryJdbcUrlUtility.REQUEST_GOOGLE_DRIVE_SCOPE_PROPERTY_NAME));
}

@Test
Expand All @@ -369,7 +369,7 @@ public void testParseOAuthProperties_UserAccount_RequestDriveScopeDisabled() {
BigQueryJdbcOAuthUtility.parseOAuthProperties(
DataSource.fromUrl(url), this.getClass().getName());
assertEquals(
"0", properties.get(BigQueryJdbcUrlUtility.REQUEST_GOOGLE_DRIVE_SCOPE_PROPERTY_NAME));
"false", properties.get(BigQueryJdbcUrlUtility.REQUEST_GOOGLE_DRIVE_SCOPE_PROPERTY_NAME));
}

@Test
Expand All @@ -381,8 +381,7 @@ public void testParseOAuthProperties_UserAccount_RequestDriveScopeDefault() {
BigQueryJdbcOAuthUtility.parseOAuthProperties(
DataSource.fromUrl(url), this.getClass().getName());
assertEquals(
String.valueOf(BigQueryJdbcUrlUtility.DEFAULT_REQUEST_GOOGLE_DRIVE_SCOPE_VALUE),
properties.get(BigQueryJdbcUrlUtility.REQUEST_GOOGLE_DRIVE_SCOPE_PROPERTY_NAME));
"false", properties.get(BigQueryJdbcUrlUtility.REQUEST_GOOGLE_DRIVE_SCOPE_PROPERTY_NAME));
}

@Test
Expand All @@ -391,7 +390,7 @@ public void testGetUserAuthorizer_WithDriveScope() throws URISyntaxException {
authProperties.put(BigQueryJdbcUrlUtility.OAUTH_CLIENT_ID_PROPERTY_NAME, "redactedClientId");
authProperties.put(
BigQueryJdbcUrlUtility.OAUTH_CLIENT_SECRET_PROPERTY_NAME, "redactedClientSecret");
authProperties.put(BigQueryJdbcUrlUtility.REQUEST_GOOGLE_DRIVE_SCOPE_PROPERTY_NAME, "1");
authProperties.put(BigQueryJdbcUrlUtility.REQUEST_GOOGLE_DRIVE_SCOPE_PROPERTY_NAME, "true");

UserAuthorizer authorizer =
BigQueryJdbcOAuthUtility.getUserAuthorizer(
Expand Down Expand Up @@ -432,6 +431,35 @@ public void testGetUserAuthorizer_InvalidDriveScopeValue() throws URISyntaxExcep
assertFalse(authorizer.getScopes().contains("https://www.googleapis.com/auth/drive.readonly"));
}

@Test
public void testParseOAuthProperties_ServiceAccount_RequestDriveScopeEnabled() {
String url =
"jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;"
+ "OAuthType=0;OAuthServiceAcctEmail=dummy@email.com;OAuthPvtKey=key;"
+ "RequestGoogleDriveScope=1;";
Map<String, String> properties =
BigQueryJdbcOAuthUtility.parseOAuthProperties(
DataSource.fromUrl(url), this.getClass().getName());
assertEquals(
"true", properties.get(BigQueryJdbcUrlUtility.REQUEST_GOOGLE_DRIVE_SCOPE_PROPERTY_NAME));
}

@Test
public void testGetCredentialsForPreGeneratedToken_WithDriveScope() {
Map<String, String> authProperties =
BigQueryJdbcOAuthUtility.parseOAuthProperties(
DataSource.fromUrl(
"jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;"
+ "OAuthType=2;ProjectId=MyBigQueryProject;"
+ "OAuthAccessToken=RedactedToken;"
+ "RequestGoogleDriveScope=1;"),
null);

GoogleCredentials credentials =
BigQueryJdbcOAuthUtility.getCredentials(authProperties, Collections.EMPTY_MAP, null);
assertThat(credentials).isNotNull();
}

@Test
public void testParseUserImpersonationDefault() {
String connectionUri =
Expand All @@ -444,7 +472,7 @@ public void testParseUserImpersonationDefault() {
"impersonated",
result.get(BigQueryJdbcUrlUtility.OAUTH_SA_IMPERSONATION_EMAIL_PROPERTY_NAME));
assertEquals(
BigQueryJdbcUrlUtility.DEFAULT_OAUTH_SA_IMPERSONATION_SCOPES_VALUE,
BigQueryJdbcOAuthUtility.BIGQUERY_SCOPE,
result.get(BigQueryJdbcUrlUtility.OAUTH_SA_IMPERSONATION_SCOPES_PROPERTY_NAME));
assertEquals(
BigQueryJdbcUrlUtility.DEFAULT_OAUTH_SA_IMPERSONATION_TOKEN_LIFETIME_VALUE,
Expand Down
1 change: 0 additions & 1 deletion java-iam-policy/.repo-metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
"repo": "googleapis/google-cloud-java",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why editing iam-policy files?

"repo_short": "java-iam-policy",
"distribution_name": "com.google.cloud:google-iam-policy",
"api_id": "iam.googleapis.com",
"library_type": "GAPIC_AUTO",
"requires_billing": true,
"excluded_dependencies": "google-iam-policy",
Expand Down
2 changes: 1 addition & 1 deletion java-iam-policy/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ Java is a registered trademark of Oracle and/or its affiliates.
[code-of-conduct]: https://github.com/googleapis/google-cloud-java/blob/main/CODE_OF_CONDUCT.md#contributor-code-of-conduct
[license]: https://github.com/googleapis/google-cloud-java/blob/main/LICENSE
[enable-billing]: https://cloud.google.com/apis/docs/getting-started#enabling_billing
[enable-api]: https://console.cloud.google.com/flows/enableapi?apiid=iam.googleapis.com

[libraries-bom]: https://github.com/GoogleCloudPlatform/cloud-opensource-java/wiki/The-Google-Cloud-Platform-Libraries-BOM
[shell_img]: https://gstatic.com/cloudssh/images/open-btn.png

Expand Down
Loading