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

Add OAuth2AuthCredentials class and tests #1030

Merged
merged 1 commit into from
May 28, 2016
Merged
Show file tree
Hide file tree
Changes from all 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
15 changes: 11 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ First, ensure that the necessary Google Cloud APIs are enabled for your project.
Next, choose a method for authenticating API requests from within your project:

1. When using `gcloud-java` libraries from within Compute/App Engine, no additional authentication steps are necessary.
2. When using `gcloud-java` libraries elsewhere, there are two options:
2. When using `gcloud-java` libraries elsewhere, there are three options:
* [Generate a JSON service account key](https://cloud.google.com/storage/docs/authentication?hl=en#service_accounts). After downloading that key, you must do one of the following:
* Define the environment variable GOOGLE_APPLICATION_CREDENTIALS to be the location of the key. For example:
```bash
Expand All @@ -116,11 +116,18 @@ Next, choose a method for authenticating API requests from within your project:
* Supply the JSON credentials file when building the service options. For example, this Storage object has the necessary permissions to interact with your Google Cloud Storage data:
```java
Storage storage = StorageOptions.builder()
.authCredentials(AuthCredentials.createForJson(new FileInputStream("/path/to/my/key.json"))
.authCredentials(AuthCredentials.createForJson(new FileInputStream("/path/to/my/key.json"))
.build()
.service();
```
* If running locally for development/testing, you can use Google Cloud SDK. Download the SDK if you haven't already, then login using the SDK (`gcloud auth login` in command line). Be sure to set your project ID as described above.
* If you already have an OAuth2 access token, you can use it to authenticate (notice that in this case the access token will not be automatically refreshed):
```java
Storage storage = StorageOptions.builder()
.authCredentials(AuthCredentials.createFor("your_access_token"))
.build()
.service();
```
* If running locally for development/testing, you can use use Google Cloud SDK. Download the SDK if you haven't already, then login using the SDK (`gcloud auth login` in command line). Be sure to set your project ID as described above.
```

`gcloud-java` looks for credentials in the following order, stopping once it finds credentials:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,67 @@ public RestorableState<AuthCredentials> capture() {
}
}

/**
* Represents OAuth2 credentials. These credentials can be created given an OAuth2 access token.
* The access token will not be automatically refreshed.
*/
public static class OAuth2AuthCredentials extends AuthCredentials {

private final GoogleCredentials credentials;
private final String accessToken;
private final Date expirationTime;

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

private static final long serialVersionUID = -7760693952274496205L;

private final String accessToken;
private final Date expirationTime;

private OAuth2AuthCredentialsState(String accessToken, Date expirationTime) {
this.accessToken = accessToken;
this.expirationTime = expirationTime;
}

@Override
public AuthCredentials restore() {
return new OAuth2AuthCredentials(accessToken, expirationTime);
}

@Override
public int hashCode() {
return Objects.hash(accessToken, expirationTime);
}

@Override
public boolean equals(Object obj) {
if (!(obj instanceof OAuth2AuthCredentialsState)) {
return false;
}
OAuth2AuthCredentialsState other = (OAuth2AuthCredentialsState) obj;
return Objects.equals(accessToken, other.accessToken)
&& Objects.equals(expirationTime, other.expirationTime);
}
}

OAuth2AuthCredentials(String accessToken, Date expirationTime) {
this.accessToken = checkNotNull(accessToken);
this.expirationTime = expirationTime;
this.credentials = new GoogleCredentials(new AccessToken(accessToken, expirationTime));
}

@Override
public GoogleCredentials credentials() {
return credentials;
}

@Override
public RestorableState<AuthCredentials> capture() {
return new OAuth2AuthCredentialsState(accessToken, expirationTime);
}
}

/**
* A placeholder for credentials to signify that requests sent to the server should not be
* authenticated. This is typically useful when using the local service emulators, such as
Expand Down Expand Up @@ -428,6 +489,28 @@ public static ServiceAccountAuthCredentials createFor(String account, PrivateKey
return new ServiceAccountAuthCredentials(account, privateKey);
}

/**
* Creates OAuth2 Credentials given the string representation of an access token. The access token
* will not be automatically refreshed.
*
* @param accessToken string representation of an access token
* @return the credentials instance
*/
public static OAuth2AuthCredentials createFor(String accessToken) {
return createFor(accessToken, (Date) null);
}

/**
* Creates OAuth2 Credentials given the string representation of an access token and its
* expiration time. The access token will not be automatically refreshed.
*
* @param accessToken string representation of an access token
* @return the credentials instance
*/
public static OAuth2AuthCredentials createFor(String accessToken, Date expirationTime) {
return new OAuth2AuthCredentials(accessToken, expirationTime);
}

/**
* Creates a placeholder denoting that no credentials should be used. This is typically useful
* when using the local service emulators, such as {@code LocalDatastoreHelper} and
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
/*
* Copyright 2016 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.google.cloud;

import static com.google.common.base.Charsets.UTF_8;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;

import com.google.auth.oauth2.AccessToken;
import com.google.auth.oauth2.ServiceAccountCredentials;
import com.google.cloud.AuthCredentials.OAuth2AuthCredentials;
import com.google.cloud.AuthCredentials.ServiceAccountAuthCredentials;
import com.google.common.io.BaseEncoding;

import org.junit.BeforeClass;
import org.junit.Test;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Date;

public class AuthCredentialsTest {

private static final String ACCESS_TOKEN = "accessToken";
private static final Date EXPIRATION_DATE = new Date();

This comment was marked as spam.

private static final String SERVICE_ACCOUNT = "[email protected]";
private static final String PRIVATE_KEY_STRING = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoG"
+ "BAL2xolH1zrISQ8+GzOV29BNjjzq4/HIP8Psd1+cZb81vDklSF+95wB250MSE0BDc81pvIMwj5OmIfLg1NY6uB1xav"
+ "OPpVdx1z664AGc/BEJ1zInXGXaQ6s+SxGenVq40Yws57gikQGMZjttpf1Qbz4DjkxsbRoeaRHn06n9pH1ejAgMBAAE"
+ "CgYEAkWcm0AJF5LMhbWKbjkxm/LG06UNApkHX6vTOOOODkonM/qDBnhvKCj8Tan+PaU2j7679Cd19qxCm4SBQJET7e"
+ "BhqLD9L2j9y0h2YUQnLbISaqUS1/EXcr2C1Lf9VCEn1y/GYuDYqs85rGoQ4ZYfM9ClROSq86fH+cbIIssqJqukCQQD"
+ "18LjfJz/ichFeli5/l1jaFid2XoCH3T6TVuuysszVx68fh60gSIxEF/0X2xB+wuPxTP4IQ+t8tD/ktd232oWXAkEAx"
+ "XPych2QBHePk9/lek4tOkKBgfnDzex7S/pI0G1vpB3VmzBbCsokn9lpOv7JV8071GDlW/7R6jlLfpQy3hN31QJAE10"
+ "osSk99m5Uv8XDU3hvHnywDrnSFOBulNs7I47AYfSe7TSZhPkxUgsxejddTR27JLyTI8N1PxRSE4feNSOXcQJAMMKJR"
+ "JT4U6IS2rmXubREhvaVdLtxFxEnAYQ1JwNfZm/XqBMw6GEy2iaeTetNXVlZRQEIoscyn1y2v/No/F5iYQJBAKBOGAS"
+ "oQcBjGTOg/H/SfcE8QVNsKEpthRrs6CkpT80aZ/AV+ksfoIf2zw2M3mAHfrO+TBLdz4sicuFQvlN9SEc=";
private static final String JSON_KEY = "{\n"
+ " \"private_key_id\": \"somekeyid\",\n"
+ " \"private_key\": \"-----BEGIN PRIVATE KEY-----\\n" + PRIVATE_KEY_STRING
+ "\\n-----END PRIVATE KEY-----\\n\",\n"
+ " \"client_email\": \"[email protected]\",\n"
+ " \"client_id\": \"someclientid.apps.googleusercontent.com\",\n"
+ " \"type\": \"service_account\"\n"
+ "}";
private static final AuthCredentials NO_AUTH_CREDENTIALS = AuthCredentials.noAuth();
private static final OAuth2AuthCredentials OAUTH2_AUTH_CREDENTIALS =
AuthCredentials.createFor(ACCESS_TOKEN, EXPIRATION_DATE);
private static final byte[] BYTES_TO_SIGN = PRIVATE_KEY_STRING.getBytes(UTF_8);

private static PrivateKey privateKey;
private static byte[] signedBytes;

@BeforeClass
public static void beforeClass() throws NoSuchAlgorithmException, InvalidKeySpecException,
InvalidKeyException, SignatureException {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
privateKey = keyFactory.generatePrivate(
new PKCS8EncodedKeySpec(BaseEncoding.base64().decode(PRIVATE_KEY_STRING)));
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initSign(privateKey);
signature.update(BYTES_TO_SIGN);
signedBytes = signature.sign();
}

@Test
public void testNoAuthCredentials() {
assertSame(NO_AUTH_CREDENTIALS, AuthCredentials.noAuth());
assertNull(NO_AUTH_CREDENTIALS.credentials());
}

@Test
public void testOAuth2AuthCredentials() {
AccessToken accessToken = OAUTH2_AUTH_CREDENTIALS.credentials().getAccessToken();
assertEquals(ACCESS_TOKEN, accessToken.getTokenValue());
assertEquals(EXPIRATION_DATE, accessToken.getExpirationTime());
OAuth2AuthCredentials oAuth2AuthCredentials =
AuthCredentials.createFor(ACCESS_TOKEN);
accessToken = oAuth2AuthCredentials.credentials().getAccessToken();
assertEquals(ACCESS_TOKEN, accessToken.getTokenValue());
assertNull(accessToken.getExpirationTime());
}

@Test
public void testServiceAccountFromJson() throws IOException, SignatureException {
ServiceAccountAuthCredentials serviceAccountAuthCredentials =
AuthCredentials.createForJson(new ByteArrayInputStream(JSON_KEY.getBytes()));
ServiceAccountCredentials credentials = serviceAccountAuthCredentials.credentials();
assertEquals(SERVICE_ACCOUNT, serviceAccountAuthCredentials.account());
assertEquals(SERVICE_ACCOUNT, credentials.getClientEmail());
assertEquals(privateKey, credentials.getPrivateKey());
assertArrayEquals(signedBytes, serviceAccountAuthCredentials.sign(BYTES_TO_SIGN));
}

@Test
public void testServiceAccountFromKey() throws IOException, SignatureException {
ServiceAccountAuthCredentials serviceAccountAuthCredentials =
AuthCredentials.createFor(SERVICE_ACCOUNT, privateKey);
ServiceAccountCredentials credentials = serviceAccountAuthCredentials.credentials();
assertEquals(SERVICE_ACCOUNT, serviceAccountAuthCredentials.account());
assertEquals(SERVICE_ACCOUNT, credentials.getClientEmail());
assertEquals(privateKey, credentials.getPrivateKey());
assertArrayEquals(signedBytes, serviceAccountAuthCredentials.sign(BYTES_TO_SIGN));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.Serializable;
import java.util.Date;

public class SerializationTest extends BaseSerializationTest {

Expand Down Expand Up @@ -94,7 +95,8 @@ protected Serializable[] serializableObjects() {
protected Restorable<?>[] restorableObjects() {
try {
return new Restorable<?>[]{AuthCredentials.createForAppEngine(), AuthCredentials.noAuth(),
AuthCredentials.createForJson(new ByteArrayInputStream(JSON_KEY.getBytes()))};
AuthCredentials.createForJson(new ByteArrayInputStream(JSON_KEY.getBytes())),
AuthCredentials.createFor("accessToken", new Date())};

This comment was marked as spam.

} catch (IOException ex) {
// never reached
throw new RuntimeException(ex);
Expand Down