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

Use Application Default Credentials to get App/Compute Engine credentials #337

Merged
merged 8 commits into from
Nov 25, 2015
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,9 @@ There are multiple ways to authenticate to use Google Cloud services.
`gcloud-java` looks for credentials in the following order, stopping once it finds credentials:

1. Credentials supplied when building the service options
2. App Engine credentials
3. Key file pointed to by the GOOGLE_APPLICATION_CREDENTIALS environment variable
4. Google Cloud SDK credentials
2. Key file pointed to by the GOOGLE_APPLICATION_CREDENTIALS environment variable
3. Google Cloud SDK credentials
4. App Engine credentials
5. Compute Engine credentials

Google Cloud Datastore
Expand Down
113 changes: 1 addition & 112 deletions gcloud-java-core/src/main/java/com/google/gcloud/AuthCredentials.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,15 @@
import static com.google.common.base.Preconditions.checkNotNull;

import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.googleapis.compute.ComputeCredential;
import com.google.api.client.googleapis.extensions.appengine.auth.oauth2.AppIdentityCredential;
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.http.HttpRequestInitializer;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.jackson.JacksonFactory;
import com.google.auth.http.HttpCredentialsAdapter;
import com.google.auth.oauth2.GoogleCredentials;

import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.security.GeneralSecurityException;
import java.security.PrivateKey;
import java.util.Objects;
import java.util.Set;
Expand All @@ -42,45 +37,6 @@
*/
public abstract class AuthCredentials implements Restorable<AuthCredentials> {

private static class AppEngineAuthCredentials extends AuthCredentials {

private static final AuthCredentials INSTANCE = new AppEngineAuthCredentials();
private static final AppEngineAuthCredentialsState STATE =
new AppEngineAuthCredentialsState();

private static class AppEngineAuthCredentialsState
implements RestorableState<AuthCredentials>, Serializable {

private static final long serialVersionUID = 3558563960848658928L;

@Override
public AuthCredentials restore() {
return INSTANCE;
}

@Override
public int hashCode() {
return getClass().getName().hashCode();
}

@Override
public boolean equals(Object obj) {
return obj instanceof AppEngineAuthCredentialsState;
}
}

@Override
protected HttpRequestInitializer httpRequestInitializer(HttpTransport transport,
Set<String> scopes) {
return new AppIdentityCredential(scopes);
}

@Override
public RestorableState<AuthCredentials> capture() {
return STATE;
}
}

public static class ServiceAccountAuthCredentials extends AuthCredentials {

private final String account;
Expand Down Expand Up @@ -163,55 +119,6 @@ public RestorableState<AuthCredentials> capture() {
}
}

private static class ComputeEngineAuthCredentials extends AuthCredentials {

private ComputeCredential computeCredential;

private static final ComputeEngineAuthCredentialsState STATE =
new ComputeEngineAuthCredentialsState();

private static class ComputeEngineAuthCredentialsState
implements RestorableState<AuthCredentials>, Serializable {

private static final long serialVersionUID = -6168594072854417404L;

@Override
public AuthCredentials restore() {
try {
return new ComputeEngineAuthCredentials();
} catch (IOException | GeneralSecurityException e) {
throw new IllegalStateException(
"Could not restore " + ComputeEngineAuthCredentials.class.getSimpleName(), e);
}
}

@Override
public int hashCode() {
return getClass().getName().hashCode();
}

@Override
public boolean equals(Object obj) {
return obj instanceof ComputeEngineAuthCredentialsState;
}
}

ComputeEngineAuthCredentials() throws IOException, GeneralSecurityException {
computeCredential = getComputeCredential();
}

@Override
protected HttpRequestInitializer httpRequestInitializer(HttpTransport transport,
Set<String> scopes) {
return computeCredential;
}

@Override
public RestorableState<AuthCredentials> capture() {
return STATE;
}
}

private static class ApplicationDefaultAuthCredentials extends AuthCredentials {

private GoogleCredentials googleCredentials;
Expand Down Expand Up @@ -264,21 +171,12 @@ public RestorableState<AuthCredentials> capture() {
protected abstract HttpRequestInitializer httpRequestInitializer(HttpTransport transport,
Set<String> scopes);

public static AuthCredentials createForAppEngine() {
return AppEngineAuthCredentials.INSTANCE;
}

public static AuthCredentials createForComputeEngine()
throws IOException, GeneralSecurityException {
return new ComputeEngineAuthCredentials();
}

/**
* Returns the Application Default Credentials.
*
* <p>Returns the Application Default Credentials which are credentials that identify and
* authorize the whole application. This is the built-in service account if running on
* Google Compute Engine or the credentials file can be read from the path in the environment
* Google App/Compute Engine or the credentials file can be read from the path in the environment
* variable GOOGLE_APPLICATION_CREDENTIALS.
* </p>
*
Expand Down Expand Up @@ -327,13 +225,4 @@ public static ServiceAccountAuthCredentials createForJson(InputStream jsonCreden
public static AuthCredentials noCredentials() {
return ServiceAccountAuthCredentials.NO_CREDENTIALS;
}

static ComputeCredential getComputeCredential() throws IOException, GeneralSecurityException {
NetHttpTransport transport = GoogleNetHttpTransport.newTrustedTransport();
// Try to connect using Google Compute Engine service account credentials.
ComputeCredential credential = new ComputeCredential(transport, new JacksonFactory());
// Force token refresh to detect if we are running on Google Compute Engine.
credential.refreshToken();
return credential;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,13 @@
import static java.nio.charset.StandardCharsets.UTF_8;

import com.google.api.client.extensions.appengine.http.UrlFetchTransport;
import com.google.api.client.googleapis.compute.ComputeCredential;
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpRequestInitializer;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.jackson.JacksonFactory;
import com.google.common.collect.Iterables;
import com.google.gcloud.spi.ServiceRpcFactory;

Expand All @@ -41,6 +44,7 @@
import java.lang.reflect.Method;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.GeneralSecurityException;
import java.util.Enumeration;
import java.util.Locale;
import java.util.Objects;
Expand Down Expand Up @@ -111,12 +115,22 @@ public HttpTransport create() {
}
// Consider Compute
try {
return AuthCredentials.getComputeCredential().getTransport();
return getComputeHttpTransport();

This comment was marked as spam.

} catch (Exception e) {
// Maybe not on GCE
}
return new NetHttpTransport();
}

private static HttpTransport getComputeHttpTransport()
throws IOException, GeneralSecurityException {
NetHttpTransport transport = GoogleNetHttpTransport.newTrustedTransport();
// Try to connect using Google Compute Engine service account credentials.
ComputeCredential credential = new ComputeCredential(transport, new JacksonFactory());
// Force token refresh to detect if we are running on Google Compute Engine.
credential.refreshToken();
return transport;
}
}

/**
Expand Down Expand Up @@ -326,28 +340,11 @@ protected ServiceOptions(Class<? extends ServiceFactory<ServiceT, OptionsT>> ser
}

private static AuthCredentials defaultAuthCredentials() {
// Consider App Engine. This will not be needed once issue #21 is fixed.
if (appEngineAppId() != null) {
try {
return AuthCredentials.createForAppEngine();
} catch (Exception ignore) {
// Maybe not on App Engine
}
}

try {
return AuthCredentials.createApplicationDefaults();
} catch (Exception ex) {
// fallback to old-style
}

// Consider old-style Compute. This will not be needed once issue #21 is fixed.
try {
return AuthCredentials.createForComputeEngine();
} catch (Exception ignore) {
// Maybe not on GCE
return AuthCredentials.noCredentials();
}
return AuthCredentials.noCredentials();
}

protected static String appEngineAppId() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,20 +133,21 @@ public class SerializationTest {

@Test
public void testServiceOptions() throws Exception {
DatastoreOptions options = DatastoreOptions.builder()
.authCredentials(AuthCredentials.createForAppEngine())
.normalizeDataset(false)
.projectId("ds1")
.build();
DatastoreOptions options =
DatastoreOptions.builder()
.normalizeDataset(false)
.projectId("ds1")
.build();
DatastoreOptions serializedCopy = serializeAndDeserialize(options);
assertEquals(options, serializedCopy);

options = options.toBuilder()
.namespace("ns1")
.retryParams(RetryParams.getDefaultInstance())
.authCredentials(AuthCredentials.noCredentials())
.force(true)
.build();
options =

This comment was marked as spam.

This comment was marked as spam.

options.toBuilder()
.namespace("ns1")
.retryParams(RetryParams.getDefaultInstance())
.authCredentials(AuthCredentials.noCredentials())
.force(true)
.build();
serializedCopy = serializeAndDeserialize(options);
assertEquals(options, serializedCopy);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,19 +73,20 @@ public class SerializationTest {

@Test
public void testServiceOptions() throws Exception {
StorageOptions options = StorageOptions.builder()
.projectId("p1")
.authCredentials(AuthCredentials.createForAppEngine())
.build();
StorageOptions options =
StorageOptions.builder()
.projectId("p1")
.build();
StorageOptions serializedCopy = serializeAndDeserialize(options);
assertEquals(options, serializedCopy);

options = options.toBuilder()
.projectId("p2")
.retryParams(RetryParams.getDefaultInstance())
.authCredentials(AuthCredentials.noCredentials())
.pathDelimiter(":")
.build();
options =
options.toBuilder()
.projectId("p2")
.retryParams(RetryParams.getDefaultInstance())
.authCredentials(AuthCredentials.noCredentials())
.pathDelimiter(":")
.build();
serializedCopy = serializeAndDeserialize(options);
assertEquals(options, serializedCopy);
}
Expand Down