Skip to content

Commit ef72c3b

Browse files
committed
Prototype Spanner Omni Login functionality
1 parent 85f51b2 commit ef72c3b

7 files changed

Lines changed: 1066 additions & 1 deletion

File tree

java-spanner/google-cloud-spanner/pom.xml

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,14 +159,19 @@
159159
<version>0.6.1</version>
160160
<configuration>
161161
<protocArtifact>com.google.protobuf:protoc:4.33.2:exe:${os.detected.classifier}</protocArtifact>
162+
<pluginId>grpc-java</pluginId>
163+
<pluginArtifact>io.grpc:protoc-gen-grpc-java:1.64.0:exe:${os.detected.classifier}</pluginArtifact>
162164
<additionalProtoPathElements>
163165
<additionalProtoPathElement>${project.basedir}/../proto-google-cloud-spanner-v1/src/main/proto</additionalProtoPathElement>
166+
<additionalProtoPathElement>${project.basedir}/../google-cloud-spanner/src/main/proto</additionalProtoPathElement>
164167
</additionalProtoPathElements>
165168
</configuration>
166169
<executions>
167170
<execution>
168-
<id>test-compile</id>
171+
<id>compile</id>
169172
<goals>
173+
<goal>compile</goal>
174+
<goal>compile-custom</goal>
170175
<goal>test-compile</goal>
171176
</goals>
172177
</execution>
@@ -532,6 +537,16 @@
532537
<version>2.91.0</version><!-- {x-version-update:proto-google-cloud-trace-v1:current} -->
533538
<scope>test</scope>
534539
</dependency>
540+
<dependency>
541+
<groupId>org.bouncycastle</groupId>
542+
<artifactId>bcprov-jdk18on</artifactId>
543+
<version>1.78</version>
544+
</dependency>
545+
<dependency>
546+
<groupId>com.google.crypto.tink</groupId>
547+
<artifactId>tink</artifactId>
548+
<version>1.13.0</version>
549+
</dependency>
535550
</dependencies>
536551
<profiles>
537552
<profile>
@@ -544,6 +559,16 @@
544559
<groupId>javax.annotation</groupId>
545560
<artifactId>javax.annotation-api</artifactId>
546561
</dependency>
562+
<dependency>
563+
<groupId>org.bouncycastle</groupId>
564+
<artifactId>bcprov-jdk18on</artifactId>
565+
<version>1.78</version>
566+
</dependency>
567+
<dependency>
568+
<groupId>com.google.crypto.tink</groupId>
569+
<artifactId>tink</artifactId>
570+
<version>1.13.0</version>
571+
</dependency>
547572
</dependencies>
548573
</profile>
549574
<profile>
@@ -719,6 +744,16 @@
719744
<groupId>com.google.api.grpc</groupId>
720745
<artifactId>grpc-google-cloud-spanner-executor-v1</artifactId>
721746
</dependency>
747+
<dependency>
748+
<groupId>org.bouncycastle</groupId>
749+
<artifactId>bcprov-jdk18on</artifactId>
750+
<version>1.78</version>
751+
</dependency>
752+
<dependency>
753+
<groupId>com.google.crypto.tink</groupId>
754+
<artifactId>tink</artifactId>
755+
<version>1.13.0</version>
756+
</dependency>
722757
</dependencies>
723758
</profile>
724759
</profiles>

java-spanner/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerOptions.java

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@
9191
import java.io.IOException;
9292
import java.net.MalformedURLException;
9393
import java.net.URL;
94+
import java.nio.charset.StandardCharsets;
9495
import java.nio.file.Files;
9596
import java.nio.file.Paths;
9697
import java.time.Duration;
@@ -1817,6 +1818,82 @@ public Builder setExperimentalHost(String host) {
18171818
return this;
18181819
}
18191820

1821+
1822+
/**
1823+
* Authenticates to Spanner Omni using the provided username and password file, and configures
1824+
* the resulting token for use in subsequent Spanner API calls. The endpoint must be set on the
1825+
* builder before calling this method.
1826+
*
1827+
* @param username The username for login.
1828+
* @param passwordFile The path to a file containing the password.
1829+
* @return this builder
1830+
*/
1831+
public Builder login(String username, String passwordFile) {
1832+
return login(username, passwordFile, true);
1833+
}
1834+
1835+
/**
1836+
* Authenticates to Spanner Omni using the provided username and password file, and configures
1837+
* the resulting token for use in subsequent Spanner API calls. The endpoint must be set on the
1838+
* builder before calling this method.
1839+
*
1840+
* @param username The username for login.
1841+
* @param passwordFile The path to a file containing the password.
1842+
* @param backgroundRefresh Whether to proactively refresh the token in a background thread before it expires. If false, GAX still triggers a synchronous inline refresh upon UNAUTHENTICATED error.
1843+
* @return this builder
1844+
*/
1845+
public Builder login(String username, String passwordFile, boolean backgroundRefresh) {
1846+
try {
1847+
byte[] rawBytes = Files.readAllBytes(Paths.get(passwordFile));
1848+
int len = rawBytes.length;
1849+
while (len > 0 && (rawBytes[len - 1] == '\n' || rawBytes[len - 1] == '\r')) {
1850+
len--;
1851+
}
1852+
byte[] passwordBytes = new byte[len];
1853+
System.arraycopy(rawBytes, 0, passwordBytes, 0, len);
1854+
return loginWithPasswordBytes(username, passwordBytes, backgroundRefresh);
1855+
} catch (IOException e) {
1856+
throw SpannerExceptionFactory.newSpannerException(
1857+
ErrorCode.NOT_FOUND, "Could not read password file: " + passwordFile, e);
1858+
}
1859+
}
1860+
1861+
/**
1862+
* Authenticates to Spanner Omni using the provided username and password, and configures the
1863+
* resulting token for use in subsequent Spanner API calls. The endpoint must be set on the
1864+
* builder before calling this method.
1865+
*
1866+
* @param username The username for login.
1867+
* @param password The password for login.
1868+
* @return this builder
1869+
*/
1870+
public Builder loginWithPassword(String username, String password) {
1871+
return loginWithPassword(username, password, true);
1872+
}
1873+
1874+
/**
1875+
* Authenticates to Spanner Omni using the provided username and password, and configures the
1876+
* resulting token for use in subsequent Spanner API calls. The endpoint must be set on the
1877+
* builder before calling this method.
1878+
*
1879+
* @param username The username for login.
1880+
* @param password The password for login.
1881+
* @param backgroundRefresh Whether to proactively refresh the token in a background thread before it expires. If false, GAX still triggers a synchronous inline refresh upon UNAUTHENTICATED error.
1882+
* @return this builder
1883+
*/
1884+
public Builder loginWithPassword(String username, String password, boolean backgroundRefresh) {
1885+
return loginWithPasswordBytes(username, password.getBytes(StandardCharsets.UTF_8), backgroundRefresh);
1886+
}
1887+
1888+
private Builder loginWithPasswordBytes(String username, byte[] password, boolean backgroundRefresh) {
1889+
if (this.experimentalHost == null) {
1890+
throw new IllegalStateException("Endpoint must be set before calling login.");
1891+
}
1892+
String target = this.experimentalHost.replaceFirst("^https?://", "");
1893+
super.setCredentials(new com.google.cloud.spanner.omni.SpannerOmniCredentials(username, password, target, backgroundRefresh));
1894+
return this;
1895+
}
1896+
18201897
/** Enables gRPC-GCP extension with the default settings. This option is enabled by default. */
18211898
public Builder enableGrpcGcpExtension() {
18221899
return this.enableGrpcGcpExtension(null);

0 commit comments

Comments
 (0)