Skip to content

Commit

Permalink
Merge pull request #430 from ajkannan/remove-initializer-dependency
Browse files Browse the repository at this point in the history
Remove http initializer from AuthCredentials
  • Loading branch information
ajkannan committed Dec 2, 2015
2 parents 80c7a96 + 942ceb4 commit ab8ca97
Show file tree
Hide file tree
Showing 6 changed files with 109 additions and 88 deletions.
135 changes: 81 additions & 54 deletions gcloud-java-core/src/main/java/com/google/gcloud/AuthCredentials.java
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -42,8 +38,67 @@ 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 final AppEngineAuthCredentialsState STATE = new AppEngineAuthCredentialsState();

private static class AppEngineCredentials extends GoogleCredentials {

private final Object appIdentityService;
private final Method getAccessToken;
private final Method getAccessTokenResult;
private final Collection<String> scopes;

AppEngineCredentials() {
try {
Class<?> factoryClass =
Class.forName("com.google.appengine.api.appidentity.AppIdentityServiceFactory");
Method method = factoryClass.getMethod("getAppIdentityService");
this.appIdentityService = method.invoke(null);
Class<?> serviceClass =
Class.forName("com.google.appengine.api.appidentity.AppIdentityService");
Class<?> tokenResultClass = Class.forName(
"com.google.appengine.api.appidentity.AppIdentityService$GetAccessTokenResult");
this.getAccessTokenResult = serviceClass.getMethod("getAccessToken", Iterable.class);
this.getAccessToken = tokenResultClass.getMethod("getAccessToken");
this.scopes = null;
} catch (Exception e) {
throw new RuntimeException("Could not create AppEngineCredentials.", e);
}
}

AppEngineCredentials(Collection<String> scopes, AppEngineCredentials unscoped) {
this.appIdentityService = unscoped.appIdentityService;
this.getAccessToken = unscoped.getAccessToken;
this.getAccessTokenResult = unscoped.getAccessTokenResult;
this.scopes = scopes;
}

/**
* 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 {
Object accessTokenResult = getAccessTokenResult.invoke(appIdentityService, scopes);
String accessToken = (String) getAccessToken.invoke(accessTokenResult);
return new AccessToken(accessToken, null);
} catch (Exception e) {
throw new IOException("Could not get the access token.", e);
}
}

@Override
public boolean createScopedRequired() {
return scopes == null || scopes.isEmpty();
}

@Override
public GoogleCredentials createScoped(Collection<String> scopes) {
return new AppEngineCredentials(scopes, this);
}
}

private static class AppEngineAuthCredentialsState
implements RestorableState<AuthCredentials>, Serializable {
Expand All @@ -67,9 +122,8 @@ public boolean equals(Object obj) {
}

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

@Override
Expand All @@ -83,8 +137,6 @@ public static class ServiceAccountAuthCredentials extends AuthCredentials {
private final String account;
private final PrivateKey privateKey;

private static final AuthCredentials NO_CREDENTIALS = new ServiceAccountAuthCredentials();

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

Expand All @@ -100,9 +152,6 @@ private ServiceAccountAuthCredentialsState(String account, PrivateKey privateKey

@Override
public AuthCredentials restore() {
if (account == null && privateKey == null) {
return NO_CREDENTIALS;
}
return new ServiceAccountAuthCredentials(account, privateKey);
}

Expand All @@ -127,23 +176,9 @@ public boolean equals(Object obj) {
this.privateKey = checkNotNull(privateKey);
}

ServiceAccountAuthCredentials() {
account = null;
privateKey = null;
}

@Override
protected HttpRequestInitializer httpRequestInitializer(
HttpTransport transport, Set<String> 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();
public ServiceAccountCredentials credentials() {
return new ServiceAccountCredentials(null, account, privateKey, null, null);
}

public String account() {
Expand Down Expand Up @@ -198,18 +233,8 @@ public boolean equals(Object obj) {
}

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

public ServiceAccountAuthCredentials toServiceAccountCredentials() {
if (googleCredentials instanceof ServiceAccountCredentials) {
ServiceAccountCredentials credentials = (ServiceAccountCredentials) googleCredentials;
return new ServiceAccountAuthCredentials(credentials.getClientEmail(),
credentials.getPrivateKey());
}
return null;
public GoogleCredentials credentials() {
return googleCredentials;
}

@Override
Expand All @@ -218,8 +243,7 @@ public RestorableState<AuthCredentials> capture() {
}
}

protected abstract HttpRequestInitializer httpRequestInitializer(HttpTransport transport,
Set<String> scopes);
public abstract GoogleCredentials credentials();

public static AuthCredentials createForAppEngine() {
return AppEngineAuthCredentials.INSTANCE;
Expand Down Expand Up @@ -271,12 +295,15 @@ 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());
}

public static AuthCredentials noCredentials() {
return ServiceAccountAuthCredentials.NO_CREDENTIALS;
GoogleCredentials tempCredentials = GoogleCredentials.fromStream(jsonCredentialStream);
if (tempCredentials instanceof ServiceAccountCredentials) {
ServiceAccountCredentials tempServiceAccountCredentials =
(ServiceAccountCredentials) tempCredentials;
return new ServiceAccountAuthCredentials(
tempServiceAccountCredentials.getClientEmail(),
tempServiceAccountCredentials.getPrivateKey());
}
throw new IOException(
"The given JSON Credentials Stream is not for a service account credential.");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -311,8 +312,9 @@ protected ServiceOptions(Class<? extends ServiceFactory<ServiceT, OptionsT>> ser
httpTransportFactory = firstNonNull(builder.httpTransportFactory,
getFromServiceLoader(HttpTransportFactory.class, DefaultHttpTransportFactory.INSTANCE));
httpTransportFactoryClassName = httpTransportFactory.getClass().getName();
authCredentials = firstNonNull(builder.authCredentials, defaultAuthCredentials());
authCredentialsState = authCredentials.capture();
authCredentials =
builder.authCredentials != null ? builder.authCredentials : defaultAuthCredentials();
authCredentialsState = authCredentials != null ? authCredentials.capture() : null;
retryParams = builder.retryParams;
serviceFactory = firstNonNull(builder.serviceFactory,
getFromServiceLoader(serviceFactoryClass, defaultServiceFactory()));
Expand Down Expand Up @@ -348,7 +350,7 @@ private static AuthCredentials defaultAuthCredentials() {
try {
return AuthCredentials.createApplicationDefaults();
} catch (Exception ex) {
return AuthCredentials.noCredentials();
return null;
}
}

Expand Down Expand Up @@ -508,13 +510,15 @@ public RetryParams retryParams() {
* options.
*/
public HttpRequestInitializer httpRequestInitializer() {
HttpTransport httpTransport = httpTransportFactory.create();
final HttpRequestInitializer baseRequestInitializer =
authCredentials().httpRequestInitializer(httpTransport, scopes());
final HttpRequestInitializer delegate = authCredentials() != null
? new HttpCredentialsAdapter(authCredentials().credentials().createScoped(scopes()))
: null;
return new HttpRequestInitializer() {
@Override
public void initialize(HttpRequest httpRequest) throws IOException {
baseRequestInitializer.initialize(httpRequest);
if (delegate != null) {
delegate.initialize(httpRequest);
}
if (connectTimeout >= 0) {
httpRequest.setConnectTimeout(connectTimeout);
}
Expand Down Expand Up @@ -580,7 +584,7 @@ private void readObject(ObjectInputStream input) throws IOException, ClassNotFou
httpTransportFactory = newInstance(httpTransportFactoryClassName);
serviceFactory = newInstance(serviceFactoryClassName);
serviceRpcFactory = newInstance(serviceRpcFactoryClassName);
authCredentials = authCredentialsState.restore();
authCredentials = authCredentialsState != null ? authCredentialsState.restore() : null;
}

private static <T> T newInstance(String className) throws IOException, ClassNotFoundException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ public void testServiceOptions() throws Exception {
options = options.toBuilder()
.namespace("ns1")
.retryParams(RetryParams.defaultInstance())
.authCredentials(AuthCredentials.noCredentials())
.authCredentials(null)
.force(true)
.build();
serializedCopy = serializeAndDeserialize(options);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,26 +31,24 @@
import static java.nio.charset.StandardCharsets.UTF_8;

import com.google.api.services.storage.model.StorageObject;
import com.google.auth.oauth2.ServiceAccountCredentials;
import com.google.common.base.Function;
import com.google.common.base.Functions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.hash.Hashing;
import com.google.common.io.BaseEncoding;
import com.google.common.primitives.Ints;
import com.google.gcloud.AuthCredentials;
import com.google.gcloud.AuthCredentials.ApplicationDefaultAuthCredentials;
import com.google.gcloud.AuthCredentials.ServiceAccountAuthCredentials;
import com.google.gcloud.PageImpl;
import com.google.gcloud.BaseService;
import com.google.gcloud.ExceptionHandler;
import com.google.gcloud.ExceptionHandler.Interceptor;
import com.google.gcloud.RetryHelper.RetryHelperException;
import com.google.gcloud.Page;
import com.google.gcloud.PageImpl;
import com.google.gcloud.RetryHelper.RetryHelperException;
import com.google.gcloud.spi.StorageRpc;
import com.google.gcloud.spi.StorageRpc.RewriteResponse;
import com.google.gcloud.spi.StorageRpc.Tuple;
Expand All @@ -71,7 +69,6 @@
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;

Expand Down Expand Up @@ -563,18 +560,15 @@ public URL signUrl(BlobInfo blobInfo, long duration, TimeUnit unit, SignUrlOptio
for (SignUrlOption option : options) {
optionMap.put(option.option(), option.value());
}
ServiceAccountAuthCredentials cred =
ServiceAccountAuthCredentials authCred =
(ServiceAccountAuthCredentials) optionMap.get(SignUrlOption.Option.SERVICE_ACCOUNT_CRED);
if (cred == null) {
AuthCredentials serviceCred = this.options().authCredentials();
if (serviceCred instanceof ServiceAccountAuthCredentials) {
cred = (ServiceAccountAuthCredentials) serviceCred;
} else {
if (serviceCred instanceof ApplicationDefaultAuthCredentials) {
cred = ((ApplicationDefaultAuthCredentials) serviceCred).toServiceAccountCredentials();
}
}
checkArgument(cred != null, "Signing key was not provided and could not be derived");
ServiceAccountCredentials cred = authCred != null ? authCred.credentials() : null;
if (authCred == null) {
checkArgument(
this.options().authCredentials() != null
&& this.options().authCredentials().credentials() instanceof ServiceAccountCredentials,
"Signing key was not provided and could not be derived");
cred = (ServiceAccountCredentials) this.options().authCredentials().credentials();
}
// construct signature - see https://cloud.google.com/storage/docs/access-control#Signed-URLs
StringBuilder stBuilder = new StringBuilder();
Expand Down Expand Up @@ -610,12 +604,12 @@ public URL signUrl(BlobInfo blobInfo, long duration, TimeUnit unit, SignUrlOptio
stBuilder.append(path);
try {
Signature signer = Signature.getInstance("SHA256withRSA");
signer.initSign(cred.privateKey());
signer.initSign(cred.getPrivateKey());
signer.update(stBuilder.toString().getBytes(UTF_8));
String signature =
URLEncoder.encode(BaseEncoding.base64().encode(signer.sign()), UTF_8.name());
stBuilder = new StringBuilder("https://storage.googleapis.com").append(path);
stBuilder.append("?GoogleAccessId=").append(cred.account());
stBuilder.append("?GoogleAccessId=").append(cred.getClientEmail());
stBuilder.append("&Expires=").append(expiration);
stBuilder.append("&Signature=").append(signature);
return new URL(stBuilder.toString());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ public void testServiceOptions() throws Exception {
options = options.toBuilder()
.projectId("p2")
.retryParams(RetryParams.defaultInstance())
.authCredentials(AuthCredentials.noCredentials())
.authCredentials(null)
.pathDelimiter(":")
.build();
serializedCopy = serializeAndDeserialize(options);
Expand Down Expand Up @@ -111,7 +111,6 @@ public void testReadChannelState() throws IOException, ClassNotFoundException {
StorageOptions options = StorageOptions.builder()
.projectId("p2")
.retryParams(RetryParams.defaultInstance())
.authCredentials(AuthCredentials.noCredentials())
.build();
BlobReadChannel reader =
new BlobReadChannelImpl(options, BlobId.of("b", "n"), EMPTY_RPC_OPTIONS);
Expand All @@ -127,7 +126,6 @@ public void testWriteChannelState() throws IOException, ClassNotFoundException {
StorageOptions options = StorageOptions.builder()
.projectId("p2")
.retryParams(RetryParams.defaultInstance())
.authCredentials(AuthCredentials.noCredentials())
.build();
BlobWriteChannelImpl writer = new BlobWriteChannelImpl(
options, BlobInfo.builder(BlobId.of("b", "n")).build(), "upload-id");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,10 @@
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import com.google.common.io.BaseEncoding;
import com.google.gcloud.AuthCredentials;
import com.google.gcloud.AuthCredentials.ServiceAccountAuthCredentials;
import com.google.gcloud.Page;
import com.google.gcloud.RetryParams;
import com.google.gcloud.ServiceOptions;
import com.google.gcloud.Page;
import com.google.gcloud.spi.StorageRpc;
import com.google.gcloud.spi.StorageRpc.Tuple;
import com.google.gcloud.spi.StorageRpcFactory;
Expand Down Expand Up @@ -260,7 +259,6 @@ public void setUp() throws IOException, InterruptedException {
EasyMock.replay(rpcFactoryMock);
options = StorageOptions.builder()
.projectId("projectId")
.authCredentials(AuthCredentials.noCredentials())
.clock(TIME_SOURCE)
.serviceRpcFactory(rpcFactoryMock)
.build();
Expand Down

0 comments on commit ab8ca97

Please sign in to comment.