Skip to content
This repository was archived by the owner on May 12, 2026. It is now read-only.
Merged
63 changes: 50 additions & 13 deletions appengine/java/com/google/auth/appengine/AppEngineCredentials.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,41 +31,48 @@

package com.google.auth.appengine;

import com.google.auth.oauth2.AccessToken;
import com.google.auth.oauth2.GoogleCredentials;
import com.google.common.collect.ImmutableList;
import com.google.appengine.api.appidentity.AppIdentityService;
import com.google.appengine.api.appidentity.AppIdentityService.GetAccessTokenResult;
import com.google.appengine.api.appidentity.AppIdentityServiceFactory;
import com.google.auth.oauth2.AccessToken;
import com.google.auth.oauth2.GoogleCredentials;
import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.Collection;
import java.util.Date;
import java.util.Objects;

/**
* OAuth2 credentials representing the built-in service account for Google App ENgine.
*
* <p>Fetches access tokens from the App Identity service.
*/
public class AppEngineCredentials extends GoogleCredentials {

private final AppIdentityService appIdentityService;


private static final long serialVersionUID = -2627708355455064660L;

private final String appIdentityServiceClassName;
private final Collection<String> scopes;

private final boolean scopesRequired;

private final boolean scopesRequired;

private transient AppIdentityService appIdentityService;

public AppEngineCredentials(Collection<String> scopes) {
this(scopes, null);
}

public AppEngineCredentials(Collection<String> scopes, AppIdentityService appIdentityService) {
this.scopes = ImmutableList.copyOf(scopes);
this.scopes = scopes == null ? ImmutableSet.<String>of() : ImmutableList.copyOf(scopes);
this.appIdentityService = appIdentityService != null ? appIdentityService
: AppIdentityServiceFactory.getAppIdentityService();
scopesRequired = (scopes == null || scopes.isEmpty());
this.appIdentityServiceClassName = this.appIdentityService.getClass().getName();
scopesRequired = this.scopes.isEmpty();
}

/**
* Refresh the access token by getting it from the App Identity service
*/
Expand All @@ -88,5 +95,35 @@ public boolean createScopedRequired() {
@Override
public GoogleCredentials createScoped(Collection<String> scopes) {
return new AppEngineCredentials(scopes, appIdentityService);
}
}

@Override
public int hashCode() {
return Objects.hash(scopes, scopesRequired, appIdentityServiceClassName);
}

@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("scopes", scopes)
.add("scopesRequired", scopesRequired)
.add("appIdentityServiceClassName", appIdentityServiceClassName)
.toString();
}

@Override
public boolean equals(Object obj) {
if (!(obj instanceof AppEngineCredentials)) {
return false;
}
AppEngineCredentials other = (AppEngineCredentials) obj;
return this.scopesRequired == other.scopesRequired
&& Objects.equals(this.scopes, other.scopes)
&& Objects.equals(this.appIdentityServiceClassName, other.appIdentityServiceClassName);
}

private void readObject(ObjectInputStream input) throws IOException, ClassNotFoundException {
input.defaultReadObject();
appIdentityService = newInstance(appIdentityServiceClassName);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,11 @@
package com.google.auth.appengine;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.Assert.assertNotSame;

import com.google.auth.Credentials;
import com.google.auth.oauth2.AccessToken;
Expand All @@ -45,7 +46,11 @@
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.URI;
import java.util.Arrays;
import java.util.Collection;
Expand Down Expand Up @@ -117,7 +122,72 @@ public void createScoped_clonesWithScopes() throws IOException {
assertEquals(1, appIdentity.getGetAccessTokenCallCount());
assertContainsBearerToken(metadata, expectedAccessToken);
}


@Test
public void equals_true() throws IOException {
final Collection<String> emptyScopes = Collections.emptyList();
MockAppIdentityService appIdentity = new MockAppIdentityService();
GoogleCredentials credentials = new AppEngineCredentials(emptyScopes, appIdentity);
GoogleCredentials otherCredentials = new AppEngineCredentials(emptyScopes, appIdentity);
assertTrue(credentials.equals(credentials));
assertTrue(credentials.equals(otherCredentials));
assertTrue(otherCredentials.equals(credentials));
}

@Test
public void equals_false() throws IOException {
final Collection<String> emptyScopes = Collections.emptyList();
final Collection<String> scopes = Collections.singleton("SomeScope");
MockAppIdentityService appIdentity = new MockAppIdentityService();
GoogleCredentials credentials = new AppEngineCredentials(emptyScopes, appIdentity);
GoogleCredentials otherCredentials = new AppEngineCredentials(scopes, appIdentity);
assertFalse(credentials.equals(otherCredentials));
assertFalse(otherCredentials.equals(credentials));
}

@Test
public void toString_containsFields() throws IOException {
String expectedToString = String.format(
"AppEngineCredentials{scopes=[%s], scopesRequired=%b, appIdentityServiceClassName=%s}",
"SomeScope",
false,
MockAppIdentityService.class.getName());
final Collection<String> scopes = Collections.singleton("SomeScope");
MockAppIdentityService appIdentity = new MockAppIdentityService();
GoogleCredentials credentials = new AppEngineCredentials(scopes, appIdentity);
assertEquals(expectedToString, credentials.toString());
}

@Test
public void hashCode_equals() throws IOException {
final Collection<String> emptyScopes = Collections.emptyList();
MockAppIdentityService appIdentity = new MockAppIdentityService();
GoogleCredentials credentials = new AppEngineCredentials(emptyScopes, appIdentity);
GoogleCredentials otherCredentials = new AppEngineCredentials(emptyScopes, appIdentity);
assertEquals(credentials.hashCode(), otherCredentials.hashCode());
}

@Test
public void hashCode_notEquals() throws IOException {
final Collection<String> emptyScopes = Collections.emptyList();
final Collection<String> scopes = Collections.singleton("SomeScope");
MockAppIdentityService appIdentity = new MockAppIdentityService();
GoogleCredentials credentials = new AppEngineCredentials(emptyScopes, appIdentity);
GoogleCredentials otherCredentials = new AppEngineCredentials(scopes, appIdentity);
assertFalse(credentials.hashCode() == otherCredentials.hashCode());

This comment was marked as spam.

}

@Test
public void serialize() throws IOException, ClassNotFoundException {
final Collection<String> scopes = Collections.singleton("SomeScope");
MockAppIdentityService appIdentity = new MockAppIdentityService();
GoogleCredentials credentials = new AppEngineCredentials(scopes, appIdentity);
GoogleCredentials deserializedCredentials = serializeAndDeserialize(credentials);
assertEquals(credentials, deserializedCredentials);
assertEquals(credentials.hashCode(), deserializedCredentials.hashCode());
assertEquals(credentials.toString(), deserializedCredentials.toString());
}

private static void assertContainsBearerToken(Map<String, List<String>> metadata, String token) {
assertNotNull(metadata);
assertNotNull(token);
Expand All @@ -133,4 +203,24 @@ private static void assertContainsBearerToken(Map<String, List<String>> metadata
}
assertTrue("Bearer token not found", found);
}

@SuppressWarnings("unchecked")
public <T> T serializeAndDeserialize(T obj) throws IOException, ClassNotFoundException {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
ObjectOutputStream output = null;
ObjectInputStream input = null;
try {
output = new ObjectOutputStream(bytes);
output.writeObject(obj);
input = new ObjectInputStream(new ByteArrayInputStream(bytes.toByteArray()));
return (T) input.readObject();
} finally {
if (output != null) {
output.close();
}
if (input != null) {
input.close();
}
}
}
}
2 changes: 1 addition & 1 deletion appengine/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava-jdk5</artifactId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
Expand Down
5 changes: 4 additions & 1 deletion credentials/java/com/google/auth/Credentials.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.google.auth;

import java.io.IOException;
import java.io.Serializable;
import java.net.URI;
import java.util.List;
import java.util.Map;
Expand All @@ -9,7 +10,9 @@
/**
* Represents an abstract authorized identity instance.
*/
public abstract class Credentials {
public abstract class Credentials implements Serializable {

private static final long serialVersionUID = 808575179767517313L;

/**
* A constant string name describing the authentication technology.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.google.auth;

import java.io.IOException;
import java.util.List;
import java.util.Map;

Expand Down
48 changes: 48 additions & 0 deletions oauth2_http/java/com/google/auth/http/HttpTransportFactory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright 2016, Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
*
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

package com.google.auth.http;

import com.google.api.client.http.HttpTransport;

/**
* A base interface for all {@link HttpTransport} factories.
*
* <p>Implementation must provide a public no-arg constructor. Loading of a factory implementation
* is done via {@link java.util.ServiceLoader}.
*/
public interface HttpTransportFactory {

/**
* Creates a {@code HttpTransport} instance.
*/
HttpTransport create();
}
31 changes: 30 additions & 1 deletion oauth2_http/java/com/google/auth/oauth2/AccessToken.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
package com.google.auth.oauth2;

import com.google.common.base.MoreObjects;

import java.io.Serializable;
import java.util.Date;
import java.util.Objects;

/**
* Represents a temporary OAuth2 access token and its expiration information.
*/
public class AccessToken {
public class AccessToken implements Serializable {

private static final long serialVersionUID = -8514239465808977353L;

private final String tokenValue;
private final Long expirationTimeMillis;
Expand Down Expand Up @@ -39,4 +45,27 @@ public Date getExpirationTime() {
Long getExpirationTimeMillis() {
return expirationTimeMillis;
}

@Override
public int hashCode() {
return Objects.hash(tokenValue, expirationTimeMillis);
}

@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("tokenValue", tokenValue)
.add("expirationTimeMillis", expirationTimeMillis)
.toString();
}

@Override
public boolean equals(Object obj) {
if (!(obj instanceof AccessToken)) {
return false;
}
AccessToken other = (AccessToken) obj;
return Objects.equals(this.tokenValue, other.tokenValue)
&& Objects.equals(this.expirationTimeMillis, other.expirationTimeMillis);
}
}
Loading