-
Notifications
You must be signed in to change notification settings - Fork 1.1k
fix(bqjdbc): add Google Driver scope to all credential types #12847
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
773f1d3
a3db96b
313371b
c1bed22
8b4c91f
4c329e0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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); | ||
|
|
||
| private static final int USER_AUTH_TIMEOUT_MS = 120000; | ||
| private static final BigQueryJdbcCustomLogger LOG = | ||
| new BigQueryJdbcCustomLogger(BigQueryJdbcOAuthUtility.class.getName()); | ||
|
|
@@ -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 = | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I also don't like using magic string & comparing it for |
||
| 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) | ||
|
|
@@ -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(); | ||
|
|
@@ -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 | ||
|
|
@@ -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); | ||
|
|
@@ -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); | ||
|
|
@@ -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"); | ||
|
|
@@ -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(); | ||
| } | ||
|
|
@@ -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( | ||
|
|
@@ -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(); | ||
|
|
@@ -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 | ||
|
|
@@ -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); | ||
| } | ||
|
|
@@ -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) { | ||
|
|
||
|
|
@@ -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 = | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,7 +10,6 @@ | |
| "repo": "googleapis/google-cloud-java", | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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", | ||
|
|
||
There was a problem hiding this comment.
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?