Skip to content
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

feat: allow using existing OAuth token for JDBC connection #37

Merged
merged 6 commits into from Jan 22, 2020
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -17,6 +17,7 @@
package com.google.cloud.spanner.jdbc;

import com.google.auth.Credentials;
import com.google.auth.oauth2.AccessToken;
import com.google.auth.oauth2.GoogleCredentials;
import com.google.auth.oauth2.ServiceAccountCredentials;
import com.google.cloud.NoCredentials;
Expand Down Expand Up @@ -140,6 +141,7 @@ public String[] getValidValues() {
static final boolean DEFAULT_READONLY = false;
static final boolean DEFAULT_RETRY_ABORTS_INTERNALLY = true;
private static final String DEFAULT_CREDENTIALS = null;
private static final String DEFAULT_OAUTH_TOKEN = null;
private static final String DEFAULT_NUM_CHANNELS = null;
private static final String DEFAULT_USER_AGENT = null;

Expand All @@ -156,6 +158,10 @@ public String[] getValidValues() {
public static final String RETRY_ABORTS_INTERNALLY_PROPERTY_NAME = "retryAbortsInternally";
/** Name of the 'credentials' connection property. */
public static final String CREDENTIALS_PROPERTY_NAME = "credentials";
/**
* OAuth token to use for authentication. Cannot be used in combination with a credentials file.
*/
public static final String OAUTH_TOKEN_PROPERTY_NAME = "oauthToken";
/** Name of the 'numChannels' connection property. */
public static final String NUM_CHANNELS_PROPERTY_NAME = "numChannels";
/** Custom user agent string is only for other Google libraries. */
Expand All @@ -173,6 +179,7 @@ public String[] getValidValues() {
ConnectionProperty.createBooleanProperty(
RETRY_ABORTS_INTERNALLY_PROPERTY_NAME, "", DEFAULT_RETRY_ABORTS_INTERNALLY),
ConnectionProperty.createStringProperty(CREDENTIALS_PROPERTY_NAME, ""),
ConnectionProperty.createStringProperty(OAUTH_TOKEN_PROPERTY_NAME, ""),
ConnectionProperty.createStringProperty(NUM_CHANNELS_PROPERTY_NAME, ""),
ConnectionProperty.createBooleanProperty(
USE_PLAIN_TEXT_PROPERTY_NAME, "", DEFAULT_USE_PLAIN_TEXT),
Expand Down Expand Up @@ -223,6 +230,7 @@ public static void closeSpanner() {
public static class Builder {
private String uri;
private String credentialsUrl;
private String oauthToken;
private Credentials credentials;
private List<StatementExecutionInterceptor> statementExecutionInterceptors =
Collections.emptyList();
Expand Down Expand Up @@ -308,6 +316,22 @@ public Builder setCredentialsUrl(String credentialsUrl) {
return this;
}

/**
* Sets the OAuth token to use with this connection. The token must be a valid token with access
* to the resources (project/instance/database) that the connection will be accessing. This
* authentication method cannot be used in combination with a credentials file. If both an OAuth
* token and a credentials file is specified, the {@link #build()} method will throw an
* exception.
*
* @param oauthToken A valid OAuth token for the Google Cloud project that is used by this
* connection.
* @return this builder
*/
public Builder setOAuthToken(String oauthToken) {
this.oauthToken = oauthToken;
return this;
}

@VisibleForTesting
Builder setStatementExecutionInterceptors(List<StatementExecutionInterceptor> interceptors) {
this.statementExecutionInterceptors = interceptors;
Expand Down Expand Up @@ -339,6 +363,7 @@ public static Builder newBuilder() {

private final String uri;
private final String credentialsUrl;
private final String oauthToken;

private final boolean usePlainText;
private final String host;
Expand All @@ -363,6 +388,13 @@ private ConnectionOptions(Builder builder) {
this.uri = builder.uri;
this.credentialsUrl =
builder.credentialsUrl != null ? builder.credentialsUrl : parseCredentials(builder.uri);
this.oauthToken =
builder.oauthToken != null ? builder.oauthToken : parseOAuthToken(builder.uri);
// Check that not both credentials and an OAuth token have been specified.
Preconditions.checkArgument(
(builder.credentials == null && this.credentialsUrl == null) || this.oauthToken == null,
"Cannot specify both credentials and an OAuth token.");

this.usePlainText = parseUsePlainText(this.uri);
this.userAgent = parseUserAgent(this.uri);

Expand All @@ -376,8 +408,13 @@ private ConnectionOptions(Builder builder) {
// Using credentials on a plain text connection is not allowed, so if the user has not specified
// any credentials and is using a plain text connection, we should not try to get the
// credentials from the environment, but default to NoCredentials.
if (builder.credentials == null && this.credentialsUrl == null && this.usePlainText) {
if (builder.credentials == null
&& this.credentialsUrl == null
&& this.oauthToken == null
&& this.usePlainText) {
this.credentials = NoCredentials.getInstance();
} else if (this.oauthToken != null) {
this.credentials = new GoogleCredentials(new AccessToken(oauthToken, null));
} else {
this.credentials =
builder.credentials == null
Expand Down Expand Up @@ -446,6 +483,12 @@ static String parseCredentials(String uri) {
return value != null ? value : DEFAULT_CREDENTIALS;
}

@VisibleForTesting
static String parseOAuthToken(String uri) {
String value = parseUriProperty(uri, OAUTH_TOKEN_PROPERTY_NAME);
return value != null ? value : DEFAULT_OAUTH_TOKEN;
}

@VisibleForTesting
static String parseNumChannels(String uri) {
String value = parseUriProperty(uri, NUM_CHANNELS_PROPERTY_NAME);
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/com/google/cloud/spanner/jdbc/JdbcDriver.java
Expand Up @@ -74,6 +74,12 @@
* <li>credentials (String): URL for the credentials file to use for the connection. If you do not
* specify any credentials at all, the default credentials of the environment as returned by
* {@link GoogleCredentials#getApplicationDefault()} will be used.
* <li>oauthtoken (String): A valid OAuth2 token to use for the JDBC connection. The token must
* have been obtained with one or both of the scopes
* 'https://www.googleapis.com/auth/spanner.admin' and/or
* 'https://www.googleapis.com/auth/spanner.data'. If you specify both a credentials file and
* an OAuth token, the JDBC driver will throw an exception when you try to obtain a
* connection.
* <li>autocommit (boolean): Sets the initial autocommit mode for the connection. Default is true.
* <li>readonly (boolean): Sets the initial readonly mode for the connection. Default is false.
* <li>retryAbortsInternally (boolean): Sets the initial retryAbortsInternally mode for the
Expand Down