Skip to content
Merged
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);
Comment thread
Neenu1995 marked this conversation as resolved.
Outdated

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 =
Comment thread
Neenu1995 marked this conversation as resolved.
Outdated
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);
Comment thread
keshavdandeva marked this conversation as resolved.
Outdated
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;
Comment thread
Neenu1995 marked this conversation as resolved.
Outdated
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",
Comment thread
Neenu1995 marked this conversation as resolved.
"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