Skip to content

Commit

Permalink
Merge branch 'master' into ae
Browse files Browse the repository at this point in the history
  • Loading branch information
elharo authored Dec 21, 2020
2 parents a9c148e + 269b49d commit 40af346
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,11 @@

/**
* ImpersonatedCredentials allowing credentials issued to a user or service account to impersonate
* another. <br>
* The source project using ImpersonatedCredentials must enable the "IAMCredentials" API.<br>
* Also, the target service account must grant the orginating principal the "Service Account Token
* Creator" IAM role. <br>
* Usage:<br>
* another. The source project using ImpersonatedCredentials must enable the "IAMCredentials" API.
* Also, the target service account must grant the originating principal the "Service Account Token
* Creator" IAM role.
*
* <p>Usage:
*
* <pre>
* String credPath = "/path/to/svc_account.json";
Expand All @@ -90,16 +90,12 @@ public class ImpersonatedCredentials extends GoogleCredentials

private static final long serialVersionUID = -2133257318957488431L;
private static final String RFC3339 = "yyyy-MM-dd'T'HH:mm:ss'Z'";
private static final int ONE_HOUR_IN_SECONDS = 3600;
private static final int TWELVE_HOURS_IN_SECONDS = 43200;
private static final String CLOUD_PLATFORM_SCOPE =
"https://www.googleapis.com/auth/cloud-platform";
private static final String IAM_ACCESS_TOKEN_ENDPOINT =
"https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/%s:generateAccessToken";

private static final String SCOPE_EMPTY_ERROR = "Scopes cannot be null";
private static final String LIFETIME_EXCEEDED_ERROR =
"lifetime must be less than or equal to 3600";

private GoogleCredentials sourceCredentials;
private String targetPrincipal;
private List<String> delegates;
Expand All @@ -110,19 +106,23 @@ public class ImpersonatedCredentials extends GoogleCredentials
private transient HttpTransportFactory transportFactory;

/**
* @param sourceCredentials The source credential used as to acquire the impersonated credentials
* @param targetPrincipal The service account to impersonate.
* @param delegates The chained list of delegates required to grant the final access_token. If
* @param sourceCredentials the source credential used as to acquire the impersonated credentials
* @param targetPrincipal the service account to impersonate
* @param delegates the chained list of delegates required to grant the final access_token. If
* set, the sequence of identities must have "Service Account Token Creator" capability
* granted to the preceding identity. For example, if set to [serviceAccountB,
* serviceAccountC], the sourceCredential must have the Token Creator role on serviceAccountB.
* serviceAccountB must have the Token Creator on serviceAccountC. Finally, C must have Token
* Creator on target_principal. If left unset, sourceCredential must have that role on
* Creator on target_principal. If unset, sourceCredential must have that role on
* targetPrincipal.
* @param scopes Scopes to request during the authorization grant.
* @param lifetime Number of seconds the delegated credential should be valid for (up to 3600).
* @param transportFactory HTTP transport factory, creates the transport used to get access
* tokens.
* @param scopes scopes to request during the authorization grant
* @param lifetime number of seconds the delegated credential should be valid. By default this
* value should be at most 3600. However, you can follow <a
* href='https://cloud.google.com/iam/docs/creating-short-lived-service-account-credentials#sa-credentials-oauth'>these
* instructions</a> to set up the service account and extend the maximum lifetime to 43200 (12
* hours).
* @param transportFactory HTTP transport factory that creates the transport used to get access
* tokens
* @return new credentials
*/
public static ImpersonatedCredentials create(
Expand All @@ -143,17 +143,22 @@ public static ImpersonatedCredentials create(
}

/**
* @param sourceCredentials The source credential used as to acquire the impersonated credentials
* @param targetPrincipal The service account to impersonate.
* @param delegates The chained list of delegates required to grant the final access_token. If
* @param sourceCredentials the source credential used as to acquire the impersonated credentials
* @param targetPrincipal the service account to impersonate
* @param delegates the chained list of delegates required to grant the final access_token. If
* set, the sequence of identities must have "Service Account Token Creator" capability
* granted to the preceding identity. For example, if set to [serviceAccountB,
* serviceAccountC], the sourceCredential must have the Token Creator role on serviceAccountB.
* serviceAccountB must have the Token Creator on serviceAccountC. Finally, C must have Token
* Creator on target_principal. If left unset, sourceCredential must have that role on
* targetPrincipal.
* @param scopes Scopes to request during the authorization grant.
* @param lifetime Number of seconds the delegated credential should be valid for (up to 3600).
* @param scopes scopes to request during the authorization grant
* @param lifetime number of seconds the delegated credential should be valid. By default this
* value should be at most 3600. However, you can follow <a
* href='https://cloud.google.com/iam/docs/creating-short-lived-service-account-credentials#sa-credentials-oauth'>these
* instructions</a> to set up the service account and extend the maximum lifetime to 43200 (12
* hours).
* https://cloud.google.com/iam/docs/creating-short-lived-service-account-credentials#sa-credentials-oauth
* @return new credentials
*/
public static ImpersonatedCredentials create(
Expand All @@ -174,7 +179,7 @@ public static ImpersonatedCredentials create(
/**
* Returns the email field of the serviceAccount that is being impersonated.
*
* @return email address of the impersonated service account.
* @return email address of the impersonated service account
*/
@Override
public String getAccount() {
Expand Down Expand Up @@ -216,7 +221,10 @@ private ImpersonatedCredentials(Builder builder) {
this.delegates = new ArrayList<String>();
}
if (this.scopes == null) {
throw new IllegalStateException(SCOPE_EMPTY_ERROR);
throw new IllegalStateException("Scopes cannot be null");
}
if (this.lifetime > TWELVE_HOURS_IN_SECONDS) {
throw new IllegalStateException("lifetime must be less than or equal to 43200");
}
}

Expand Down Expand Up @@ -267,26 +275,25 @@ public AccessToken refreshAccessToken() throws IOException {
OAuth2Utils.validateString(responseData, "expireTime", "Expected to find an expireTime");

DateFormat format = new SimpleDateFormat(RFC3339);
Date date;
try {
date = format.parse(expireTime);
Date date = format.parse(expireTime);
return new AccessToken(accessToken, date);
} catch (ParseException pe) {
throw new IOException("Error parsing expireTime: " + pe.getMessage());
}
return new AccessToken(accessToken, date);
}

/**
* Returns an IdToken for the current Credential.
*
* @param targetAudience the audience field for the issued ID Token
* @param options List of Credential specific options for for the token. For example, an IDToken
* for a ImpersonatedCredentials can return the email address within the token claims if
* @param targetAudience the audience field for the issued ID token
* @param options credential specific options for for the token. For example, an ID token for an
* ImpersonatedCredentials can return the email address within the token claims if
* "ImpersonatedCredentials.INCLUDE_EMAIL" is provided as a list option.<br>
* Only one option value is supported: "ImpersonatedCredentials.INCLUDE_EMAIL" If no options
* are set, the default excludes the "includeEmail" attribute in the API request
* @return IdToken object which includes the raw id_token, expiration and audience.
* @throws IOException if the attempt to get an IdToken failed
* are set, the default excludes the "includeEmail" attribute in the API request.
* @return IdToken object which includes the raw id_token, expiration, and audience
* @throws IOException if the attempt to get an ID token failed
*/
@Beta
@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ public class ImpersonatedCredentialsTest extends BaseSerializationTest {
Arrays.asList("https://www.googleapis.com/auth/devstorage.read_only");
private static final String ACCESS_TOKEN = "1/MkSJoj1xsli0AccessToken_NKPY2";
private static final int VALID_LIFETIME = 300;
private static final int INVALID_LIFETIME = 43210;
private static JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();

private static final String RFC3339 = "yyyy-MM-dd'T'HH:mm:ss'Z'";
Expand Down Expand Up @@ -197,6 +198,24 @@ public void refreshAccessToken_malformedTarget() throws IOException {
}
}

@Test()
public void credential_with_invalid_lifetime() throws IOException, IllegalStateException {

GoogleCredentials sourceCredentials = getSourceCredentials();
try {
ImpersonatedCredentials targetCredentials =
ImpersonatedCredentials.create(
sourceCredentials, IMPERSONATED_CLIENT_EMAIL, null, SCOPES, INVALID_LIFETIME);
targetCredentials.refreshAccessToken().getTokenValue();
fail(
String.format(
"Should throw exception with message containing '%s'",
"lifetime must be less than or equal to 43200"));
} catch (IllegalStateException expected) {
assertTrue(expected.getMessage().contains("lifetime must be less than or equal to 43200"));
}
}

@Test()
public void credential_with_invalid_scope() throws IOException, IllegalStateException {

Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.google.http.version>1.38.0</project.google.http.version>
<project.junit.version>4.13.1</project.junit.version>
<project.guava.version>30.0-android</project.guava.version>
<project.guava.version>30.1-android</project.guava.version>
<project.appengine.version>1.9.84</project.appengine.version>
<project.autovalue.version>1.7.4</project.autovalue.version>
<project.findbugs.version>3.0.2</project.findbugs.version>
Expand Down

0 comments on commit 40af346

Please sign in to comment.