diff --git a/gcloud-java-core/src/main/java/com/google/gcloud/AuthCredentials.java b/gcloud-java-core/src/main/java/com/google/gcloud/AuthCredentials.java index 3303e4f8a652..21e64d0958bf 100644 --- a/gcloud-java-core/src/main/java/com/google/gcloud/AuthCredentials.java +++ b/gcloud-java-core/src/main/java/com/google/gcloud/AuthCredentials.java @@ -18,21 +18,17 @@ import static com.google.common.base.Preconditions.checkNotNull; -import com.google.api.client.googleapis.auth.oauth2.GoogleCredential; -import com.google.api.client.googleapis.extensions.appengine.auth.oauth2.AppIdentityCredential; -import com.google.api.client.http.HttpRequestInitializer; -import com.google.api.client.http.HttpTransport; -import com.google.api.client.json.jackson.JacksonFactory; -import com.google.auth.http.HttpCredentialsAdapter; +import com.google.auth.oauth2.AccessToken; import com.google.auth.oauth2.GoogleCredentials; import com.google.auth.oauth2.ServiceAccountCredentials; import java.io.IOException; import java.io.InputStream; import java.io.Serializable; +import java.lang.reflect.Method; import java.security.PrivateKey; +import java.util.Collection; import java.util.Objects; -import java.util.Set; /** * Credentials for accessing Google Cloud services. @@ -42,8 +38,66 @@ public abstract class AuthCredentials implements Restorable { private static class AppEngineAuthCredentials extends AuthCredentials { private static final AuthCredentials INSTANCE = new AppEngineAuthCredentials(); - private static final AppEngineAuthCredentialsState STATE = - new AppEngineAuthCredentialsState(); + private static final AppEngineAuthCredentialsState STATE = new AppEngineAuthCredentialsState(); + + private static class AppEngineCredentials extends GoogleCredentials { + + private final Object appIdentityService; + private final Collection scopes; + private final boolean scopesRequired; + + AppEngineCredentials() { + try { + Class factoryClass = + Class.forName("com.google.appengine.api.appidentity.AppIdentityServiceFactory"); + Method method = factoryClass.getMethod("getAppIdentityService"); + this.appIdentityService = method.invoke(null); + this.scopes = null; + this.scopesRequired = true; + } catch (Exception e) { + throw new RuntimeException("Could not create AppEngineCredentials using reflection."); + } + } + + AppEngineCredentials(Collection scopes, Object appIdentityService) { + this.appIdentityService = appIdentityService; + this.scopes = scopes; + this.scopesRequired = (scopes == null || scopes.isEmpty()); + } + + /** + * Refresh the access token by getting it from the App Identity service + */ + @Override + public AccessToken refreshAccessToken() throws IOException { + if (createScopedRequired()) { + throw new IOException("AppEngineCredentials requires createScoped call before use."); + } + try { + Class serviceClass = + Class.forName("com.google.appengine.api.appidentity.AppIdentityService"); + Class tokenResultClass = Class.forName( + "com.google.appengine.api.appidentity.AppIdentityService$GetAccessTokenResult"); + Method getAccessTokenResult = serviceClass.getMethod("getAccessToken", Iterable.class); + Object accessTokenResult = getAccessTokenResult.invoke(appIdentityService, scopes); + Method getAccessToken = tokenResultClass.getMethod("getAccessToken"); + String accessToken = (String) getAccessToken.invoke(accessTokenResult); + return new AccessToken(accessToken, null); + } catch (Exception e) { + throw new RuntimeException("Could not get the access token using reflection."); + } + } + + @Override + public boolean createScopedRequired() { + return scopesRequired; + } + + @Override + public GoogleCredentials createScoped(Collection scopes) { + return new AppEngineCredentials(scopes, appIdentityService); + } + } private static class AppEngineAuthCredentialsState implements RestorableState, Serializable { @@ -67,9 +121,8 @@ public boolean equals(Object obj) { } @Override - protected HttpRequestInitializer httpRequestInitializer(HttpTransport transport, - Set scopes) { - return new AppIdentityCredential(scopes); + protected GoogleCredentials credentials() { + return new AppEngineCredentials(); } @Override @@ -133,17 +186,9 @@ public boolean equals(Object obj) { } @Override - protected HttpRequestInitializer httpRequestInitializer( - HttpTransport transport, Set scopes) { - GoogleCredential.Builder builder = new GoogleCredential.Builder() - .setTransport(transport) - .setJsonFactory(new JacksonFactory()); - if (privateKey != null) { - builder.setServiceAccountPrivateKey(privateKey); - builder.setServiceAccountId(account); - builder.setServiceAccountScopes(scopes); - } - return builder.build(); + protected GoogleCredentials credentials() { + return (privateKey == null) + ? null : new ServiceAccountCredentials(null, account, privateKey, null, null); } public String account() { @@ -198,9 +243,8 @@ public boolean equals(Object obj) { } @Override - protected HttpRequestInitializer httpRequestInitializer(HttpTransport transport, - Set scopes) { - return new HttpCredentialsAdapter(googleCredentials.createScoped(scopes)); + protected GoogleCredentials credentials() { + return googleCredentials; } public ServiceAccountAuthCredentials toServiceAccountCredentials() { @@ -218,8 +262,7 @@ public RestorableState capture() { } } - protected abstract HttpRequestInitializer httpRequestInitializer(HttpTransport transport, - Set scopes); + protected abstract GoogleCredentials credentials(); public static AuthCredentials createForAppEngine() { return AppEngineAuthCredentials.INSTANCE; @@ -271,9 +314,17 @@ public static ServiceAccountAuthCredentials createFor(String account, PrivateKey */ public static ServiceAccountAuthCredentials createForJson(InputStream jsonCredentialStream) throws IOException { - GoogleCredential tempCredentials = GoogleCredential.fromStream(jsonCredentialStream); - return new ServiceAccountAuthCredentials(tempCredentials.getServiceAccountId(), - tempCredentials.getServiceAccountPrivateKey()); + GoogleCredentials tempCredentials = GoogleCredentials.fromStream(jsonCredentialStream); + if (tempCredentials instanceof ServiceAccountCredentials) { + ServiceAccountCredentials tempServiceAccountCredentials = + (ServiceAccountCredentials) tempCredentials; + return new ServiceAccountAuthCredentials( + tempServiceAccountCredentials.getClientEmail(), + tempServiceAccountCredentials.getPrivateKey()); + } else { + throw new IOException( + "The given JSON Credentials Stream is not a service account credential."); + } } public static AuthCredentials noCredentials() { diff --git a/gcloud-java-core/src/main/java/com/google/gcloud/ServiceOptions.java b/gcloud-java-core/src/main/java/com/google/gcloud/ServiceOptions.java index 25fda29c363d..bc0e91d0a694 100644 --- a/gcloud-java-core/src/main/java/com/google/gcloud/ServiceOptions.java +++ b/gcloud-java-core/src/main/java/com/google/gcloud/ServiceOptions.java @@ -25,6 +25,7 @@ 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.auth.http.HttpCredentialsAdapter; import com.google.common.collect.Iterables; import com.google.gcloud.spi.ServiceRpcFactory; @@ -508,9 +509,8 @@ public RetryParams retryParams() { * options. */ public HttpRequestInitializer httpRequestInitializer() { - HttpTransport httpTransport = httpTransportFactory.create(); final HttpRequestInitializer baseRequestInitializer = - authCredentials().httpRequestInitializer(httpTransport, scopes()); + new HttpCredentialsAdapter(authCredentials().credentials().createScoped(scopes())); return new HttpRequestInitializer() { @Override public void initialize(HttpRequest httpRequest) throws IOException {