From 18089ed8eb769a0ab334b02cac25e725425b83e4 Mon Sep 17 00:00:00 2001 From: Leonardo Siracusa Date: Tue, 20 Oct 2020 14:10:10 -0700 Subject: [PATCH] feat: add external account credentials to ADC --- .../google/auth/oauth2/GoogleCredentials.java | 7 +++- .../auth/oauth2/AwsCredentialsTest.java | 27 +++++++++++-- .../auth/oauth2/GoogleCredentialsTest.java | 38 +++++++++++++++++++ .../oauth2/IdentityPoolCredentialsTest.java | 26 +++++++++++-- ...ckExternalAccountCredentialsTransport.java | 4 +- 5 files changed, 92 insertions(+), 10 deletions(-) diff --git a/oauth2_http/java/com/google/auth/oauth2/GoogleCredentials.java b/oauth2_http/java/com/google/auth/oauth2/GoogleCredentials.java index c32e72f47..07b2d3d04 100644 --- a/oauth2_http/java/com/google/auth/oauth2/GoogleCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/GoogleCredentials.java @@ -31,6 +31,8 @@ package com.google.auth.oauth2; +import static com.google.auth.oauth2.IdentityPoolCredentials.EXTERNAL_ACCOUNT_FILE_TYPE; + import com.google.api.client.json.GenericJson; import com.google.api.client.json.JsonFactory; import com.google.api.client.json.JsonObjectParser; @@ -49,8 +51,8 @@ public class GoogleCredentials extends OAuth2Credentials { private static final long serialVersionUID = -1522852442442473691L; - static final String QUOTA_PROJECT_ID_HEADER_KEY = "x-goog-user-project"; + static final String QUOTA_PROJECT_ID_HEADER_KEY = "x-goog-user-project"; static final String USER_FILE_TYPE = "authorized_user"; static final String SERVICE_ACCOUNT_FILE_TYPE = "service_account"; @@ -165,6 +167,9 @@ public static GoogleCredentials fromStream( if (SERVICE_ACCOUNT_FILE_TYPE.equals(fileType)) { return ServiceAccountCredentials.fromJson(fileContents, transportFactory); } + if (EXTERNAL_ACCOUNT_FILE_TYPE.equals(fileType)) { + return ExternalAccountCredentials.fromJson(fileContents, transportFactory); + } throw new IOException( String.format( "Error reading credentials from stream, 'type' value '%s' not recognized." diff --git a/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java index 7fd708bf6..8cee102a6 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java @@ -40,10 +40,12 @@ import com.google.api.client.json.GenericJson; import com.google.api.client.json.JsonParser; +import com.google.auth.TestUtils; import com.google.auth.http.HttpTransportFactory; import com.google.auth.oauth2.AwsCredentials.AwsCredentialSource; import com.google.auth.oauth2.ExternalAccountCredentialsTest.MockExternalAccountCredentialsTransportFactory; import java.io.IOException; +import java.io.InputStream; import java.net.URI; import java.util.Arrays; import java.util.Collection; @@ -315,15 +317,34 @@ public void createdScoped_clonedCredentialWithAddedScopes() { assertEquals(newScopes, newCredentials.getScopes()); } - private AwsCredentialSource buildAwsCredentialSource( + private static AwsCredentialSource buildAwsCredentialSource( MockExternalAccountCredentialsTransportFactory transportFactory) { Map credentialSourceMap = new HashMap<>(); - credentialSourceMap.put("region_url", transportFactory.transport.getAwsRegionEndpoint()); - credentialSourceMap.put("url", transportFactory.transport.getAwsCredentialsEndpoint()); + credentialSourceMap.put("region_url", transportFactory.transport.getAwsRegionUrl()); + credentialSourceMap.put("url", transportFactory.transport.getAwsCredentialsUrl()); credentialSourceMap.put("regional_cred_verification_url", GET_CALLER_IDENTITY_URL); return new AwsCredentialSource(credentialSourceMap); } + static InputStream writeAwsCredentialsStream(String stsUrl, String regionUrl, String metadataUrl) + throws IOException { + GenericJson json = new GenericJson(); + json.put("audience", "audience"); + json.put("subject_token_type", "subjectTokenType"); + json.put("token_url", stsUrl); + json.put("token_info_url", "tokenInfoUrl"); + json.put("type", ExternalAccountCredentials.EXTERNAL_ACCOUNT_FILE_TYPE); + + GenericJson credentialSource = new GenericJson(); + credentialSource.put("environment_id", "aws1"); + credentialSource.put("region_url", regionUrl); + credentialSource.put("url", metadataUrl); + credentialSource.put("regional_cred_verification_url", GET_CALLER_IDENTITY_URL); + json.put("credential_source", credentialSource); + + return TestUtils.jsonToInputStream(json); + } + /** Used to test the retrieval of AWS credentials from environment variables. */ private static class TestAwsCredentials extends AwsCredentials { diff --git a/oauth2_http/javatests/com/google/auth/oauth2/GoogleCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/GoogleCredentialsTest.java index ebf60a8ce..d48ab94e4 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/GoogleCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/GoogleCredentialsTest.java @@ -40,6 +40,7 @@ import com.google.api.client.testing.http.MockHttpTransport; import com.google.auth.TestUtils; import com.google.auth.http.HttpTransportFactory; +import com.google.auth.oauth2.IdentityPoolCredentialsTest.MockExternalAccountCredentialsTransportFactory; import com.google.common.collect.ImmutableList; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -224,6 +225,43 @@ public void fromStream_userNoRefreshToken_throws() throws IOException { testFromStreamException(userStream, "refresh_token"); } + @Test + public void fromStream_identityPoolCredentials_providesToken() throws IOException { + MockExternalAccountCredentialsTransportFactory transportFactory = + new MockExternalAccountCredentialsTransportFactory(); + InputStream identityPoolCredentialStream = + IdentityPoolCredentialsTest.writeIdentityPoolCredentialsStream( + transportFactory.transport.getStsUrl(), transportFactory.transport.getMetadataUrl()); + + GoogleCredentials credentials = + GoogleCredentials.fromStream(identityPoolCredentialStream, transportFactory); + + assertNotNull(credentials); + credentials = credentials.createScoped(SCOPES); + Map> metadata = credentials.getRequestMetadata(CALL_URI); + TestUtils.assertContainsBearerToken(metadata, transportFactory.transport.getAccessToken()); + } + + @Test + public void fromStream_awsCredentials_providesToken() throws IOException { + MockExternalAccountCredentialsTransportFactory transportFactory = + new MockExternalAccountCredentialsTransportFactory(); + + InputStream awsCredentialStream = + AwsCredentialsTest.writeAwsCredentialsStream( + transportFactory.transport.getStsUrl(), + transportFactory.transport.getAwsRegionUrl(), + transportFactory.transport.getAwsCredentialsUrl()); + + GoogleCredentials credentials = + GoogleCredentials.fromStream(awsCredentialStream, transportFactory); + + assertNotNull(credentials); + credentials = credentials.createScoped(SCOPES); + Map> metadata = credentials.getRequestMetadata(CALL_URI); + TestUtils.assertContainsBearerToken(metadata, transportFactory.transport.getAccessToken()); + } + @Test public void createScoped_overloadCallsImplementation() { final AtomicReference> called = new AtomicReference<>(); diff --git a/oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java index 84bedcfaf..1d9f56d14 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java @@ -39,11 +39,13 @@ import com.google.api.client.http.HttpTransport; import com.google.api.client.json.GenericJson; +import com.google.auth.TestUtils; import com.google.auth.http.HttpTransportFactory; import com.google.auth.oauth2.IdentityPoolCredentials.IdentityPoolCredentialSource; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; +import java.io.InputStream; import java.util.Arrays; import java.util.HashMap; import java.util.List; @@ -415,14 +417,30 @@ public void run() { e.getMessage()); } - @Test - public void identityPoolCredentialSource_jsonFormatTypeWithoutSubjectTokenFieldName() {} + static InputStream writeIdentityPoolCredentialsStream(String tokenUrl, String url) + throws IOException { + GenericJson json = new GenericJson(); + json.put("audience", "audience"); + json.put("subject_token_type", "subjectTokenType"); + json.put("token_url", tokenUrl); + json.put("token_info_url", "tokenInfoUrl"); + json.put("type", ExternalAccountCredentials.EXTERNAL_ACCOUNT_FILE_TYPE); + + GenericJson credentialSource = new GenericJson(); + GenericJson headers = new GenericJson(); + headers.put("Metadata-Flavor", "Google"); + credentialSource.put("url", url); + credentialSource.put("headers", headers); + + json.put("credential_source", credentialSource); + return TestUtils.jsonToInputStream(json); + } - private IdentityPoolCredentialSource buildUrlBasedCredentialSource(String url) { + private static IdentityPoolCredentialSource buildUrlBasedCredentialSource(String url) { return buildUrlBasedCredentialSource(url, /* formatMap= */ null); } - private IdentityPoolCredentialSource buildUrlBasedCredentialSource( + private static IdentityPoolCredentialSource buildUrlBasedCredentialSource( String url, Map formatMap) { Map credentialSourceMap = new HashMap<>(); Map headers = new HashMap<>(); diff --git a/oauth2_http/javatests/com/google/auth/oauth2/MockExternalAccountCredentialsTransport.java b/oauth2_http/javatests/com/google/auth/oauth2/MockExternalAccountCredentialsTransport.java index df205814c..9860acf0b 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/MockExternalAccountCredentialsTransport.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/MockExternalAccountCredentialsTransport.java @@ -228,11 +228,11 @@ public String getMetadataUrl() { return METADATA_SERVER_URL; } - public String getAwsCredentialsEndpoint() { + public String getAwsCredentialsUrl() { return AWS_CREDENTIALS_URL; } - public String getAwsRegionEndpoint() { + public String getAwsRegionUrl() { return AWS_REGION_URL; }