From 72c6891c7f0ae836aae473b71f40a40867652b0e Mon Sep 17 00:00:00 2001 From: Olav Loite Date: Tue, 21 Jan 2020 08:53:32 +0100 Subject: [PATCH 1/5] feat: allow using existing OAuth token for JDBC connection Allow the user to specify an existing OAuth token to use for a JDBC connection, instead of requiring the user to specify a credentials file or using the default credentials of the environment. Fixes #29 --- .../spanner/jdbc/AbstractJdbcConnection.java | 6 + .../jdbc/CloudSpannerJdbcConnection.java | 3 + .../google/cloud/spanner/jdbc/Connection.java | 4 + .../cloud/spanner/jdbc/ConnectionImpl.java | 7 + .../cloud/spanner/jdbc/ConnectionOptions.java | 45 ++- .../spanner/jdbc/ConnectionOptionsTest.java | 259 +++++++++--------- .../spanner/jdbc/it/ITJdbcConnectTest.java | 54 +++- 7 files changed, 237 insertions(+), 141 deletions(-) diff --git a/src/main/java/com/google/cloud/spanner/jdbc/AbstractJdbcConnection.java b/src/main/java/com/google/cloud/spanner/jdbc/AbstractJdbcConnection.java index 71a1c20a..76e979f0 100644 --- a/src/main/java/com/google/cloud/spanner/jdbc/AbstractJdbcConnection.java +++ b/src/main/java/com/google/cloud/spanner/jdbc/AbstractJdbcConnection.java @@ -16,6 +16,7 @@ package com.google.cloud.spanner.jdbc; +import com.google.cloud.spanner.SpannerOptions; import com.google.common.annotations.VisibleForTesting; import com.google.rpc.Code; import java.sql.CallableStatement; @@ -73,6 +74,11 @@ ConnectionOptions getConnectionOptions() { return options; } + @Override + public SpannerOptions getSpannerOptions() { + return spanner.getSpannerOptions(); + } + @Override public CallableStatement prepareCall(String sql) throws SQLException { return checkClosedAndThrowUnsupported(CALLABLE_STATEMENTS_UNSUPPORTED); diff --git a/src/main/java/com/google/cloud/spanner/jdbc/CloudSpannerJdbcConnection.java b/src/main/java/com/google/cloud/spanner/jdbc/CloudSpannerJdbcConnection.java index d4c6629b..ab8a78b1 100644 --- a/src/main/java/com/google/cloud/spanner/jdbc/CloudSpannerJdbcConnection.java +++ b/src/main/java/com/google/cloud/spanner/jdbc/CloudSpannerJdbcConnection.java @@ -19,6 +19,7 @@ import com.google.cloud.spanner.AbortedException; import com.google.cloud.spanner.Mutation; import com.google.cloud.spanner.ResultSet; +import com.google.cloud.spanner.SpannerOptions; import java.sql.Connection; import java.sql.SQLException; import java.sql.Timestamp; @@ -150,6 +151,8 @@ public interface CloudSpannerJdbcConnection extends Connection { */ String getConnectionUrl(); + SpannerOptions getSpannerOptions(); + /** * @see * com.google.cloud.spanner.jdbc.Connection#addTransactionRetryListener(TransactionRetryListener) diff --git a/src/main/java/com/google/cloud/spanner/jdbc/Connection.java b/src/main/java/com/google/cloud/spanner/jdbc/Connection.java index 0ae8ed5c..0286580e 100644 --- a/src/main/java/com/google/cloud/spanner/jdbc/Connection.java +++ b/src/main/java/com/google/cloud/spanner/jdbc/Connection.java @@ -25,6 +25,7 @@ import com.google.cloud.spanner.ResultSet; import com.google.cloud.spanner.SpannerBatchUpdateException; import com.google.cloud.spanner.SpannerException; +import com.google.cloud.spanner.SpannerOptions; import com.google.cloud.spanner.Statement; import com.google.cloud.spanner.TimestampBound; import com.google.cloud.spanner.jdbc.StatementResult.ResultType; @@ -121,6 +122,9 @@ */ interface Connection extends AutoCloseable { + /** @return the {@link SpannerOptions} that were used to create this connection. */ + SpannerOptions getSpannerOptions(); + /** Closes this connection. This is a no-op if the {@link Connection} has alread been closed. */ @Override void close(); diff --git a/src/main/java/com/google/cloud/spanner/jdbc/ConnectionImpl.java b/src/main/java/com/google/cloud/spanner/jdbc/ConnectionImpl.java index 8490dfcd..df5b243e 100644 --- a/src/main/java/com/google/cloud/spanner/jdbc/ConnectionImpl.java +++ b/src/main/java/com/google/cloud/spanner/jdbc/ConnectionImpl.java @@ -26,6 +26,7 @@ import com.google.cloud.spanner.Spanner; import com.google.cloud.spanner.SpannerException; import com.google.cloud.spanner.SpannerExceptionFactory; +import com.google.cloud.spanner.SpannerOptions; import com.google.cloud.spanner.Statement; import com.google.cloud.spanner.TimestampBound; import com.google.cloud.spanner.TimestampBound.Mode; @@ -231,6 +232,12 @@ static UnitOfWorkType of(TransactionMode transactionMode) { setDefaultTransactionOptions(); } + /** @return the {@link SpannerOptions} that were used to create this connection. */ + @Override + public SpannerOptions getSpannerOptions() { + return spanner.getOptions(); + } + private DdlClient createDdlClient() { return DdlClient.newBuilder() .setDatabaseAdminClient(spanner.getDatabaseAdminClient()) diff --git a/src/main/java/com/google/cloud/spanner/jdbc/ConnectionOptions.java b/src/main/java/com/google/cloud/spanner/jdbc/ConnectionOptions.java index 3b03aca5..db41659c 100644 --- a/src/main/java/com/google/cloud/spanner/jdbc/ConnectionOptions.java +++ b/src/main/java/com/google/cloud/spanner/jdbc/ConnectionOptions.java @@ -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; @@ -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; @@ -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. */ @@ -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), @@ -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 statementExecutionInterceptors = Collections.emptyList(); @@ -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 interceptors) { this.statementExecutionInterceptors = interceptors; @@ -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; @@ -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); @@ -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 @@ -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); diff --git a/src/test/java/com/google/cloud/spanner/jdbc/ConnectionOptionsTest.java b/src/test/java/com/google/cloud/spanner/jdbc/ConnectionOptionsTest.java index 1435cd1d..b45c9572 100644 --- a/src/test/java/com/google/cloud/spanner/jdbc/ConnectionOptionsTest.java +++ b/src/test/java/com/google/cloud/spanner/jdbc/ConnectionOptionsTest.java @@ -16,10 +16,8 @@ package com.google.cloud.spanner.jdbc; -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.nullValue; -import static org.junit.Assert.assertThat; +import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.fail; import com.google.auth.oauth2.GoogleCredentials; import com.google.auth.oauth2.ServiceAccountCredentials; @@ -42,15 +40,14 @@ public void testBuildWithValidURIAndCredentialsFileURL() { "cloudspanner:/projects/test-project-123/instances/test-instance-123/databases/test-database-123"); builder.setCredentialsUrl(FILE_TEST_PATH); ConnectionOptions options = builder.build(); - assertThat(options.getHost(), is(equalTo(DEFAULT_HOST))); - assertThat(options.getProjectId(), is(equalTo("test-project-123"))); - assertThat(options.getInstanceId(), is(equalTo("test-instance-123"))); - assertThat(options.getDatabaseName(), is(equalTo("test-database-123"))); - assertThat( - (GoogleCredentials) options.getCredentials(), - is(equalTo(new CredentialsService().createCredentials(FILE_TEST_PATH)))); - assertThat(options.isAutocommit(), is(equalTo(ConnectionOptions.DEFAULT_AUTOCOMMIT))); - assertThat(options.isReadOnly(), is(equalTo(ConnectionOptions.DEFAULT_READONLY))); + assertThat(options.getHost()).isEqualTo(DEFAULT_HOST); + assertThat(options.getProjectId()).isEqualTo("test-project-123"); + assertThat(options.getInstanceId()).isEqualTo("test-instance-123"); + assertThat(options.getDatabaseName()).isEqualTo("test-database-123"); + assertThat(options.getCredentials()) + .isEqualTo(new CredentialsService().createCredentials(FILE_TEST_PATH)); + assertThat(options.isAutocommit()).isEqualTo(ConnectionOptions.DEFAULT_AUTOCOMMIT); + assertThat(options.isReadOnly()).isEqualTo(ConnectionOptions.DEFAULT_READONLY); } @Test @@ -60,15 +57,14 @@ public void testBuildWithValidURIAndProperties() { "cloudspanner:/projects/test-project-123/instances/test-instance-123/databases/test-database-123?autocommit=false;readonly=true"); builder.setCredentialsUrl(FILE_TEST_PATH); ConnectionOptions options = builder.build(); - assertThat(options.getHost(), is(equalTo(DEFAULT_HOST))); - assertThat(options.getProjectId(), is(equalTo("test-project-123"))); - assertThat(options.getInstanceId(), is(equalTo("test-instance-123"))); - assertThat(options.getDatabaseName(), is(equalTo("test-database-123"))); - assertThat( - (GoogleCredentials) options.getCredentials(), - is(equalTo(new CredentialsService().createCredentials(FILE_TEST_PATH)))); - assertThat(options.isAutocommit(), is(equalTo(false))); - assertThat(options.isReadOnly(), is(equalTo(true))); + assertThat(options.getHost()).isEqualTo(DEFAULT_HOST); + assertThat(options.getProjectId()).isEqualTo("test-project-123"); + assertThat(options.getInstanceId()).isEqualTo("test-instance-123"); + assertThat(options.getDatabaseName()).isEqualTo("test-database-123"); + assertThat(options.getCredentials()) + .isEqualTo(new CredentialsService().createCredentials(FILE_TEST_PATH)); + assertThat(options.isAutocommit()).isEqualTo(false); + assertThat(options.isReadOnly()).isEqualTo(true); } @Test @@ -78,15 +74,14 @@ public void testBuildWithHostAndValidURI() { "cloudspanner://test-spanner.googleapis.com/projects/test-project-123/instances/test-instance-123/databases/test-database-123"); builder.setCredentialsUrl(FILE_TEST_PATH); ConnectionOptions options = builder.build(); - assertThat(options.getHost(), is(equalTo("https://test-spanner.googleapis.com"))); - assertThat(options.getProjectId(), is(equalTo("test-project-123"))); - assertThat(options.getInstanceId(), is(equalTo("test-instance-123"))); - assertThat(options.getDatabaseName(), is(equalTo("test-database-123"))); - assertThat( - (GoogleCredentials) options.getCredentials(), - is(equalTo(new CredentialsService().createCredentials(FILE_TEST_PATH)))); - assertThat(options.isAutocommit(), is(equalTo(ConnectionOptions.DEFAULT_AUTOCOMMIT))); - assertThat(options.isReadOnly(), is(equalTo(ConnectionOptions.DEFAULT_READONLY))); + assertThat(options.getHost()).isEqualTo("https://test-spanner.googleapis.com"); + assertThat(options.getProjectId()).isEqualTo("test-project-123"); + assertThat(options.getInstanceId()).isEqualTo("test-instance-123"); + assertThat(options.getDatabaseName()).isEqualTo("test-database-123"); + assertThat(options.getCredentials()) + .isEqualTo(new CredentialsService().createCredentials(FILE_TEST_PATH)); + assertThat(options.isAutocommit()).isEqualTo(ConnectionOptions.DEFAULT_AUTOCOMMIT); + assertThat(options.isReadOnly()).isEqualTo(ConnectionOptions.DEFAULT_READONLY); } @Test @@ -96,15 +91,14 @@ public void testBuildWithLocalhostPortAndValidURI() { "cloudspanner://localhost:8443/projects/test-project-123/instances/test-instance-123/databases/test-database-123"); builder.setCredentialsUrl(FILE_TEST_PATH); ConnectionOptions options = builder.build(); - assertThat(options.getHost(), is(equalTo("https://localhost:8443"))); - assertThat(options.getProjectId(), is(equalTo("test-project-123"))); - assertThat(options.getInstanceId(), is(equalTo("test-instance-123"))); - assertThat(options.getDatabaseName(), is(equalTo("test-database-123"))); - assertThat( - (GoogleCredentials) options.getCredentials(), - is(equalTo(new CredentialsService().createCredentials(FILE_TEST_PATH)))); - assertThat(options.isAutocommit(), is(equalTo(ConnectionOptions.DEFAULT_AUTOCOMMIT))); - assertThat(options.isReadOnly(), is(equalTo(ConnectionOptions.DEFAULT_READONLY))); + assertThat(options.getHost()).isEqualTo("https://localhost:8443"); + assertThat(options.getProjectId()).isEqualTo("test-project-123"); + assertThat(options.getInstanceId()).isEqualTo("test-instance-123"); + assertThat(options.getDatabaseName()).isEqualTo("test-database-123"); + assertThat(options.getCredentials()) + .isEqualTo(new CredentialsService().createCredentials(FILE_TEST_PATH)); + assertThat(options.isAutocommit()).isEqualTo(ConnectionOptions.DEFAULT_AUTOCOMMIT); + assertThat(options.isReadOnly()).isEqualTo(ConnectionOptions.DEFAULT_READONLY); } @Test @@ -114,21 +108,20 @@ public void testBuildWithDefaultProjectPlaceholder() { "cloudspanner:/projects/default_project_id/instances/test-instance-123/databases/test-database-123"); builder.setCredentialsUrl(FILE_TEST_PATH); ConnectionOptions options = builder.build(); - assertThat(options.getHost(), is(equalTo(DEFAULT_HOST))); + assertThat(options.getHost()).isEqualTo(DEFAULT_HOST); String projectId = SpannerOptions.getDefaultProjectId(); if (projectId == null) { projectId = ((ServiceAccountCredentials) new CredentialsService().createCredentials(FILE_TEST_PATH)) .getProjectId(); } - assertThat(options.getProjectId(), is(equalTo(projectId))); - assertThat(options.getInstanceId(), is(equalTo("test-instance-123"))); - assertThat(options.getDatabaseName(), is(equalTo("test-database-123"))); - assertThat( - (GoogleCredentials) options.getCredentials(), - is(equalTo(new CredentialsService().createCredentials(FILE_TEST_PATH)))); - assertThat(options.isAutocommit(), is(equalTo(ConnectionOptions.DEFAULT_AUTOCOMMIT))); - assertThat(options.isReadOnly(), is(equalTo(ConnectionOptions.DEFAULT_READONLY))); + assertThat(options.getProjectId()).isEqualTo(projectId); + assertThat(options.getInstanceId()).isEqualTo("test-instance-123"); + assertThat(options.getDatabaseName()).isEqualTo("test-database-123"); + assertThat(options.getCredentials()) + .isEqualTo(new CredentialsService().createCredentials(FILE_TEST_PATH)); + assertThat(options.isAutocommit()).isEqualTo(ConnectionOptions.DEFAULT_AUTOCOMMIT); + assertThat(options.isReadOnly()).isEqualTo(ConnectionOptions.DEFAULT_READONLY); } @Test @@ -203,24 +196,21 @@ public void testBuilderSetUri() { } private void setInvalidUri(ConnectionOptions.Builder builder, String uri) { - boolean invalid = false; try { builder.setUri(uri); + fail(uri + " should be considered an invalid uri"); } catch (IllegalArgumentException e) { - invalid = true; } - assertThat(uri + " should be considered an invalid uri", invalid, is(true)); } private void setInvalidProperty( ConnectionOptions.Builder builder, String uri, String expectedInvalidProperties) { - boolean invalid = false; try { builder.setUri(uri); + fail("missing expected exception"); } catch (IllegalArgumentException e) { - invalid = e.getMessage().contains(expectedInvalidProperties); + assertThat(e.getMessage()).contains(expectedInvalidProperties); } - assertThat(uri + " should contain invalid properties", invalid, is(true)); } @Test @@ -228,86 +218,72 @@ public void testParseUriProperty() { final String baseUri = "cloudspanner:/projects/test-project-123/instances/test-instance/databases/test-database"; - assertThat(ConnectionOptions.parseUriProperty(baseUri, "autocommit"), is(nullValue())); - assertThat( - ConnectionOptions.parseUriProperty(baseUri + "?autocommit=true", "autocommit"), - is(equalTo("true"))); - assertThat( - ConnectionOptions.parseUriProperty(baseUri + "?autocommit=false", "autocommit"), - is(equalTo("false"))); + assertThat(ConnectionOptions.parseUriProperty(baseUri, "autocommit")).isNull(); + assertThat(ConnectionOptions.parseUriProperty(baseUri + "?autocommit=true", "autocommit")) + .isEqualTo("true"); + assertThat(ConnectionOptions.parseUriProperty(baseUri + "?autocommit=false", "autocommit")) + .isEqualTo("false"); + assertThat(ConnectionOptions.parseUriProperty(baseUri + "?autocommit=true;", "autocommit")) + .isEqualTo("true"); + assertThat(ConnectionOptions.parseUriProperty(baseUri + "?autocommit=false;", "autocommit")) + .isEqualTo("false"); assertThat( - ConnectionOptions.parseUriProperty(baseUri + "?autocommit=true;", "autocommit"), - is(equalTo("true"))); + ConnectionOptions.parseUriProperty( + baseUri + "?autocommit=true;readOnly=false", "autocommit")) + .isEqualTo("true"); assertThat( - ConnectionOptions.parseUriProperty(baseUri + "?autocommit=false;", "autocommit"), - is(equalTo("false"))); + ConnectionOptions.parseUriProperty( + baseUri + "?autocommit=false;readOnly=false", "autocommit")) + .isEqualTo("false"); assertThat( - ConnectionOptions.parseUriProperty( - baseUri + "?autocommit=true;readOnly=false", "autocommit"), - is(equalTo("true"))); + ConnectionOptions.parseUriProperty( + baseUri + "?readOnly=false;autocommit=true", "autocommit")) + .isEqualTo("true"); assertThat( - ConnectionOptions.parseUriProperty( - baseUri + "?autocommit=false;readOnly=false", "autocommit"), - is(equalTo("false"))); + ConnectionOptions.parseUriProperty( + baseUri + "?readOnly=false;autocommit=false", "autocommit")) + .isEqualTo("false"); assertThat( - ConnectionOptions.parseUriProperty( - baseUri + "?readOnly=false;autocommit=true", "autocommit"), - is(equalTo("true"))); + ConnectionOptions.parseUriProperty( + baseUri + "?readOnly=false;autocommit=true;foo=bar", "autocommit")) + .isEqualTo("true"); assertThat( - ConnectionOptions.parseUriProperty( - baseUri + "?readOnly=false;autocommit=false", "autocommit"), - is(equalTo("false"))); - assertThat( - ConnectionOptions.parseUriProperty( - baseUri + "?readOnly=false;autocommit=true;foo=bar", "autocommit"), - is(equalTo("true"))); - assertThat( - ConnectionOptions.parseUriProperty( - baseUri + "?readOnly=false;autocommit=false;foo=bar", "autocommit"), - is(equalTo("false"))); + ConnectionOptions.parseUriProperty( + baseUri + "?readOnly=false;autocommit=false;foo=bar", "autocommit")) + .isEqualTo("false"); // case insensitive - assertThat( - ConnectionOptions.parseUriProperty(baseUri + "?AutoCommit=true", "autocommit"), - is(equalTo("true"))); - assertThat( - ConnectionOptions.parseUriProperty(baseUri + "?AutoCommit=false", "autocommit"), - is(equalTo("false"))); + assertThat(ConnectionOptions.parseUriProperty(baseUri + "?AutoCommit=true", "autocommit")) + .isEqualTo("true"); + assertThat(ConnectionOptions.parseUriProperty(baseUri + "?AutoCommit=false", "autocommit")) + .isEqualTo("false"); // ; instead of ? before the properties is ok - assertThat( - ConnectionOptions.parseUriProperty(baseUri + ";autocommit=true", "autocommit"), - is(equalTo("true"))); + assertThat(ConnectionOptions.parseUriProperty(baseUri + ";autocommit=true", "autocommit")) + .isEqualTo("true"); // forgot the ? or ; before the properties - assertThat( - ConnectionOptions.parseUriProperty(baseUri + "autocommit=true", "autocommit"), - is(nullValue())); + assertThat(ConnectionOptions.parseUriProperty(baseUri + "autocommit=true", "autocommit")) + .isNull(); // substring is not ok - assertThat( - ConnectionOptions.parseUriProperty(baseUri + "?isautocommit=true", "autocommit"), - is(nullValue())); + assertThat(ConnectionOptions.parseUriProperty(baseUri + "?isautocommit=true", "autocommit")) + .isNull(); } @Test public void testParseProperties() { final String baseUri = "cloudspanner:/projects/test-project-123/instances/test-instance/databases/test-database"; - assertThat( - ConnectionOptions.parseProperties(baseUri + "?autocommit=true"), - is(equalTo(Arrays.asList("autocommit")))); - assertThat( - ConnectionOptions.parseProperties(baseUri + "?autocommit=true;readonly=false"), - is(equalTo(Arrays.asList("autocommit", "readonly")))); - assertThat( - ConnectionOptions.parseProperties(baseUri + "?autocommit=true;READONLY=false"), - is(equalTo(Arrays.asList("autocommit", "READONLY")))); - assertThat( - ConnectionOptions.parseProperties(baseUri + ";autocommit=true;readonly=false"), - is(equalTo(Arrays.asList("autocommit", "readonly")))); - assertThat( - ConnectionOptions.parseProperties(baseUri + ";autocommit=true;readonly=false;"), - is(equalTo(Arrays.asList("autocommit", "readonly")))); + assertThat(ConnectionOptions.parseProperties(baseUri + "?autocommit=true")) + .isEqualTo(Arrays.asList("autocommit")); + assertThat(ConnectionOptions.parseProperties(baseUri + "?autocommit=true;readonly=false")) + .isEqualTo(Arrays.asList("autocommit", "readonly")); + assertThat(ConnectionOptions.parseProperties(baseUri + "?autocommit=true;READONLY=false")) + .isEqualTo(Arrays.asList("autocommit", "READONLY")); + assertThat(ConnectionOptions.parseProperties(baseUri + ";autocommit=true;readonly=false")) + .isEqualTo(Arrays.asList("autocommit", "readonly")); + assertThat(ConnectionOptions.parseProperties(baseUri + ";autocommit=true;readonly=false;")) + .isEqualTo(Arrays.asList("autocommit", "readonly")); } @Test @@ -315,20 +291,53 @@ public void testParsePropertiesSpecifiedMultipleTimes() { final String baseUri = "cloudspanner:/projects/test-project-123/instances/test-instance/databases/test-database"; assertThat( - ConnectionOptions.parseUriProperty( - baseUri + "?autocommit=true;autocommit=false", "autocommit"), - is(equalTo("true"))); + ConnectionOptions.parseUriProperty( + baseUri + "?autocommit=true;autocommit=false", "autocommit")) + .isEqualTo("true"); assertThat( - ConnectionOptions.parseUriProperty( - baseUri + "?autocommit=false;autocommit=true", "autocommit"), - is(equalTo("false"))); + ConnectionOptions.parseUriProperty( + baseUri + "?autocommit=false;autocommit=true", "autocommit")) + .isEqualTo("false"); assertThat( - ConnectionOptions.parseUriProperty( - baseUri + ";autocommit=false;readonly=false;autocommit=true", "autocommit"), - is(equalTo("false"))); + ConnectionOptions.parseUriProperty( + baseUri + ";autocommit=false;readonly=false;autocommit=true", "autocommit")) + .isEqualTo("false"); ConnectionOptions.newBuilder() .setUri( "cloudspanner:/projects/test-project-123/instances/test-instance/databases/test-database" + ";autocommit=false;readonly=false;autocommit=true"); } + + @Test + public void testParseOAuthToken() { + assertThat( + ConnectionOptions.parseUriProperty( + "cloudspanner:/projects/test-project-123/instances/test-instance/databases/test-database" + + "?oauthtoken=RsT5OjbzRn430zqMLgV3Ia", + "OAuthToken")) + .isEqualTo("RsT5OjbzRn430zqMLgV3Ia"); + // Try to use both credentials and an OAuth token. That should fail. + ConnectionOptions.Builder builder = + ConnectionOptions.newBuilder() + .setUri( + "cloudspanner:/projects/test-project-123/instances/test-instance/databases/test-database" + + "?OAuthToken=RsT5OjbzRn430zqMLgV3Ia;credentials=/path/to/credentials.json"); + try { + builder.build(); + fail("missing expected exception"); + } catch (IllegalArgumentException e) { + assertThat(e.getMessage()).contains("Cannot specify both credentials and an OAuth token"); + } + + // Now try to use only an OAuth token. + builder = + ConnectionOptions.newBuilder() + .setUri( + "cloudspanner:/projects/test-project-123/instances/test-instance/databases/test-database" + + "?OAuthToken=RsT5OjbzRn430zqMLgV3Ia"); + ConnectionOptions options = builder.build(); + assertThat(options.getCredentials()).isInstanceOf(GoogleCredentials.class); + GoogleCredentials credentials = (GoogleCredentials) options.getCredentials(); + assertThat(credentials.getAccessToken().getTokenValue()).isEqualTo("RsT5OjbzRn430zqMLgV3Ia"); + } } diff --git a/src/test/java/com/google/cloud/spanner/jdbc/it/ITJdbcConnectTest.java b/src/test/java/com/google/cloud/spanner/jdbc/it/ITJdbcConnectTest.java index cef3148e..5a0785ae 100644 --- a/src/test/java/com/google/cloud/spanner/jdbc/it/ITJdbcConnectTest.java +++ b/src/test/java/com/google/cloud/spanner/jdbc/it/ITJdbcConnectTest.java @@ -16,10 +16,9 @@ package com.google.cloud.spanner.jdbc.it; -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; +import static com.google.common.truth.Truth.assertThat; +import com.google.auth.oauth2.GoogleCredentials; import com.google.cloud.spanner.IntegrationTest; import com.google.cloud.spanner.jdbc.CloudSpannerJdbcConnection; import com.google.cloud.spanner.jdbc.ITAbstractJdbcTest; @@ -57,30 +56,30 @@ private String createBaseUrl() { } private void testDefaultConnection(Connection connection) throws SQLException { - assertThat(connection.isWrapperFor(CloudSpannerJdbcConnection.class), is(true)); + assertThat(connection.isWrapperFor(CloudSpannerJdbcConnection.class)).isTrue(); CloudSpannerJdbcConnection cs = connection.unwrap(CloudSpannerJdbcConnection.class); - assertThat(cs.getAutoCommit(), is(true)); - assertThat(cs.isReadOnly(), is(false)); + assertThat(cs.getAutoCommit()).isTrue(); + assertThat(cs.isReadOnly()).isFalse(); try (ResultSet rs = connection.createStatement().executeQuery("SELECT 1")) { - assertThat(rs.next(), is(true)); - assertThat(rs.getInt(1), is(equalTo(1))); + assertThat(rs.next()).isTrue(); + assertThat(rs.getInt(1)).isEqualTo(1); } cs.setAutoCommit(false); - assertThat(cs.isRetryAbortsInternally(), is(true)); + assertThat(cs.isRetryAbortsInternally()).isTrue(); } private void testNonDefaultConnection(Connection connection) throws SQLException { - assertThat(connection.isWrapperFor(CloudSpannerJdbcConnection.class), is(true)); + assertThat(connection.isWrapperFor(CloudSpannerJdbcConnection.class)).isTrue(); CloudSpannerJdbcConnection cs = connection.unwrap(CloudSpannerJdbcConnection.class); - assertThat(cs.getAutoCommit(), is(false)); - assertThat(cs.isReadOnly(), is(true)); + assertThat(cs.getAutoCommit()).isFalse(); + assertThat(cs.isReadOnly()).isTrue(); try (ResultSet rs = connection.createStatement().executeQuery("SELECT 1")) { - assertThat(rs.next(), is(true)); - assertThat(rs.getInt(1), is(equalTo(1))); + assertThat(rs.next()).isTrue(); + assertThat(rs.getInt(1)).isEqualTo(1); } connection.commit(); cs.setReadOnly(false); - assertThat(cs.isRetryAbortsInternally(), is(false)); + assertThat(cs.isRetryAbortsInternally()).isFalse(); } @Test @@ -194,4 +193,29 @@ public void testConnectWithDataSourceWithConflictingValues() throws SQLException testNonDefaultConnection(connection); } } + + @Test + public void testConnectWithOAuthToken() throws SQLException { + String url = createBaseUrl(); + try (Connection connection = DriverManager.getConnection(url)) { + CloudSpannerJdbcConnection cc = connection.unwrap(CloudSpannerJdbcConnection.class); + if (cc.getSpannerOptions().getCredentials() instanceof GoogleCredentials) { + GoogleCredentials credentials = (GoogleCredentials) cc.getSpannerOptions().getCredentials(); + // Get the OAuth token from this connection and open a new connection using that token. + String token = credentials.getAccessToken().getTokenValue(); + String urlWithOAuth = createBaseUrl() + "?OAuthToken=" + token; + try (Connection connectionWithOAuth = DriverManager.getConnection(urlWithOAuth)) { + // Check that the two connections use two different SpannerOptions instances. + assertThat( + connectionWithOAuth.unwrap(CloudSpannerJdbcConnection.class).getSpannerOptions()) + .isNotEqualTo(cc.getSpannerOptions()); + // Try to do a query using the connection created with an OAuth token. + testDefaultConnection(connectionWithOAuth); + } + } else { + throw new IllegalArgumentException( + "Unknown credentials type used: " + cc.getSpannerOptions().getCredentials()); + } + } + } } From 04b70374255188b442372b78209504dbb3b9846d Mon Sep 17 00:00:00 2001 From: Olav Loite Date: Tue, 21 Jan 2020 20:00:56 +0100 Subject: [PATCH 2/5] tests: fix test cases --- .../spanner/jdbc/AbstractJdbcConnection.java | 6 ---- .../jdbc/CloudSpannerJdbcConnection.java | 3 -- .../google/cloud/spanner/jdbc/JdbcDriver.java | 6 ++++ .../spanner/jdbc/ConnectionOptionsTest.java | 29 +++++++++++++++++ .../cloud/spanner/jdbc/JdbcDriverTest.java | 21 +++++++++--- .../spanner/jdbc/it/ITJdbcConnectTest.java | 32 +++++++------------ 6 files changed, 63 insertions(+), 34 deletions(-) diff --git a/src/main/java/com/google/cloud/spanner/jdbc/AbstractJdbcConnection.java b/src/main/java/com/google/cloud/spanner/jdbc/AbstractJdbcConnection.java index 76e979f0..71a1c20a 100644 --- a/src/main/java/com/google/cloud/spanner/jdbc/AbstractJdbcConnection.java +++ b/src/main/java/com/google/cloud/spanner/jdbc/AbstractJdbcConnection.java @@ -16,7 +16,6 @@ package com.google.cloud.spanner.jdbc; -import com.google.cloud.spanner.SpannerOptions; import com.google.common.annotations.VisibleForTesting; import com.google.rpc.Code; import java.sql.CallableStatement; @@ -74,11 +73,6 @@ ConnectionOptions getConnectionOptions() { return options; } - @Override - public SpannerOptions getSpannerOptions() { - return spanner.getSpannerOptions(); - } - @Override public CallableStatement prepareCall(String sql) throws SQLException { return checkClosedAndThrowUnsupported(CALLABLE_STATEMENTS_UNSUPPORTED); diff --git a/src/main/java/com/google/cloud/spanner/jdbc/CloudSpannerJdbcConnection.java b/src/main/java/com/google/cloud/spanner/jdbc/CloudSpannerJdbcConnection.java index ab8a78b1..d4c6629b 100644 --- a/src/main/java/com/google/cloud/spanner/jdbc/CloudSpannerJdbcConnection.java +++ b/src/main/java/com/google/cloud/spanner/jdbc/CloudSpannerJdbcConnection.java @@ -19,7 +19,6 @@ import com.google.cloud.spanner.AbortedException; import com.google.cloud.spanner.Mutation; import com.google.cloud.spanner.ResultSet; -import com.google.cloud.spanner.SpannerOptions; import java.sql.Connection; import java.sql.SQLException; import java.sql.Timestamp; @@ -151,8 +150,6 @@ public interface CloudSpannerJdbcConnection extends Connection { */ String getConnectionUrl(); - SpannerOptions getSpannerOptions(); - /** * @see * com.google.cloud.spanner.jdbc.Connection#addTransactionRetryListener(TransactionRetryListener) diff --git a/src/main/java/com/google/cloud/spanner/jdbc/JdbcDriver.java b/src/main/java/com/google/cloud/spanner/jdbc/JdbcDriver.java index 80ee2cc6..c43f9797 100644 --- a/src/main/java/com/google/cloud/spanner/jdbc/JdbcDriver.java +++ b/src/main/java/com/google/cloud/spanner/jdbc/JdbcDriver.java @@ -74,6 +74,12 @@ *
  • 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. + *
  • 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. *
  • autocommit (boolean): Sets the initial autocommit mode for the connection. Default is true. *
  • readonly (boolean): Sets the initial readonly mode for the connection. Default is false. *
  • retryAbortsInternally (boolean): Sets the initial retryAbortsInternally mode for the diff --git a/src/test/java/com/google/cloud/spanner/jdbc/ConnectionOptionsTest.java b/src/test/java/com/google/cloud/spanner/jdbc/ConnectionOptionsTest.java index b45c9572..08d625b1 100644 --- a/src/test/java/com/google/cloud/spanner/jdbc/ConnectionOptionsTest.java +++ b/src/test/java/com/google/cloud/spanner/jdbc/ConnectionOptionsTest.java @@ -340,4 +340,33 @@ public void testParseOAuthToken() { GoogleCredentials credentials = (GoogleCredentials) options.getCredentials(); assertThat(credentials.getAccessToken().getTokenValue()).isEqualTo("RsT5OjbzRn430zqMLgV3Ia"); } + + @Test + public void testSetOAuthToken() { + ConnectionOptions options = + ConnectionOptions.newBuilder() + .setUri( + "cloudspanner:/projects/test-project-123/instances/test-instance/databases/test-database") + .setOAuthToken("RsT5OjbzRn430zqMLgV3Ia") + .build(); + assertThat(options.getCredentials()).isInstanceOf(GoogleCredentials.class); + GoogleCredentials credentials = (GoogleCredentials) options.getCredentials(); + assertThat(credentials.getAccessToken()).isNotNull(); + assertThat(credentials.getAccessToken().getTokenValue()).isEqualTo("RsT5OjbzRn430zqMLgV3Ia"); + } + + @Test + public void testSetOAuthTokenAndCredentials() { + try { + ConnectionOptions.newBuilder() + .setUri( + "cloudspanner:/projects/test-project-123/instances/test-instance/databases/test-database") + .setOAuthToken("RsT5OjbzRn430zqMLgV3Ia") + .setCredentialsUrl(FILE_TEST_PATH) + .build(); + fail("missing expected exception"); + } catch (IllegalArgumentException e) { + assertThat(e.getMessage()).contains("Cannot specify both credentials and an OAuth token"); + } + } } diff --git a/src/test/java/com/google/cloud/spanner/jdbc/JdbcDriverTest.java b/src/test/java/com/google/cloud/spanner/jdbc/JdbcDriverTest.java index 68c252eb..db597e04 100644 --- a/src/test/java/com/google/cloud/spanner/jdbc/JdbcDriverTest.java +++ b/src/test/java/com/google/cloud/spanner/jdbc/JdbcDriverTest.java @@ -16,8 +16,8 @@ package com.google.cloud.spanner.jdbc; -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; +import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.fail; import com.google.cloud.spanner.MockSpannerServiceImpl; import io.grpc.Server; @@ -74,7 +74,7 @@ public void testConnect() throws SQLException { String.format( "jdbc:cloudspanner://localhost:%d/projects/test-project/instances/static-test-instance/databases/test-database;usePlainText=true;credentials=%s", server.getPort(), TEST_KEY_PATH))) { - assertThat(connection.isClosed(), is(false)); + assertThat(connection.isClosed()).isFalse(); } } @@ -85,7 +85,20 @@ public void testInvalidConnect() throws SQLException { String.format( "jdbc:cloudspanner://localhost:%d/projects/test-project/instances/static-test-instance/databases/test-database;usePlainText=true;credentialsUrl=%s", server.getPort(), TEST_KEY_PATH))) { - assertThat(connection.isClosed(), is(false)); + assertThat(connection.isClosed()).isFalse(); + } + } + + @Test + public void testConnectWithCredentialsAndOAuthToken() throws SQLException { + try (Connection connection = + DriverManager.getConnection( + String.format( + "jdbc:cloudspanner://localhost:%d/projects/test-project/instances/static-test-instance/databases/test-database;usePlainText=true;credentials=%s;OAuthToken=%s", + server.getPort(), TEST_KEY_PATH, "some-token"))) { + fail("missing expected exception"); + } catch (SQLException e) { + assertThat(e.getMessage()).contains("Cannot specify both credentials and an OAuth token"); } } } diff --git a/src/test/java/com/google/cloud/spanner/jdbc/it/ITJdbcConnectTest.java b/src/test/java/com/google/cloud/spanner/jdbc/it/ITJdbcConnectTest.java index 5a0785ae..7700aca3 100644 --- a/src/test/java/com/google/cloud/spanner/jdbc/it/ITJdbcConnectTest.java +++ b/src/test/java/com/google/cloud/spanner/jdbc/it/ITJdbcConnectTest.java @@ -18,11 +18,14 @@ import static com.google.common.truth.Truth.assertThat; +import com.google.auth.oauth2.AccessToken; import com.google.auth.oauth2.GoogleCredentials; import com.google.cloud.spanner.IntegrationTest; +import com.google.cloud.spanner.SpannerOptions; import com.google.cloud.spanner.jdbc.CloudSpannerJdbcConnection; import com.google.cloud.spanner.jdbc.ITAbstractJdbcTest; import com.google.cloud.spanner.jdbc.JdbcDataSource; +import java.io.FileInputStream; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; @@ -195,27 +198,14 @@ public void testConnectWithDataSourceWithConflictingValues() throws SQLException } @Test - public void testConnectWithOAuthToken() throws SQLException { - String url = createBaseUrl(); - try (Connection connection = DriverManager.getConnection(url)) { - CloudSpannerJdbcConnection cc = connection.unwrap(CloudSpannerJdbcConnection.class); - if (cc.getSpannerOptions().getCredentials() instanceof GoogleCredentials) { - GoogleCredentials credentials = (GoogleCredentials) cc.getSpannerOptions().getCredentials(); - // Get the OAuth token from this connection and open a new connection using that token. - String token = credentials.getAccessToken().getTokenValue(); - String urlWithOAuth = createBaseUrl() + "?OAuthToken=" + token; - try (Connection connectionWithOAuth = DriverManager.getConnection(urlWithOAuth)) { - // Check that the two connections use two different SpannerOptions instances. - assertThat( - connectionWithOAuth.unwrap(CloudSpannerJdbcConnection.class).getSpannerOptions()) - .isNotEqualTo(cc.getSpannerOptions()); - // Try to do a query using the connection created with an OAuth token. - testDefaultConnection(connectionWithOAuth); - } - } else { - throw new IllegalArgumentException( - "Unknown credentials type used: " + cc.getSpannerOptions().getCredentials()); - } + public void testConnectWithOAuthToken() throws Exception { + GoogleCredentials credentials = GoogleCredentials.fromStream(new FileInputStream(getKeyFile())); + credentials = credentials.createScoped(SpannerOptions.getDefaultInstance().getScopes()); + AccessToken token = credentials.refreshAccessToken(); + String urlWithOAuth = createBaseUrl() + "?OAuthToken=" + token.getTokenValue(); + try (Connection connectionWithOAuth = DriverManager.getConnection(urlWithOAuth)) { + // Try to do a query using the connection created with an OAuth token. + testDefaultConnection(connectionWithOAuth); } } } From cb1f462a5826d7990eab4206a16419eb51193ffd Mon Sep 17 00:00:00 2001 From: Olav Loite Date: Tue, 21 Jan 2020 20:19:02 +0100 Subject: [PATCH 3/5] fix: remove unused method --- .../java/com/google/cloud/spanner/jdbc/Connection.java | 4 ---- .../java/com/google/cloud/spanner/jdbc/ConnectionImpl.java | 7 ------- 2 files changed, 11 deletions(-) diff --git a/src/main/java/com/google/cloud/spanner/jdbc/Connection.java b/src/main/java/com/google/cloud/spanner/jdbc/Connection.java index 0286580e..0ae8ed5c 100644 --- a/src/main/java/com/google/cloud/spanner/jdbc/Connection.java +++ b/src/main/java/com/google/cloud/spanner/jdbc/Connection.java @@ -25,7 +25,6 @@ import com.google.cloud.spanner.ResultSet; import com.google.cloud.spanner.SpannerBatchUpdateException; import com.google.cloud.spanner.SpannerException; -import com.google.cloud.spanner.SpannerOptions; import com.google.cloud.spanner.Statement; import com.google.cloud.spanner.TimestampBound; import com.google.cloud.spanner.jdbc.StatementResult.ResultType; @@ -122,9 +121,6 @@ */ interface Connection extends AutoCloseable { - /** @return the {@link SpannerOptions} that were used to create this connection. */ - SpannerOptions getSpannerOptions(); - /** Closes this connection. This is a no-op if the {@link Connection} has alread been closed. */ @Override void close(); diff --git a/src/main/java/com/google/cloud/spanner/jdbc/ConnectionImpl.java b/src/main/java/com/google/cloud/spanner/jdbc/ConnectionImpl.java index df5b243e..8490dfcd 100644 --- a/src/main/java/com/google/cloud/spanner/jdbc/ConnectionImpl.java +++ b/src/main/java/com/google/cloud/spanner/jdbc/ConnectionImpl.java @@ -26,7 +26,6 @@ import com.google.cloud.spanner.Spanner; import com.google.cloud.spanner.SpannerException; import com.google.cloud.spanner.SpannerExceptionFactory; -import com.google.cloud.spanner.SpannerOptions; import com.google.cloud.spanner.Statement; import com.google.cloud.spanner.TimestampBound; import com.google.cloud.spanner.TimestampBound.Mode; @@ -232,12 +231,6 @@ static UnitOfWorkType of(TransactionMode transactionMode) { setDefaultTransactionOptions(); } - /** @return the {@link SpannerOptions} that were used to create this connection. */ - @Override - public SpannerOptions getSpannerOptions() { - return spanner.getOptions(); - } - private DdlClient createDdlClient() { return DdlClient.newBuilder() .setDatabaseAdminClient(spanner.getDatabaseAdminClient()) From b0fd3f4806cbc1885d91518df610f1912f11adf0 Mon Sep 17 00:00:00 2001 From: Olav Loite Date: Tue, 21 Jan 2020 21:05:30 +0100 Subject: [PATCH 4/5] fix: use default credentials when no file found --- .../google/cloud/spanner/jdbc/it/ITJdbcConnectTest.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/test/java/com/google/cloud/spanner/jdbc/it/ITJdbcConnectTest.java b/src/test/java/com/google/cloud/spanner/jdbc/it/ITJdbcConnectTest.java index 7700aca3..580bfff9 100644 --- a/src/test/java/com/google/cloud/spanner/jdbc/it/ITJdbcConnectTest.java +++ b/src/test/java/com/google/cloud/spanner/jdbc/it/ITJdbcConnectTest.java @@ -199,7 +199,12 @@ public void testConnectWithDataSourceWithConflictingValues() throws SQLException @Test public void testConnectWithOAuthToken() throws Exception { - GoogleCredentials credentials = GoogleCredentials.fromStream(new FileInputStream(getKeyFile())); + GoogleCredentials credentials; + if (hasValidKeyFile()) { + credentials = GoogleCredentials.fromStream(new FileInputStream(getKeyFile())); + } else { + credentials = GoogleCredentials.getApplicationDefault(); + } credentials = credentials.createScoped(SpannerOptions.getDefaultInstance().getScopes()); AccessToken token = credentials.refreshAccessToken(); String urlWithOAuth = createBaseUrl() + "?OAuthToken=" + token.getTokenValue(); From b9e116e6883fe5036620fc57a2b321c9e3a7baaa Mon Sep 17 00:00:00 2001 From: Olav Loite Date: Wed, 22 Jan 2020 14:45:26 +0100 Subject: [PATCH 5/5] fix: fix faulty merge --- .../com/google/cloud/spanner/jdbc/ConnectionOptionsTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/java/com/google/cloud/spanner/jdbc/ConnectionOptionsTest.java b/src/test/java/com/google/cloud/spanner/jdbc/ConnectionOptionsTest.java index d5af70d3..8eacbbf3 100644 --- a/src/test/java/com/google/cloud/spanner/jdbc/ConnectionOptionsTest.java +++ b/src/test/java/com/google/cloud/spanner/jdbc/ConnectionOptionsTest.java @@ -19,6 +19,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.fail; +import com.google.auth.oauth2.GoogleCredentials; import com.google.auth.oauth2.ServiceAccountCredentials; import com.google.cloud.spanner.SpannerOptions; import java.util.Arrays;