Skip to content

Commit

Permalink
Add HttpTransportFactory class, make credentials serializable, add te…
Browse files Browse the repository at this point in the history
…sts (#69)

* Add HttpTransportFactory class, make credentials serializable, add tests

* Avoid using repackaged classes

* Add VisibleForTesting annotation to ServiceAccountJwtAccessCredentials' clock

* Remove clientSecret from UserCredentials' toString

* Add explicit language version declaration to main pom

* Use java7 language features and fix minor warnings

* Reuse BaseSerializationTest in AppEngineCredentialsTest

* Split inequality tests, remove hashCode inequality tests
  • Loading branch information
mziccard authored and garrettjonesgoogle committed Oct 10, 2016
1 parent 5d56615 commit 905b597
Show file tree
Hide file tree
Showing 35 changed files with 1,620 additions and 395 deletions.
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,13 +32,15 @@
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;
import com.google.auth.oauth2.BaseSerializationTest;
import com.google.auth.oauth2.GoogleCredentials;

import org.junit.Test;
Expand All @@ -58,7 +60,7 @@
* Unit tests for AppEngineCredentials
*/
@RunWith(JUnit4.class)
public class AppEngineCredentialsTest {
public class AppEngineCredentialsTest extends BaseSerializationTest {

private static final Collection<String> SCOPES =
Collections.unmodifiableCollection(Arrays.asList("scope1", "scope2"));
Expand Down Expand Up @@ -117,7 +119,62 @@ 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_scopes() 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 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 Down
8 changes: 7 additions & 1 deletion appengine/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,19 @@
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava-jdk5</artifactId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.auth</groupId>
<artifactId>google-auth-library-oauth2-http</artifactId>
<type>test-jar</type>
<scope>test</scope>
</dependency>
</dependencies>

</project>
5 changes: 4 additions & 1 deletion credentials/java/com/google/auth/Credentials.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,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 @@ -40,7 +41,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
Expand Up @@ -99,7 +99,7 @@ public void initialize(HttpRequest request) throws IOException {
}
for (Map.Entry<String, List<String>> entry : credentialHeaders.entrySet()) {
String headerName = entry.getKey();
List<String> requestValues = new ArrayList<String>();
List<String> requestValues = new ArrayList<>();
requestValues.addAll(entry.getValue());
requestHeaders.put(headerName, requestValues);
}
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
Expand Up @@ -31,12 +31,18 @@

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 @@ -70,4 +76,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);
}
}

0 comments on commit 905b597

Please sign in to comment.