From b21127a5ddad0860be7182db33db98f668fca3d8 Mon Sep 17 00:00:00 2001 From: aeitzman Date: Sun, 26 Nov 2023 22:04:35 -0800 Subject: [PATCH 01/32] feat: adds programmatic auth credentials for identity pool and aws credential types --- .../google/auth/oauth2/AwsCredentials.java | 111 +++++++++++++- .../oauth2/ExternalAccountCredentials.java | 26 ++-- .../oauth2/ExternalAccountMetricsHandler.java | 6 +- .../auth/oauth2/IdentityPoolCredentials.java | 39 ++++- .../auth/oauth2/PluggableAuthCredentials.java | 4 - .../auth/oauth2/AwsCredentialsTest.java | 143 ++++++++++++++++++ .../ExternalAccountCredentialsTest.java | 33 ---- 7 files changed, 299 insertions(+), 63 deletions(-) diff --git a/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java b/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java index e47bbe167..45fc392a3 100644 --- a/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java @@ -50,6 +50,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.function.Supplier; import javax.annotation.Nullable; /** @@ -66,17 +67,40 @@ public class AwsCredentials extends ExternalAccountCredentials { static final String AWS_SECRET_ACCESS_KEY = "AWS_SECRET_ACCESS_KEY"; static final String AWS_SESSION_TOKEN = "AWS_SESSION_TOKEN"; + static final String DEFAULT_REGIONAL_CREDENTIAL_VERIFICATION_URL = + "https://sts.{region}.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15"; + static final String AWS_IMDSV2_SESSION_TOKEN_HEADER = "x-aws-ec2-metadata-token"; static final String AWS_IMDSV2_SESSION_TOKEN_TTL_HEADER = "x-aws-ec2-metadata-token-ttl-seconds"; static final String AWS_IMDSV2_SESSION_TOKEN_TTL = "300"; private static final long serialVersionUID = -3670131891574618105L; + @Nullable private final AwsCredentialSource awsCredentialSource; + @Nullable + private final Supplier awsSecurityCredentialsSupplier; + @Nullable + private final String regionalCredentialVerificationUrlOverride; + @Nullable + private final String region; /** Internal constructor. See {@link AwsCredentials.Builder}. */ AwsCredentials(Builder builder) { super(builder); - this.awsCredentialSource = (AwsCredentialSource) builder.credentialSource; + // If user has provided a security credential supplier, use that to retrieve the AWS security + // credentials. + if (builder.awsSecurityCredentialsSupplier != null){ + this.awsSecurityCredentialsSupplier = builder.awsSecurityCredentialsSupplier; + if (builder.region == null){ + throw new IllegalArgumentException("A region must be specified when using a credential supplier."); + } + this.awsCredentialSource = null; + } else { + this.awsCredentialSource = (AwsCredentialSource) builder.credentialSource; + this.awsSecurityCredentialsSupplier = null; + } + this.region = builder.region; + this.regionalCredentialVerificationUrlOverride = builder.regionalCredentialVerificationUrlOverride; } @Override @@ -115,7 +139,7 @@ public String retrieveSubjectToken() throws IOException { AwsRequestSigner.newBuilder( credentials, "POST", - awsCredentialSource.regionalCredentialVerificationUrl.replace("{region}", region), + this.getRegionalCredentialVerificationUrl().replace("{region}", region), region) .setAdditionalHeaders(headers) .build(); @@ -132,6 +156,9 @@ public GoogleCredentials createScoped(Collection newScopes) { @Override String getCredentialSourceType() { + if (this.awsSecurityCredentialsSupplier != null){ + return "programmatic"; + } return "aws"; } @@ -184,7 +211,7 @@ private String buildSubjectToken(AwsRequestSignature signature) token.put("method", signature.getHttpMethod()); token.put( "url", - awsCredentialSource.regionalCredentialVerificationUrl.replace( + this.getRegionalCredentialVerificationUrl().replace( "{region}", signature.getRegion())); return URLEncoder.encode(token.toString(), "UTF-8"); } @@ -218,7 +245,9 @@ private boolean canRetrieveSecurityCredentialsFromEnvironment() { @VisibleForTesting boolean shouldUseMetadataServer() { - return !canRetrieveRegionFromEnvironment() || !canRetrieveSecurityCredentialsFromEnvironment(); + return this.awsSecurityCredentialsSupplier == null && + (!canRetrieveRegionFromEnvironment() || + !canRetrieveSecurityCredentialsFromEnvironment()); } @VisibleForTesting @@ -258,6 +287,11 @@ Map createMetadataRequestHeaders(AwsCredentialSource awsCredenti @VisibleForTesting String getAwsRegion(Map metadataRequestHeaders) throws IOException { + // If user has provided a region string, return that instead of checking environment or metadata + // server. + if (this.region != null){ + return this.region; + } String region; if (canRetrieveRegionFromEnvironment()) { // For AWS Lambda, the region is retrieved through the AWS_REGION environment variable. @@ -283,6 +317,15 @@ String getAwsRegion(Map metadataRequestHeaders) throws IOExcepti @VisibleForTesting AwsSecurityCredentials getAwsSecurityCredentials(Map metadataRequestHeaders) throws IOException { + // If this credential is using programmatic auth, call the user provided supplier. + if (this.awsSecurityCredentialsSupplier != null){ + try { + return this.awsSecurityCredentialsSupplier.get(); + } catch (Throwable e){ + throw new GoogleAuthException(false, 0, "Error retrieving token from custom token supplier.", e); + } + } + // Check environment variables for credentials first. if (canRetrieveSecurityCredentialsFromEnvironment()) { String accessKeyId = getEnvironmentProvider().getEnv(AWS_ACCESS_KEY_ID); @@ -319,6 +362,19 @@ AwsSecurityCredentials getAwsSecurityCredentials(Map metadataReq return new AwsSecurityCredentials(accessKeyId, secretAccessKey, token); } + @VisibleForTesting + String getRegionalCredentialVerificationUrl(){ + if (this.regionalCredentialVerificationUrlOverride != null){ + return this.regionalCredentialVerificationUrlOverride; + } + else if (this.awsCredentialSource != null){ + return this.awsCredentialSource.regionalCredentialVerificationUrl; + } + else { + return DEFAULT_REGIONAL_CREDENTIAL_VERIFICATION_URL; + } + } + @VisibleForTesting String getEnv(String name) { return System.getenv(name); @@ -348,10 +404,57 @@ public static AwsCredentials.Builder newBuilder(AwsCredentials awsCredentials) { public static class Builder extends ExternalAccountCredentials.Builder { + private Supplier awsSecurityCredentialsSupplier; + + private String region; + + private String regionalCredentialVerificationUrlOverride; + Builder() {} Builder(AwsCredentials credentials) { super(credentials); + this.region = credentials.region; + this.awsSecurityCredentialsSupplier = credentials.awsSecurityCredentialsSupplier; + this.regionalCredentialVerificationUrlOverride = credentials.regionalCredentialVerificationUrlOverride; + } + + /** + * Sets the AWS security credentials supplier. The supplier should return a valid + * {@code AwsSecurityCredentials} object. An AWS region also is required when using a supplier. + * + * @param awsSecurityCredentialsSupplier the supplier method to be called. + * @return this {@code Builder} object + */ + public Builder setAwsSecurityCredentialsSupplier(Supplier awsSecurityCredentialsSupplier){ + this.awsSecurityCredentialsSupplier = awsSecurityCredentialsSupplier; + return this; + } + + /** + * Sets the AWS region. Required when using an AWS Security Credentials Supplier. If set, will + * override any region obtained via environment variables or the metadata endpoint. + * + * @param region the aws region to set. + * @return this {@code Builder} object + */ + public Builder setRegion(String region){ + this.region = region; + return this; + } + + /** + * Sets the AWS regional credential verification URL. If set, will override any credential + * verification URL provided in the credential source. If not set, the credential verification + * URL will default to + * https://sts.{region}.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15" + * + * @param regionalCredentialVerificationUrlOverride the AWS credential verification url to set. + * @return this {@code Builder} object + */ + public Builder setRegionalCredentialVerificationUrlOverride(String regionalCredentialVerificationUrlOverride){ + this.regionalCredentialVerificationUrlOverride = regionalCredentialVerificationUrlOverride; + return this; } @Override diff --git a/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java b/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java index 85ac86b4b..25c89d843 100644 --- a/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java @@ -81,6 +81,8 @@ abstract static class CredentialSource implements java.io.Serializable { static final String EXTERNAL_ACCOUNT_FILE_TYPE = "external_account"; static final String EXECUTABLE_SOURCE_KEY = "executable"; + static final String DEFAULT_TOKEN_URL = "https://sts.googleapis.com/v1/token"; + private final String transportFactoryClassName; private final String audience; private final String subjectTokenType; @@ -103,11 +105,7 @@ abstract static class CredentialSource implements java.io.Serializable { protected transient HttpTransportFactory transportFactory; - @Nullable protected final ImpersonatedCredentials impersonatedCredentials; - - // Internal override for impersonated credentials. This is done to keep - // impersonatedCredentials final. - @Nullable private ImpersonatedCredentials impersonatedCredentialsOverride; + @Nullable protected ImpersonatedCredentials impersonatedCredentials; private EnvironmentProvider environmentProvider; @@ -243,12 +241,13 @@ protected ExternalAccountCredentials(ExternalAccountCredentials.Builder builder) this.transportFactoryClassName = checkNotNull(this.transportFactory.getClass().getName()); this.audience = checkNotNull(builder.audience); this.subjectTokenType = checkNotNull(builder.subjectTokenType); - this.tokenUrl = checkNotNull(builder.tokenUrl); - this.credentialSource = checkNotNull(builder.credentialSource); + this.credentialSource = builder.credentialSource; this.tokenInfoUrl = builder.tokenInfoUrl; this.serviceAccountImpersonationUrl = builder.serviceAccountImpersonationUrl; this.clientId = builder.clientId; this.clientSecret = builder.clientSecret; + this.tokenUrl = builder.tokenUrl == null + ? DEFAULT_TOKEN_URL : builder.tokenUrl; this.scopes = (builder.scopes == null || builder.scopes.isEmpty()) ? Arrays.asList(CLOUD_PLATFORM_SCOPE) @@ -279,8 +278,6 @@ protected ExternalAccountCredentials(ExternalAccountCredentials.Builder builder) builder.metricsHandler == null ? new ExternalAccountMetricsHandler(this) : builder.metricsHandler; - - this.impersonatedCredentials = buildImpersonatedCredentials(); } ImpersonatedCredentials buildImpersonatedCredentials() { @@ -318,10 +315,6 @@ ImpersonatedCredentials buildImpersonatedCredentials() { .build(); } - void overrideImpersonatedCredentials(ImpersonatedCredentials credentials) { - this.impersonatedCredentialsOverride = credentials; - } - @Override public void getRequestMetadata( URI uri, Executor executor, final RequestMetadataCallback callback) { @@ -490,10 +483,9 @@ private static boolean isAwsCredential(Map credentialSource) { protected AccessToken exchangeExternalCredentialForAccessToken( StsTokenExchangeRequest stsTokenExchangeRequest) throws IOException { // Handle service account impersonation if necessary. - // Internal override takes priority. - if (impersonatedCredentialsOverride != null) { - return impersonatedCredentialsOverride.refreshAccessToken(); - } else if (impersonatedCredentials != null) { + this.impersonatedCredentials = (this.impersonatedCredentials != null) ? + this.impersonatedCredentials : this.buildImpersonatedCredentials(); + if (impersonatedCredentials != null) { return impersonatedCredentials.refreshAccessToken(); } diff --git a/oauth2_http/java/com/google/auth/oauth2/ExternalAccountMetricsHandler.java b/oauth2_http/java/com/google/auth/oauth2/ExternalAccountMetricsHandler.java index fcb656b5d..18fc124b8 100644 --- a/oauth2_http/java/com/google/auth/oauth2/ExternalAccountMetricsHandler.java +++ b/oauth2_http/java/com/google/auth/oauth2/ExternalAccountMetricsHandler.java @@ -43,7 +43,7 @@ class ExternalAccountMetricsHandler implements java.io.Serializable { private final boolean configLifetime; private final boolean saImpersonation; - private String credentialSourceType; + private ExternalAccountCredentials credentials; /** * Constructor for the external account metrics handler. @@ -55,7 +55,7 @@ class ExternalAccountMetricsHandler implements java.io.Serializable { this.saImpersonation = creds.getServiceAccountImpersonationUrl() != null; this.configLifetime = creds.getServiceAccountImpersonationOptions().customTokenLifetimeRequested; - this.credentialSourceType = creds.getCredentialSourceType(); + this.credentials = creds; } /** @@ -69,7 +69,7 @@ String getExternalAccountMetricsHeader() { MetricsUtils.getLanguageAndAuthLibraryVersions(), BYOID_METRICS_SECTION, SOURCE_KEY, - this.credentialSourceType, + this.credentials.getCredentialSourceType(), IMPERSONATION_KEY, this.saImpersonation, CONFIG_LIFETIME_KEY, diff --git a/oauth2_http/java/com/google/auth/oauth2/IdentityPoolCredentials.java b/oauth2_http/java/com/google/auth/oauth2/IdentityPoolCredentials.java index bb745ff55..45a8a40c5 100644 --- a/oauth2_http/java/com/google/auth/oauth2/IdentityPoolCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/IdentityPoolCredentials.java @@ -52,6 +52,8 @@ import java.nio.file.Paths; import java.util.ArrayList; import java.util.Collection; +import java.util.function.Supplier; +import javax.annotation.Nullable; /** * Url-sourced and file-sourced external account credentials. @@ -62,12 +64,22 @@ public class IdentityPoolCredentials extends ExternalAccountCredentials { private static final long serialVersionUID = 2471046175477275881L; + @Nullable private final IdentityPoolCredentialSource identityPoolCredentialSource; + @Nullable + private final Supplier subjectTokenSupplier; + /** Internal constructor. See {@link Builder}. */ IdentityPoolCredentials(Builder builder) { super(builder); - this.identityPoolCredentialSource = (IdentityPoolCredentialSource) builder.credentialSource; + if (builder.subjectTokenSupplier != null){ + this.subjectTokenSupplier = builder.subjectTokenSupplier; + this.identityPoolCredentialSource = null; + } else { + this.identityPoolCredentialSource = (IdentityPoolCredentialSource) builder.credentialSource; + this.subjectTokenSupplier = null; + } } @Override @@ -87,7 +99,14 @@ public AccessToken refreshAccessToken() throws IOException { @Override public String retrieveSubjectToken() throws IOException { - if (identityPoolCredentialSource.credentialSourceType + if (this.subjectTokenSupplier != null){ + try { + return this.subjectTokenSupplier.get(); + } catch (Throwable e){ + throw new GoogleAuthException(false, 0, "Error retrieving token from custom token supplier.", e); + } + } + else if (identityPoolCredentialSource.credentialSourceType == IdentityPoolCredentialSource.IdentityPoolCredentialSourceType.FILE) { return retrieveSubjectTokenFromCredentialFile(); } @@ -96,6 +115,9 @@ public String retrieveSubjectToken() throws IOException { @Override String getCredentialSourceType() { + if (this.subjectTokenSupplier != null){ + return "programmatic"; + } if (((IdentityPoolCredentialSource) this.getCredentialSource()).credentialSourceType == IdentityPoolCredentialSourceType.FILE) { return "file"; @@ -176,6 +198,8 @@ public static Builder newBuilder(IdentityPoolCredentials identityPoolCredentials public static class Builder extends ExternalAccountCredentials.Builder { + private Supplier subjectTokenSupplier; + Builder() {} Builder(IdentityPoolCredentials credentials) { @@ -187,6 +211,17 @@ public Builder setWorkforcePoolUserProject(String workforcePoolUserProject) { return this; } + /** + * Sets the subject token supplier. The supplier should return a valid subject token string. + * + * @param subjectTokenSupplier the supplier method to be called. + * @return this {@code Builder} object + */ + public Builder setSubjectTokenSupplier(Supplier subjectTokenSupplier) { + this.subjectTokenSupplier = subjectTokenSupplier; + return this; + } + @Override public IdentityPoolCredentials build() { return new IdentityPoolCredentials(this); diff --git a/oauth2_http/java/com/google/auth/oauth2/PluggableAuthCredentials.java b/oauth2_http/java/com/google/auth/oauth2/PluggableAuthCredentials.java index 84eb6dc55..ac27d77fc 100644 --- a/oauth2_http/java/com/google/auth/oauth2/PluggableAuthCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/PluggableAuthCredentials.java @@ -110,10 +110,6 @@ public class PluggableAuthCredentials extends ExternalAccountCredentials { } else { handler = new PluggableAuthHandler(getEnvironmentProvider()); } - - // Re-initialize impersonated credentials as the handler hasn't been set yet when - // this is called in the base class. - overrideImpersonatedCredentials(buildImpersonatedCredentials()); } @Override diff --git a/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java index b1e384020..91592c3d1 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java @@ -54,6 +54,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.function.Supplier; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -68,6 +69,8 @@ public class AwsCredentialsTest extends BaseSerializationTest { private static final String AWS_REGION_URL = "https://169.254.169.254/region"; private static final String AWS_IMDSV2_SESSION_TOKEN_URL = "https://169.254.169.254/imdsv2"; private static final String AWS_IMDSV2_SESSION_TOKEN = "sessiontoken"; + private static final String DEFAULT_REGIONAL_CREDENTIAL_VERIFICATION_URL = + "https://sts.{region}.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15"; private static final String GET_CALLER_IDENTITY_URL = "https://sts.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15"; @@ -102,6 +105,15 @@ public class AwsCredentialsTest extends BaseSerializationTest { .setCredentialSource(AWS_CREDENTIAL_SOURCE) .build(); + private static final AwsSecurityCredentials ProgrammaticAwsCreds = + new AwsSecurityCredentials( + "testAccessKey", + "testSecretAccessKey", + null); + + private static final Supplier CredentialSupplier = + () -> ProgrammaticAwsCreds; + @Test public void test_awsCredentialSource() { String keys[] = {"region_url", "url", "imdsv2_session_token_url"}; @@ -209,6 +221,58 @@ public void refreshAccessToken_withServiceAccountImpersonationOptions() throws I ExternalAccountCredentialsTest.validateMetricsHeader(headers, "aws", true, true); } + @Test + public void refreshAccessTokenProgrammaticRefresh_withoutServiceAccountImpersonation() throws IOException { + MockExternalAccountCredentialsTransportFactory transportFactory = + new MockExternalAccountCredentialsTransportFactory(); + + AwsCredentials awsCredential = (AwsCredentials) AwsCredentials.newBuilder() + .setRegion("test") + .setAwsSecurityCredentialsSupplier(CredentialSupplier) + .setHttpTransportFactory(transportFactory) + .setAudience("audience") + .setTokenUrl(STS_URL) + .setSubjectTokenType("subjectTokenType") + .build(); + + AccessToken accessToken = awsCredential.refreshAccessToken(); + + assertEquals(transportFactory.transport.getAccessToken(), accessToken.getTokenValue()); + + // Validate metrics header is set correctly on the sts request. + Map> headers = + transportFactory.transport.getRequests().get(0).getHeaders(); + ExternalAccountCredentialsTest.validateMetricsHeader(headers, "programmatic", false, false); + } + + @Test + public void refreshAccessTokenProgrammaticRefresh_withServiceAccountImpersonation() throws IOException { + MockExternalAccountCredentialsTransportFactory transportFactory = + new MockExternalAccountCredentialsTransportFactory(); + + transportFactory.transport.setExpireTime(TestUtils.getDefaultExpireTime()); + + AwsCredentials awsCredential = (AwsCredentials) AwsCredentials.newBuilder() + .setRegion("test") + .setAwsSecurityCredentialsSupplier(CredentialSupplier) + .setHttpTransportFactory(transportFactory) + .setAudience("audience") + .setTokenUrl(STS_URL) + .setSubjectTokenType("subjectTokenType") + .setServiceAccountImpersonationUrl( + transportFactory.transport.getServiceAccountImpersonationUrl()) + .build(); + + AccessToken accessToken = awsCredential.refreshAccessToken(); + + assertEquals(transportFactory.transport.getServiceAccountAccessToken(), accessToken.getTokenValue()); + + // Validate metrics header is set correctly on the sts request. + Map> headers = + transportFactory.transport.getRequests().get(0).getHeaders(); + ExternalAccountCredentialsTest.validateMetricsHeader(headers, "programmatic", true, false); + } + @Test public void retrieveSubjectToken() throws IOException { MockExternalAccountCredentialsTransportFactory transportFactory = @@ -539,6 +603,85 @@ public void retrieveSubjectToken_noRegionUrlProvided() { assertTrue(requests.isEmpty()); } + @Test + public void retrieveSubjectToken_withProgrammaticRefresh() throws IOException { + MockExternalAccountCredentialsTransportFactory transportFactory = + new MockExternalAccountCredentialsTransportFactory(); + + AwsCredentials awsCredential = (AwsCredentials) AwsCredentials.newBuilder() + .setRegion("test") + .setAwsSecurityCredentialsSupplier(CredentialSupplier) + .setHttpTransportFactory(transportFactory) + .setAudience("audience") + .setTokenUrl(STS_URL) + .setSubjectTokenType("subjectTokenType") + .build(); + + String subjectToken = URLDecoder.decode(awsCredential.retrieveSubjectToken(), "UTF-8"); + + JsonParser parser = OAuth2Utils.JSON_FACTORY.createJsonParser(subjectToken); + GenericJson json = parser.parseAndClose(GenericJson.class); + + List> headersList = (List>) json.get("headers"); + Map headers = new HashMap<>(); + for (Map header : headersList) { + headers.put(header.get("key"), header.get("value")); + } + + String expectedCredentialVerificationUrl = DEFAULT_REGIONAL_CREDENTIAL_VERIFICATION_URL.replace( + "{region}", "test"); + + assertEquals("POST", json.get("method")); + assertEquals(expectedCredentialVerificationUrl, json.get("url")); + assertEquals(URI.create(expectedCredentialVerificationUrl).getHost(), headers.get("host")); + assertEquals(awsCredential.getAudience(), headers.get("x-goog-cloud-target-resource")); + assertTrue(headers.containsKey("x-amz-date")); + assertNotNull(headers.get("Authorization")); + } + + @Test + public void retrieveSubjectToken_withProgrammaticRefreshSessionToken() throws IOException { + MockExternalAccountCredentialsTransportFactory transportFactory = + new MockExternalAccountCredentialsTransportFactory(); + + AwsSecurityCredentials securityCredentialsWithToken = + new AwsSecurityCredentials("accessToken", "secretAccessKey", "token"); + + Supplier awsSecurityCredentialsSupplierWithToken + = () -> securityCredentialsWithToken; + + AwsCredentials awsCredential = (AwsCredentials) AwsCredentials.newBuilder() + .setRegion("test") + .setAwsSecurityCredentialsSupplier(awsSecurityCredentialsSupplierWithToken) + .setHttpTransportFactory(transportFactory) + .setAudience("audience") + .setTokenUrl(STS_URL) + .setSubjectTokenType("subjectTokenType") + .build(); + + String subjectToken = URLDecoder.decode(awsCredential.retrieveSubjectToken(), "UTF-8"); + + JsonParser parser = OAuth2Utils.JSON_FACTORY.createJsonParser(subjectToken); + GenericJson json = parser.parseAndClose(GenericJson.class); + + List> headersList = (List>) json.get("headers"); + Map headers = new HashMap<>(); + for (Map header : headersList) { + headers.put(header.get("key"), header.get("value")); + } + + String expectedCredentialVerificationUrl = DEFAULT_REGIONAL_CREDENTIAL_VERIFICATION_URL.replace( + "{region}", "test"); + + assertEquals("POST", json.get("method")); + assertEquals(expectedCredentialVerificationUrl, json.get("url")); + assertEquals(URI.create(expectedCredentialVerificationUrl).getHost(), headers.get("host")); + assertEquals("token", headers.get("x-amz-security-token")); + assertEquals(awsCredential.getAudience(), headers.get("x-goog-cloud-target-resource")); + assertTrue(headers.containsKey("x-amz-date")); + assertNotNull(headers.get("Authorization")); + } + @Test public void getAwsSecurityCredentials_fromEnvironmentVariablesNoToken() throws IOException { TestEnvironmentProvider environmentProvider = new TestEnvironmentProvider(); diff --git a/oauth2_http/javatests/com/google/auth/oauth2/ExternalAccountCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/ExternalAccountCredentialsTest.java index ec558cfa8..01c676d42 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/ExternalAccountCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/ExternalAccountCredentialsTest.java @@ -999,39 +999,6 @@ public void exchangeExternalCredentialForAccessToken_withServiceAccountImpersona assertEquals("2800s", query.get("lifetime")); } - @Test - public void exchangeExternalCredentialForAccessToken_withServiceAccountImpersonationOverride() - throws IOException { - transportFactory.transport.setExpireTime(TestUtils.getDefaultExpireTime()); - - String serviceAccountEmail = "different@different.iam.gserviceaccount.com"; - ExternalAccountCredentials credential = - ExternalAccountCredentials.fromStream( - IdentityPoolCredentialsTest.writeIdentityPoolCredentialsStream( - transportFactory.transport.getStsUrl(), - transportFactory.transport.getMetadataUrl(), - transportFactory.transport.getServiceAccountImpersonationUrl(), - /* serviceAccountImpersonationOptionsMap= */ null), - transportFactory); - - // Override impersonated credentials. - ExternalAccountCredentials sourceCredentials = - IdentityPoolCredentials.newBuilder((IdentityPoolCredentials) credential) - .setServiceAccountImpersonationUrl(null) - .build(); - credential.overrideImpersonatedCredentials( - new ImpersonatedCredentials.Builder(sourceCredentials, serviceAccountEmail) - .setScopes(new ArrayList<>(sourceCredentials.getScopes())) - .setHttpTransportFactory(transportFactory) - .build()); - - credential.exchangeExternalCredentialForAccessToken( - StsTokenExchangeRequest.newBuilder("credential", "subjectTokenType").build()); - - assertTrue( - transportFactory.transport.getRequests().get(2).getUrl().contains(serviceAccountEmail)); - } - @Test public void exchangeExternalCredentialForAccessToken_throws() throws IOException { ExternalAccountCredentials credential = From e6457d9baf3ad321f58c757633addfa442e5bf14 Mon Sep 17 00:00:00 2001 From: aeitzman Date: Mon, 27 Nov 2023 00:08:31 -0800 Subject: [PATCH 02/32] feat: add quality of life improvements for building external account credentials in code. --- .../google/auth/oauth2/AwsCredentials.java | 81 +++++++++++++++++++ .../oauth2/ExternalAccountCredentials.java | 27 +++++++ .../auth/oauth2/IdentityPoolCredentials.java | 79 ++++++++++++++++++ .../auth/oauth2/PluggableAuthCredentials.java | 80 ++++++++++++++++++ .../auth/oauth2/AwsCredentialsTest.java | 29 ------- .../ExternalAccountCredentialsTest.java | 19 +++++ .../oauth2/IdentityPoolCredentialsTest.java | 17 ---- .../oauth2/PluggableAuthCredentialsTest.java | 15 +--- 8 files changed, 289 insertions(+), 58 deletions(-) diff --git a/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java b/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java index e47bbe167..596e4da5a 100644 --- a/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java @@ -40,6 +40,7 @@ import com.google.api.client.http.HttpResponse; import com.google.api.client.json.GenericJson; import com.google.api.client.json.JsonParser; +import com.google.auth.http.HttpTransportFactory; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableList; import java.io.IOException; @@ -354,6 +355,86 @@ public static class Builder extends ExternalAccountCredentials.Builder { super(credentials); } + public Builder setHttpTransportFactory(HttpTransportFactory transportFactory) { + super.setHttpTransportFactory(transportFactory); + return this; + } + + public Builder setAudience(String audience) { + super.setAudience(audience); + return this; + } + + public Builder setSubjectTokenType(String subjectTokenType) { + super.setSubjectTokenType(subjectTokenType); + return this; + } + + public Builder setSubjectTokenType(SubjectTokenTypes subjectTokenType) { + super.setSubjectTokenType(subjectTokenType); + return this; + } + + public Builder setTokenUrl(String tokenUrl) { + super.setTokenUrl(tokenUrl); + return this; + } + + public Builder setCredentialSource(AwsCredentialSource credentialSource) { + super.setCredentialSource(credentialSource); + return this; + } + + public Builder setServiceAccountImpersonationUrl(String serviceAccountImpersonationUrl) { + super.setServiceAccountImpersonationUrl(serviceAccountImpersonationUrl); + return this; + } + + public Builder setTokenInfoUrl(String tokenInfoUrl) { + super.setTokenInfoUrl(tokenInfoUrl); + return this; + } + + public Builder setQuotaProjectId(String quotaProjectId) { + super.setQuotaProjectId(quotaProjectId); + return this; + } + + public Builder setClientId(String clientId) { + super.setClientId(clientId); + return this; + } + + public Builder setClientSecret(String clientSecret) { + super.setClientSecret(clientSecret); + return this; + } + + public Builder setScopes(Collection scopes) { + super.setScopes(scopes); + return this; + } + + public Builder setWorkforcePoolUserProject(String workforcePoolUserProject) { + super.setWorkforcePoolUserProject(workforcePoolUserProject); + return this; + } + + public Builder setServiceAccountImpersonationOptions(Map optionsMap) { + super.setServiceAccountImpersonationOptions(optionsMap); + return this; + } + + public Builder setUniverseDomain(String universeDomain) { + super.setUniverseDomain(universeDomain); + return this; + } + + Builder setEnvironmentProvider(EnvironmentProvider environmentProvider) { + super.setEnvironmentProvider(environmentProvider); + return this; + } + @Override public AwsCredentials build() { return new AwsCredentials(this); diff --git a/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java b/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java index 85ac86b4b..eb80e384c 100644 --- a/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java @@ -63,6 +63,21 @@ */ public abstract class ExternalAccountCredentials extends GoogleCredentials { + /** + * Enum to specify values for the subjectTokenType field in {@code ExternalAccountCredentials}. + */ + public enum SubjectTokenTypes{ + AWS4("urn:ietf:params:aws:token-type:aws4_request"), + JWT("urn:ietf:params:oauth:token-type:jwt"), + SAML2("urn:ietf:params:oauth:token-type:saml2"); + + public final String value; + + private SubjectTokenTypes(String value) { + this.value = value; + } + } + private static final long serialVersionUID = 8049126194174465023L; /** Base credential source class. Dictates the retrieval method of the external credential. */ @@ -791,6 +806,18 @@ public Builder setSubjectTokenType(String subjectTokenType) { return this; } + /** + * Sets the Security Token Service subject token type based on the OAuth 2.0 token exchange + * spec. Indicates the type of the security token in the credential file. + * + * @param subjectTokenType the {@code SubjectTokenType} to set + * @return this {@code Builder} object + */ + public Builder setSubjectTokenType(SubjectTokenTypes subjectTokenType) { + this.subjectTokenType = subjectTokenType.value; + return this; + } + /** * Sets the Security Token Service token exchange endpoint. * diff --git a/oauth2_http/java/com/google/auth/oauth2/IdentityPoolCredentials.java b/oauth2_http/java/com/google/auth/oauth2/IdentityPoolCredentials.java index bb745ff55..b21078ad3 100644 --- a/oauth2_http/java/com/google/auth/oauth2/IdentityPoolCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/IdentityPoolCredentials.java @@ -37,6 +37,7 @@ import com.google.api.client.http.HttpResponse; import com.google.api.client.json.GenericJson; import com.google.api.client.json.JsonObjectParser; +import com.google.auth.http.HttpTransportFactory; import com.google.auth.oauth2.IdentityPoolCredentialSource.CredentialFormatType; import com.google.auth.oauth2.IdentityPoolCredentialSource.IdentityPoolCredentialSourceType; import com.google.common.io.CharStreams; @@ -52,6 +53,7 @@ import java.nio.file.Paths; import java.util.ArrayList; import java.util.Collection; +import java.util.Map; /** * Url-sourced and file-sourced external account credentials. @@ -182,11 +184,88 @@ public static class Builder extends ExternalAccountCredentials.Builder { super(credentials); } + + public Builder setHttpTransportFactory(HttpTransportFactory transportFactory) { + super.setHttpTransportFactory(transportFactory); + return this; + } + + public Builder setAudience(String audience) { + super.setAudience(audience); + return this; + } + + public Builder setSubjectTokenType(String subjectTokenType) { + super.setSubjectTokenType(subjectTokenType); + return this; + } + + public Builder setSubjectTokenType(SubjectTokenTypes subjectTokenType) { + super.setSubjectTokenType(subjectTokenType); + return this; + } + + public Builder setTokenUrl(String tokenUrl) { + super.setTokenUrl(tokenUrl); + return this; + } + + public Builder setCredentialSource(IdentityPoolCredentialSource credentialSource) { + super.setCredentialSource(credentialSource); + return this; + } + + public Builder setServiceAccountImpersonationUrl(String serviceAccountImpersonationUrl) { + super.setServiceAccountImpersonationUrl(serviceAccountImpersonationUrl); + return this; + } + + public Builder setTokenInfoUrl(String tokenInfoUrl) { + super.setTokenInfoUrl(tokenInfoUrl); + return this; + } + + public Builder setQuotaProjectId(String quotaProjectId) { + super.setQuotaProjectId(quotaProjectId); + return this; + } + + public Builder setClientId(String clientId) { + super.setClientId(clientId); + return this; + } + + public Builder setClientSecret(String clientSecret) { + super.setClientSecret(clientSecret); + return this; + } + + public Builder setScopes(Collection scopes) { + super.setScopes(scopes); + return this; + } + public Builder setWorkforcePoolUserProject(String workforcePoolUserProject) { super.setWorkforcePoolUserProject(workforcePoolUserProject); return this; } + public Builder setServiceAccountImpersonationOptions( + Map optionsMap) { + super.setServiceAccountImpersonationOptions(optionsMap); + return this; + } + + public Builder setUniverseDomain(String universeDomain) { + super.setUniverseDomain(universeDomain); + return this; + } + + Builder setEnvironmentProvider(EnvironmentProvider environmentProvider) { + super.setEnvironmentProvider(environmentProvider); + return this; + } + @Override public IdentityPoolCredentials build() { return new IdentityPoolCredentials(this); diff --git a/oauth2_http/java/com/google/auth/oauth2/PluggableAuthCredentials.java b/oauth2_http/java/com/google/auth/oauth2/PluggableAuthCredentials.java index 84eb6dc55..26f9b9f48 100644 --- a/oauth2_http/java/com/google/auth/oauth2/PluggableAuthCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/PluggableAuthCredentials.java @@ -31,6 +31,7 @@ package com.google.auth.oauth2; +import com.google.auth.http.HttpTransportFactory; import com.google.auth.oauth2.ExecutableHandler.ExecutableOptions; import com.google.common.annotations.VisibleForTesting; import java.io.IOException; @@ -224,6 +225,85 @@ public Builder setExecutableHandler(ExecutableHandler handler) { return this; } + public Builder setHttpTransportFactory(HttpTransportFactory transportFactory) { + super.setHttpTransportFactory(transportFactory); + return this; + } + + public Builder setAudience(String audience) { + super.setAudience(audience); + return this; + } + + public Builder setSubjectTokenType(String subjectTokenType) { + super.setSubjectTokenType(subjectTokenType); + return this; + } + + public Builder setSubjectTokenType(SubjectTokenTypes subjectTokenType) { + super.setSubjectTokenType(subjectTokenType); + return this; + } + + public Builder setTokenUrl(String tokenUrl) { + super.setTokenUrl(tokenUrl); + return this; + } + + public Builder setCredentialSource(PluggableAuthCredentialSource credentialSource) { + super.setCredentialSource(credentialSource); + return this; + } + + public Builder setServiceAccountImpersonationUrl(String serviceAccountImpersonationUrl) { + super.setServiceAccountImpersonationUrl(serviceAccountImpersonationUrl); + return this; + } + + public Builder setTokenInfoUrl(String tokenInfoUrl) { + super.setTokenInfoUrl(tokenInfoUrl); + return this; + } + + public Builder setQuotaProjectId(String quotaProjectId) { + super.setQuotaProjectId(quotaProjectId); + return this; + } + + public Builder setClientId(String clientId) { + super.setClientId(clientId); + return this; + } + + public Builder setClientSecret(String clientSecret) { + super.setClientSecret(clientSecret); + return this; + } + + public Builder setScopes(Collection scopes) { + super.setScopes(scopes); + return this; + } + + public Builder setWorkforcePoolUserProject(String workforcePoolUserProject) { + super.setWorkforcePoolUserProject(workforcePoolUserProject); + return this; + } + + public Builder setServiceAccountImpersonationOptions(Map optionsMap) { + super.setServiceAccountImpersonationOptions(optionsMap); + return this; + } + + public Builder setUniverseDomain(String universeDomain) { + super.setUniverseDomain(universeDomain); + return this; + } + + Builder setEnvironmentProvider(EnvironmentProvider environmentProvider) { + super.setEnvironmentProvider(environmentProvider); + return this; + } @Override public PluggableAuthCredentials build() { return new PluggableAuthCredentials(this); diff --git a/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java index b1e384020..f0781e175 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java @@ -92,7 +92,6 @@ public class AwsCredentialsTest extends BaseSerializationTest { new AwsCredentialSource(AWS_CREDENTIAL_SOURCE_MAP); private static final AwsCredentials AWS_CREDENTIAL = - (AwsCredentials) AwsCredentials.newBuilder() .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) .setAudience("audience") @@ -120,7 +119,6 @@ public void refreshAccessToken_withoutServiceAccountImpersonation() throws IOExc new MockExternalAccountCredentialsTransportFactory(); AwsCredentials awsCredential = - (AwsCredentials) AwsCredentials.newBuilder(AWS_CREDENTIAL) .setTokenUrl(transportFactory.transport.getStsUrl()) .setHttpTransportFactory(transportFactory) @@ -145,7 +143,6 @@ public void refreshAccessToken_withServiceAccountImpersonation() throws IOExcept transportFactory.transport.setExpireTime(TestUtils.getDefaultExpireTime()); AwsCredentials awsCredential = - (AwsCredentials) AwsCredentials.newBuilder() .setHttpTransportFactory(transportFactory) .setAudience("audience") @@ -176,7 +173,6 @@ public void refreshAccessToken_withServiceAccountImpersonationOptions() throws I transportFactory.transport.setExpireTime(TestUtils.getDefaultExpireTime()); AwsCredentials awsCredential = - (AwsCredentials) AwsCredentials.newBuilder() .setHttpTransportFactory(transportFactory) .setAudience("audience") @@ -215,7 +211,6 @@ public void retrieveSubjectToken() throws IOException { new MockExternalAccountCredentialsTransportFactory(); AwsCredentials awsCredential = - (AwsCredentials) AwsCredentials.newBuilder(AWS_CREDENTIAL) .setHttpTransportFactory(transportFactory) .setCredentialSource(buildAwsCredentialSource(transportFactory)) @@ -259,7 +254,6 @@ public void retrieveSubjectTokenWithSessionTokenUrl() throws IOException { new MockExternalAccountCredentialsTransportFactory(); AwsCredentials awsCredential = - (AwsCredentials) AwsCredentials.newBuilder(AWS_CREDENTIAL) .setHttpTransportFactory(transportFactory) .setCredentialSource(buildAwsImdsv2CredentialSource(transportFactory)) @@ -331,7 +325,6 @@ public void retrieveSubjectToken_imdsv1EnvVariablesSet_metadataServerNotCalled() .setEnv("AWS_SESSION_TOKEN", "awsToken"); AwsCredentials awsCredential = - (AwsCredentials) AwsCredentials.newBuilder(AWS_CREDENTIAL) .setHttpTransportFactory(transportFactory) .setCredentialSource(buildAwsCredentialSource(transportFactory)) @@ -378,7 +371,6 @@ public void retrieveSubjectToken_imdsv2EnvVariablesSet_metadataServerNotCalled() .setEnv("AWS_SESSION_TOKEN", "awsToken"); AwsCredentials awsCredential = - (AwsCredentials) AwsCredentials.newBuilder(AWS_CREDENTIAL) .setHttpTransportFactory(transportFactory) .setCredentialSource(buildAwsImdsv2CredentialSource(transportFactory)) @@ -419,7 +411,6 @@ public void retrieveSubjectToken_noRegion_expectThrows() { transportFactory.transport.addResponseErrorSequence(response); AwsCredentials awsCredential = - (AwsCredentials) AwsCredentials.newBuilder(AWS_CREDENTIAL) .setHttpTransportFactory(transportFactory) .setCredentialSource(buildAwsCredentialSource(transportFactory)) @@ -449,7 +440,6 @@ public void retrieveSubjectToken_noRole_expectThrows() { transportFactory.transport.addResponseSequence(true, false); AwsCredentials awsCredential = - (AwsCredentials) AwsCredentials.newBuilder(AWS_CREDENTIAL) .setHttpTransportFactory(transportFactory) .setCredentialSource(buildAwsCredentialSource(transportFactory)) @@ -482,7 +472,6 @@ public void retrieveSubjectToken_noCredentials_expectThrows() { transportFactory.transport.addResponseSequence(true, true, false); AwsCredentials awsCredential = - (AwsCredentials) AwsCredentials.newBuilder(AWS_CREDENTIAL) .setHttpTransportFactory(transportFactory) .setCredentialSource(buildAwsCredentialSource(transportFactory)) @@ -518,7 +507,6 @@ public void retrieveSubjectToken_noRegionUrlProvided() { credentialSource.put("regional_cred_verification_url", GET_CALLER_IDENTITY_URL); AwsCredentials awsCredential = - (AwsCredentials) AwsCredentials.newBuilder(AWS_CREDENTIAL) .setHttpTransportFactory(transportFactory) .setCredentialSource(new AwsCredentialSource(credentialSource)) @@ -547,7 +535,6 @@ public void getAwsSecurityCredentials_fromEnvironmentVariablesNoToken() throws I .setEnv("AWS_SECRET_ACCESS_KEY", "awsSecretAccessKey"); AwsCredentials testAwsCredentials = - (AwsCredentials) AwsCredentials.newBuilder(AWS_CREDENTIAL) .setEnvironmentProvider(environmentProvider) .build(); @@ -580,7 +567,6 @@ public void getAwsSecurityCredentials_fromEnvironmentVariablesWithToken() throws }); AwsCredentials testAwsCredentials = - (AwsCredentials) AwsCredentials.newBuilder(AWS_CREDENTIAL) .setEnvironmentProvider(environmentProvider) .setCredentialSource(credSource) @@ -604,7 +590,6 @@ public void getAwsSecurityCredentials_fromEnvironmentVariables_noMetadataServerC .setEnv("AWS_SESSION_TOKEN", "awsSessionToken"); AwsCredentials testAwsCredentials = - (AwsCredentials) AwsCredentials.newBuilder(AWS_CREDENTIAL) .setEnvironmentProvider(environmentProvider) .build(); @@ -623,7 +608,6 @@ public void getAwsSecurityCredentials_fromMetadataServer() throws IOException { new MockExternalAccountCredentialsTransportFactory(); AwsCredentials awsCredential = - (AwsCredentials) AwsCredentials.newBuilder(AWS_CREDENTIAL) .setHttpTransportFactory(transportFactory) .setCredentialSource(buildAwsCredentialSource(transportFactory)) @@ -656,7 +640,6 @@ public void getAwsSecurityCredentials_fromMetadataServer_noUrlProvided() { credentialSource.put("regional_cred_verification_url", GET_CALLER_IDENTITY_URL); AwsCredentials awsCredential = - (AwsCredentials) AwsCredentials.newBuilder(AWS_CREDENTIAL) .setHttpTransportFactory(transportFactory) .setCredentialSource(new AwsCredentialSource(credentialSource)) @@ -685,7 +668,6 @@ public void getAwsRegion_awsRegionEnvironmentVariable() throws IOException { MockExternalAccountCredentialsTransportFactory transportFactory = new MockExternalAccountCredentialsTransportFactory(); AwsCredentials awsCredentials = - (AwsCredentials) AwsCredentials.newBuilder(AWS_CREDENTIAL) .setHttpTransportFactory(transportFactory) .setCredentialSource(buildAwsCredentialSource(transportFactory)) @@ -711,7 +693,6 @@ public void getAwsRegion_awsDefaultRegionEnvironmentVariable() throws IOExceptio MockExternalAccountCredentialsTransportFactory transportFactory = new MockExternalAccountCredentialsTransportFactory(); AwsCredentials awsCredentials = - (AwsCredentials) AwsCredentials.newBuilder(AWS_CREDENTIAL) .setHttpTransportFactory(transportFactory) .setCredentialSource(buildAwsCredentialSource(transportFactory)) @@ -734,7 +715,6 @@ public void getAwsRegion_metadataServer() throws IOException { MockExternalAccountCredentialsTransportFactory transportFactory = new MockExternalAccountCredentialsTransportFactory(); AwsCredentials awsCredentials = - (AwsCredentials) AwsCredentials.newBuilder(AWS_CREDENTIAL) .setHttpTransportFactory(transportFactory) .setCredentialSource(buildAwsCredentialSource(transportFactory)) @@ -760,7 +740,6 @@ public void getAwsRegion_metadataServer() throws IOException { @Test public void createdScoped_clonedCredentialWithAddedScopes() { AwsCredentials credentials = - (AwsCredentials) AwsCredentials.newBuilder(AWS_CREDENTIAL) .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) .setQuotaProjectId("quotaProjectId") @@ -848,7 +827,6 @@ public void shouldUseMetadataServer_withRequiredEnvironmentVariables() { .setEnv("AWS_ACCESS_KEY_ID", "awsAccessKeyId") .setEnv("AWS_SECRET_ACCESS_KEY", "awsSecretAccessKey"); AwsCredentials awsCredential = - (AwsCredentials) AwsCredentials.newBuilder(AWS_CREDENTIAL) .setHttpTransportFactory(transportFactory) .setCredentialSource(buildAwsImdsv2CredentialSource(transportFactory)) @@ -868,7 +846,6 @@ public void shouldUseMetadataServer_missingRegion() { .setEnv("AWS_ACCESS_KEY_ID", "awsAccessKeyId") .setEnv("AWS_SECRET_ACCESS_KEY", "awsSecretAccessKey"); AwsCredentials awsCredential = - (AwsCredentials) AwsCredentials.newBuilder(AWS_CREDENTIAL) .setHttpTransportFactory(transportFactory) .setCredentialSource(buildAwsImdsv2CredentialSource(transportFactory)) @@ -891,7 +868,6 @@ public void shouldUseMetadataServer_missingAwsAccessKeyId() { .setEnv(regionKey, "awsRegion") .setEnv("AWS_SECRET_ACCESS_KEY", "awsSecretAccessKey"); AwsCredentials awsCredential = - (AwsCredentials) AwsCredentials.newBuilder(AWS_CREDENTIAL) .setHttpTransportFactory(transportFactory) .setCredentialSource(buildAwsImdsv2CredentialSource(transportFactory)) @@ -915,7 +891,6 @@ public void shouldUseMetadataServer_missingAwsSecretAccessKey() { .setEnv(regionKey, "awsRegion") .setEnv("AWS_ACCESS_KEY_ID", "awsAccessKeyId"); AwsCredentials awsCredential = - (AwsCredentials) AwsCredentials.newBuilder(AWS_CREDENTIAL) .setHttpTransportFactory(transportFactory) .setCredentialSource(buildAwsImdsv2CredentialSource(transportFactory)) @@ -938,7 +913,6 @@ public void shouldUseMetadataServer_missingAwsSecurityCreds() { // Not set here. environmentProvider.setEnv(regionKey, "awsRegion"); AwsCredentials awsCredential = - (AwsCredentials) AwsCredentials.newBuilder(AWS_CREDENTIAL) .setHttpTransportFactory(transportFactory) .setCredentialSource(buildAwsImdsv2CredentialSource(transportFactory)) @@ -953,7 +927,6 @@ public void shouldUseMetadataServer_noEnvironmentVars() { MockExternalAccountCredentialsTransportFactory transportFactory = new MockExternalAccountCredentialsTransportFactory(); AwsCredentials awsCredential = - (AwsCredentials) AwsCredentials.newBuilder(AWS_CREDENTIAL) .setHttpTransportFactory(transportFactory) .setCredentialSource(buildAwsImdsv2CredentialSource(transportFactory)) @@ -966,7 +939,6 @@ public void builder() { List scopes = Arrays.asList("scope1", "scope2"); AwsCredentials credentials = - (AwsCredentials) AwsCredentials.newBuilder() .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) .setAudience("audience") @@ -1001,7 +973,6 @@ public void serialize() throws IOException, ClassNotFoundException { List scopes = Arrays.asList("scope1", "scope2"); AwsCredentials testCredentials = - (AwsCredentials) AwsCredentials.newBuilder() .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) .setAudience("audience") diff --git a/oauth2_http/javatests/com/google/auth/oauth2/ExternalAccountCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/ExternalAccountCredentialsTest.java index ec558cfa8..b5089cbf2 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/ExternalAccountCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/ExternalAccountCredentialsTest.java @@ -44,6 +44,7 @@ import com.google.api.client.util.Clock; import com.google.auth.TestUtils; import com.google.auth.http.HttpTransportFactory; +import com.google.auth.oauth2.ExternalAccountCredentials.SubjectTokenTypes; import com.google.auth.oauth2.ExternalAccountCredentialsTest.TestExternalAccountCredentials.TestCredentialSource; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -558,6 +559,24 @@ public void constructor_builder() { assertNotNull(credentials.getCredentialSource()); } + @Test + public void constructor_builder_subjectTokenTypeEnum() { + HashMap credentialSource = new HashMap<>(); + credentialSource.put("file", "file"); + + ExternalAccountCredentials credentials = + IdentityPoolCredentials.newBuilder() + .setHttpTransportFactory(transportFactory) + .setAudience( + "//iam.googleapis.com/locations/global/workforcePools/pool/providers/provider") + .setSubjectTokenType(SubjectTokenTypes.SAML2) + .setTokenUrl(STS_URL) + .setCredentialSource(new TestCredentialSource(credentialSource)) + .build(); + + assertEquals(SubjectTokenTypes.SAML2.value, credentials.getSubjectTokenType()); + } + @Test public void constructor_builder_invalidTokenUrl() { try { diff --git a/oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java index 387430d42..e86d7a8f2 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java @@ -71,7 +71,6 @@ public class IdentityPoolCredentialsTest extends BaseSerializationTest { new IdentityPoolCredentialSource(FILE_CREDENTIAL_SOURCE_MAP); private static final IdentityPoolCredentials FILE_SOURCED_CREDENTIAL = - (IdentityPoolCredentials) IdentityPoolCredentials.newBuilder() .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) .setAudience( @@ -96,7 +95,6 @@ public HttpTransport create() { @Test public void createdScoped_clonedCredentialWithAddedScopes() { IdentityPoolCredentials credentials = - (IdentityPoolCredentials) IdentityPoolCredentials.newBuilder(FILE_SOURCED_CREDENTIAL) .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) .setQuotaProjectId("quotaProjectId") @@ -142,7 +140,6 @@ public void retrieveSubjectToken_fileSourced() throws IOException { new IdentityPoolCredentialSource(credentialSourceMap); IdentityPoolCredentials credentials = - (IdentityPoolCredentials) IdentityPoolCredentials.newBuilder(FILE_SOURCED_CREDENTIAL) .setCredentialSource(credentialSource) .build(); @@ -183,7 +180,6 @@ public void retrieveSubjectToken_fileSourcedWithJsonFormat() throws IOException file.getAbsolutePath()); IdentityPoolCredentials credential = - (IdentityPoolCredentials) IdentityPoolCredentials.newBuilder(FILE_SOURCED_CREDENTIAL) .setHttpTransportFactory(transportFactory) .setCredentialSource(credentialSource) @@ -224,7 +220,6 @@ public void retrieveSubjectToken_noFile_throws() { new IdentityPoolCredentialSource(credentialSourceMap); IdentityPoolCredentials credentials = - (IdentityPoolCredentials) IdentityPoolCredentials.newBuilder(FILE_SOURCED_CREDENTIAL) .setCredentialSource(credentialSource) .build(); @@ -245,7 +240,6 @@ public void retrieveSubjectToken_urlSourced() throws IOException { new MockExternalAccountCredentialsTransportFactory(); IdentityPoolCredentials credential = - (IdentityPoolCredentials) IdentityPoolCredentials.newBuilder(FILE_SOURCED_CREDENTIAL) .setHttpTransportFactory(transportFactory) .setCredentialSource( @@ -272,7 +266,6 @@ public void retrieveSubjectToken_urlSourcedWithJsonFormat() throws IOException { buildUrlBasedCredentialSource(transportFactory.transport.getMetadataUrl(), formatMap); IdentityPoolCredentials credential = - (IdentityPoolCredentials) IdentityPoolCredentials.newBuilder(FILE_SOURCED_CREDENTIAL) .setHttpTransportFactory(transportFactory) .setCredentialSource(credentialSource) @@ -292,7 +285,6 @@ public void retrieveSubjectToken_urlSourcedCredential_throws() { transportFactory.transport.addResponseErrorSequence(response); IdentityPoolCredentials credential = - (IdentityPoolCredentials) IdentityPoolCredentials.newBuilder(FILE_SOURCED_CREDENTIAL) .setHttpTransportFactory(transportFactory) .setCredentialSource( @@ -316,7 +308,6 @@ public void refreshAccessToken_withoutServiceAccountImpersonation() throws IOExc new MockExternalAccountCredentialsTransportFactory(); IdentityPoolCredentials credential = - (IdentityPoolCredentials) IdentityPoolCredentials.newBuilder() .setAudience( "//iam.googleapis.com/projects/123/locations/global/workloadIdentityPools/pool/providers/provider") @@ -345,7 +336,6 @@ public void refreshAccessToken_internalOptionsSet() throws IOException { new MockExternalAccountCredentialsTransportFactory(); IdentityPoolCredentials credential = - (IdentityPoolCredentials) IdentityPoolCredentials.newBuilder(FILE_SOURCED_CREDENTIAL) .setWorkforcePoolUserProject("userProject") .setAudience( @@ -380,7 +370,6 @@ public void refreshAccessToken_withServiceAccountImpersonation() throws IOExcept transportFactory.transport.setExpireTime(TestUtils.getDefaultExpireTime()); IdentityPoolCredentials credential = - (IdentityPoolCredentials) IdentityPoolCredentials.newBuilder() .setAudience( "//iam.googleapis.com/projects/123/locations/global/workloadIdentityPools/pool/providers/provider") @@ -412,7 +401,6 @@ public void refreshAccessToken_withServiceAccountImpersonationOptions() throws I transportFactory.transport.setExpireTime(TestUtils.getDefaultExpireTime()); IdentityPoolCredentials credential = - (IdentityPoolCredentials) IdentityPoolCredentials.newBuilder() .setAudience( "//iam.googleapis.com/projects/123/locations/global/workloadIdentityPools/pool/providers/provider") @@ -454,7 +442,6 @@ public void refreshAccessToken_workforceWithServiceAccountImpersonation() throws transportFactory.transport.setExpireTime(TestUtils.getDefaultExpireTime()); IdentityPoolCredentials credential = - (IdentityPoolCredentials) IdentityPoolCredentials.newBuilder(FILE_SOURCED_CREDENTIAL) .setAudience( "//iam.googleapis.com/locations/global/workforcePools/pool/providers/provider") @@ -491,7 +478,6 @@ public void refreshAccessToken_workforceWithServiceAccountImpersonationOptions() transportFactory.transport.setExpireTime(TestUtils.getDefaultExpireTime()); IdentityPoolCredentials credential = - (IdentityPoolCredentials) IdentityPoolCredentials.newBuilder(FILE_SOURCED_CREDENTIAL) .setAudience( "//iam.googleapis.com/locations/global/workforcePools/pool/providers/provider") @@ -676,7 +662,6 @@ public void builder() { List scopes = Arrays.asList("scope1", "scope2"); IdentityPoolCredentials credentials = - (IdentityPoolCredentials) IdentityPoolCredentials.newBuilder() .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) .setAudience("audience") @@ -744,7 +729,6 @@ public void builder_invalidWorkforceAudiences_throws() { public void builder_emptyWorkforceUserProjectWithWorkforceAudience() { // No exception should be thrown. IdentityPoolCredentials credentials = - (IdentityPoolCredentials) IdentityPoolCredentials.newBuilder() .setWorkforcePoolUserProject("") .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) @@ -763,7 +747,6 @@ public void builder_emptyWorkforceUserProjectWithWorkforceAudience() { @Test public void serialize() throws IOException, ClassNotFoundException { IdentityPoolCredentials testCredentials = - (IdentityPoolCredentials) IdentityPoolCredentials.newBuilder(FILE_SOURCED_CREDENTIAL) .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) .setQuotaProjectId("quotaProjectId") diff --git a/oauth2_http/javatests/com/google/auth/oauth2/PluggableAuthCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/PluggableAuthCredentialsTest.java index 395fe6cd6..1607e2760 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/PluggableAuthCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/PluggableAuthCredentialsTest.java @@ -62,7 +62,6 @@ public class PluggableAuthCredentialsTest extends BaseSerializationTest { private static final String STS_URL = "https://sts.googleapis.com"; private static final PluggableAuthCredentials CREDENTIAL = - (PluggableAuthCredentials) PluggableAuthCredentials.newBuilder() .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) .setAudience( @@ -108,7 +107,6 @@ public void retrieveSubjectToken_shouldPassAllOptionsToHandler() throws IOExcept }; PluggableAuthCredentials credential = - (PluggableAuthCredentials) PluggableAuthCredentials.newBuilder(CREDENTIAL) .setExecutableHandler(executableHandler) .setCredentialSource(buildCredentialSource(command, timeout, outputFile)) @@ -149,7 +147,6 @@ public void retrieveSubjectToken_shouldPassMinimalOptionsToHandler() throws IOEx }; PluggableAuthCredentials credential = - (PluggableAuthCredentials) PluggableAuthCredentials.newBuilder(CREDENTIAL) .setExecutableHandler(executableHandler) .setCredentialSource( @@ -184,7 +181,6 @@ public void refreshAccessToken_withoutServiceAccountImpersonation() throws IOExc transportFactory.transport.setExpireTime(TestUtils.getDefaultExpireTime()); PluggableAuthCredentials credential = - (PluggableAuthCredentials) PluggableAuthCredentials.newBuilder(CREDENTIAL) .setExecutableHandler(options -> "pluggableAuthToken") .setTokenUrl(transportFactory.transport.getStsUrl()) @@ -214,7 +210,6 @@ public void refreshAccessToken_withServiceAccountImpersonation() throws IOExcept transportFactory.transport.setExpireTime(TestUtils.getDefaultExpireTime()); PluggableAuthCredentials credential = - (PluggableAuthCredentials) PluggableAuthCredentials.newBuilder() .setAudience( "//iam.googleapis.com/projects/123/locations/global/workloadIdentityPools/pool/providers/provider") @@ -256,7 +251,6 @@ public void refreshAccessToken_withServiceAccountImpersonationOptions() throws I transportFactory.transport.setExpireTime(TestUtils.getDefaultExpireTime()); PluggableAuthCredentials credential = - (PluggableAuthCredentials) PluggableAuthCredentials.newBuilder() .setAudience( "//iam.googleapis.com/projects/123/locations/global/workloadIdentityPools/pool/providers/provider") @@ -401,11 +395,10 @@ public void pluggableAuthCredentialSource_missingExecutableCommandField_throws() public void builder_allFields() { List scopes = Arrays.asList("scope1", "scope2"); - CredentialSource source = buildCredentialSource(); + PluggableAuthCredentialSource source = buildCredentialSource(); ExecutableHandler handler = options -> "Token"; PluggableAuthCredentials credentials = - (PluggableAuthCredentials) PluggableAuthCredentials.newBuilder() .setExecutableHandler(handler) .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) @@ -439,7 +432,6 @@ public void builder_allFields() { @Test public void createdScoped_clonedCredentialWithAddedScopes() { PluggableAuthCredentials credentials = - (PluggableAuthCredentials) PluggableAuthCredentials.newBuilder(CREDENTIAL) .setExecutableHandler(options -> "pluggableAuthToken") .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) @@ -473,7 +465,6 @@ public void createdScoped_clonedCredentialWithAddedScopes() { @Test public void serialize() throws IOException, ClassNotFoundException { PluggableAuthCredentials testCredentials = - (PluggableAuthCredentials) PluggableAuthCredentials.newBuilder(CREDENTIAL) .setExecutableHandler(options -> "pluggableAuthToken") .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) @@ -487,11 +478,11 @@ public void serialize() throws IOException, ClassNotFoundException { assertThrows(NotSerializableException.class, () -> serializeAndDeserialize(testCredentials)); } - private static CredentialSource buildCredentialSource() { + private static PluggableAuthCredentialSource buildCredentialSource() { return buildCredentialSource("command", null, null); } - private static CredentialSource buildCredentialSource( + private static PluggableAuthCredentialSource buildCredentialSource( String command, @Nullable String timeoutMs, @Nullable String outputFile) { Map source = new HashMap<>(); Map executable = new HashMap<>(); From 448a8ec131e9ff37f5f40ab915bb9445d7ee5a97 Mon Sep 17 00:00:00 2001 From: aeitzman Date: Mon, 27 Nov 2023 09:30:18 -0800 Subject: [PATCH 03/32] fix: formatting --- .../google/auth/oauth2/AwsCredentials.java | 84 ++++--- .../oauth2/ExternalAccountCredentials.java | 9 +- .../auth/oauth2/IdentityPoolCredentials.java | 25 ++- .../auth/oauth2/AwsCredentialsTest.java | 211 ++++++++++++++---- .../ExternalAccountCredentialsTest.java | 20 +- .../oauth2/IdentityPoolCredentialsTest.java | 122 +++++++++- ...ckExternalAccountCredentialsTransport.java | 2 +- 7 files changed, 370 insertions(+), 103 deletions(-) diff --git a/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java b/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java index 45fc392a3..295875ddd 100644 --- a/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java @@ -75,24 +75,21 @@ public class AwsCredentials extends ExternalAccountCredentials { static final String AWS_IMDSV2_SESSION_TOKEN_TTL = "300"; private static final long serialVersionUID = -3670131891574618105L; - @Nullable - private final AwsCredentialSource awsCredentialSource; - @Nullable - private final Supplier awsSecurityCredentialsSupplier; - @Nullable - private final String regionalCredentialVerificationUrlOverride; - @Nullable - private final String region; + @Nullable private final AwsCredentialSource awsCredentialSource; + @Nullable private final Supplier awsSecurityCredentialsSupplier; + @Nullable private final String regionalCredentialVerificationUrlOverride; + @Nullable private final String region; /** Internal constructor. See {@link AwsCredentials.Builder}. */ AwsCredentials(Builder builder) { super(builder); // If user has provided a security credential supplier, use that to retrieve the AWS security // credentials. - if (builder.awsSecurityCredentialsSupplier != null){ + if (builder.awsSecurityCredentialsSupplier != null) { this.awsSecurityCredentialsSupplier = builder.awsSecurityCredentialsSupplier; - if (builder.region == null){ - throw new IllegalArgumentException("A region must be specified when using a credential supplier."); + if (builder.region == null) { + throw new IllegalArgumentException( + "A region must be specified when using an aws security credential supplier."); } this.awsCredentialSource = null; } else { @@ -100,7 +97,8 @@ public class AwsCredentials extends ExternalAccountCredentials { this.awsSecurityCredentialsSupplier = null; } this.region = builder.region; - this.regionalCredentialVerificationUrlOverride = builder.regionalCredentialVerificationUrlOverride; + this.regionalCredentialVerificationUrlOverride = + builder.regionalCredentialVerificationUrlOverride; } @Override @@ -156,7 +154,7 @@ public GoogleCredentials createScoped(Collection newScopes) { @Override String getCredentialSourceType() { - if (this.awsSecurityCredentialsSupplier != null){ + if (this.awsSecurityCredentialsSupplier != null) { return "programmatic"; } return "aws"; @@ -211,8 +209,7 @@ private String buildSubjectToken(AwsRequestSignature signature) token.put("method", signature.getHttpMethod()); token.put( "url", - this.getRegionalCredentialVerificationUrl().replace( - "{region}", signature.getRegion())); + this.getRegionalCredentialVerificationUrl().replace("{region}", signature.getRegion())); return URLEncoder.encode(token.toString(), "UTF-8"); } @@ -245,9 +242,9 @@ private boolean canRetrieveSecurityCredentialsFromEnvironment() { @VisibleForTesting boolean shouldUseMetadataServer() { - return this.awsSecurityCredentialsSupplier == null && - (!canRetrieveRegionFromEnvironment() || - !canRetrieveSecurityCredentialsFromEnvironment()); + return this.awsSecurityCredentialsSupplier == null + && (!canRetrieveRegionFromEnvironment() + || !canRetrieveSecurityCredentialsFromEnvironment()); } @VisibleForTesting @@ -289,7 +286,7 @@ Map createMetadataRequestHeaders(AwsCredentialSource awsCredenti String getAwsRegion(Map metadataRequestHeaders) throws IOException { // If user has provided a region string, return that instead of checking environment or metadata // server. - if (this.region != null){ + if (this.region != null) { return this.region; } String region; @@ -318,11 +315,12 @@ String getAwsRegion(Map metadataRequestHeaders) throws IOExcepti AwsSecurityCredentials getAwsSecurityCredentials(Map metadataRequestHeaders) throws IOException { // If this credential is using programmatic auth, call the user provided supplier. - if (this.awsSecurityCredentialsSupplier != null){ + if (this.awsSecurityCredentialsSupplier != null) { try { return this.awsSecurityCredentialsSupplier.get(); - } catch (Throwable e){ - throw new GoogleAuthException(false, 0, "Error retrieving token from custom token supplier.", e); + } catch (Throwable e) { + throw new GoogleAuthException( + false, 0, "Error retrieving token from aws security credentials supplier.", e); } } @@ -363,15 +361,13 @@ AwsSecurityCredentials getAwsSecurityCredentials(Map metadataReq } @VisibleForTesting - String getRegionalCredentialVerificationUrl(){ - if (this.regionalCredentialVerificationUrlOverride != null){ + String getRegionalCredentialVerificationUrl() { + if (this.regionalCredentialVerificationUrlOverride != null) { return this.regionalCredentialVerificationUrlOverride; - } - else if (this.awsCredentialSource != null){ + } else if (this.awsCredentialSource != null) { return this.awsCredentialSource.regionalCredentialVerificationUrl; - } - else { - return DEFAULT_REGIONAL_CREDENTIAL_VERIFICATION_URL; + } else { + return DEFAULT_REGIONAL_CREDENTIAL_VERIFICATION_URL; } } @@ -380,6 +376,21 @@ String getEnv(String name) { return System.getenv(name); } + @Nullable + public String getRegion() { + return this.region; + } + + @Nullable + public String getRegionalCredentialVerificationUrlOverride() { + return this.regionalCredentialVerificationUrlOverride; + } + + @Nullable + public Supplier getAwsSecurityCredentialsSupplier() { + return this.awsSecurityCredentialsSupplier; + } + private static GenericJson formatTokenHeaderForSts(String key, String value) { // The GCP STS endpoint expects the headers to be formatted as: // [ @@ -416,17 +427,19 @@ public static class Builder extends ExternalAccountCredentials.Builder { super(credentials); this.region = credentials.region; this.awsSecurityCredentialsSupplier = credentials.awsSecurityCredentialsSupplier; - this.regionalCredentialVerificationUrlOverride = credentials.regionalCredentialVerificationUrlOverride; + this.regionalCredentialVerificationUrlOverride = + credentials.regionalCredentialVerificationUrlOverride; } /** - * Sets the AWS security credentials supplier. The supplier should return a valid - * {@code AwsSecurityCredentials} object. An AWS region also is required when using a supplier. + * Sets the AWS security credentials supplier. The supplier should return a valid {@code + * AwsSecurityCredentials} object. An AWS region also is required when using a supplier. * * @param awsSecurityCredentialsSupplier the supplier method to be called. * @return this {@code Builder} object */ - public Builder setAwsSecurityCredentialsSupplier(Supplier awsSecurityCredentialsSupplier){ + public Builder setAwsSecurityCredentialsSupplier( + Supplier awsSecurityCredentialsSupplier) { this.awsSecurityCredentialsSupplier = awsSecurityCredentialsSupplier; return this; } @@ -438,7 +451,7 @@ public Builder setAwsSecurityCredentialsSupplier(Supplier credentialSource) { protected AccessToken exchangeExternalCredentialForAccessToken( StsTokenExchangeRequest stsTokenExchangeRequest) throws IOException { // Handle service account impersonation if necessary. - this.impersonatedCredentials = (this.impersonatedCredentials != null) ? - this.impersonatedCredentials : this.buildImpersonatedCredentials(); + this.impersonatedCredentials = + (this.impersonatedCredentials != null) + ? this.impersonatedCredentials + : this.buildImpersonatedCredentials(); if (impersonatedCredentials != null) { return impersonatedCredentials.refreshAccessToken(); } diff --git a/oauth2_http/java/com/google/auth/oauth2/IdentityPoolCredentials.java b/oauth2_http/java/com/google/auth/oauth2/IdentityPoolCredentials.java index 45a8a40c5..e8445fb12 100644 --- a/oauth2_http/java/com/google/auth/oauth2/IdentityPoolCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/IdentityPoolCredentials.java @@ -64,16 +64,14 @@ public class IdentityPoolCredentials extends ExternalAccountCredentials { private static final long serialVersionUID = 2471046175477275881L; - @Nullable - private final IdentityPoolCredentialSource identityPoolCredentialSource; + @Nullable private final IdentityPoolCredentialSource identityPoolCredentialSource; - @Nullable - private final Supplier subjectTokenSupplier; + @Nullable private final Supplier subjectTokenSupplier; /** Internal constructor. See {@link Builder}. */ IdentityPoolCredentials(Builder builder) { super(builder); - if (builder.subjectTokenSupplier != null){ + if (builder.subjectTokenSupplier != null) { this.subjectTokenSupplier = builder.subjectTokenSupplier; this.identityPoolCredentialSource = null; } else { @@ -99,14 +97,14 @@ public AccessToken refreshAccessToken() throws IOException { @Override public String retrieveSubjectToken() throws IOException { - if (this.subjectTokenSupplier != null){ + if (this.subjectTokenSupplier != null) { try { return this.subjectTokenSupplier.get(); - } catch (Throwable e){ - throw new GoogleAuthException(false, 0, "Error retrieving token from custom token supplier.", e); + } catch (Throwable e) { + throw new GoogleAuthException( + false, 0, "Error retrieving token from subject token supplier.", e); } - } - else if (identityPoolCredentialSource.credentialSourceType + } else if (identityPoolCredentialSource.credentialSourceType == IdentityPoolCredentialSource.IdentityPoolCredentialSourceType.FILE) { return retrieveSubjectTokenFromCredentialFile(); } @@ -115,7 +113,7 @@ else if (identityPoolCredentialSource.credentialSourceType @Override String getCredentialSourceType() { - if (this.subjectTokenSupplier != null){ + if (this.subjectTokenSupplier != null) { return "programmatic"; } if (((IdentityPoolCredentialSource) this.getCredentialSource()).credentialSourceType @@ -126,6 +124,10 @@ String getCredentialSourceType() { } } + public Supplier getSubjectTokenSupplier() { + return this.subjectTokenSupplier; + } + private String retrieveSubjectTokenFromCredentialFile() throws IOException { String credentialFilePath = identityPoolCredentialSource.credentialLocation; if (!Files.exists(Paths.get(credentialFilePath), LinkOption.NOFOLLOW_LINKS)) { @@ -204,6 +206,7 @@ public static class Builder extends ExternalAccountCredentials.Builder { Builder(IdentityPoolCredentials credentials) { super(credentials); + this.setSubjectTokenSupplier(credentials.subjectTokenSupplier); } public Builder setWorkforcePoolUserProject(String workforcePoolUserProject) { diff --git a/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java index 91592c3d1..bd8cddf5a 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java @@ -31,6 +31,7 @@ package com.google.auth.oauth2; +import static com.google.auth.oauth2.MockExternalAccountCredentialsTransport.SERVICE_ACCOUNT_IMPERSONATION_URL; import static org.junit.Assert.*; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -63,7 +64,7 @@ @RunWith(JUnit4.class) public class AwsCredentialsTest extends BaseSerializationTest { - private static final String STS_URL = "https://sts.googleapis.com"; + private static final String STS_URL = "https://sts.googleapis.com/v1/token"; private static final String AWS_CREDENTIALS_URL = "https://169.254.169.254"; private static final String AWS_CREDENTIALS_URL_WITH_ROLE = "https://169.254.169.254/roleName"; private static final String AWS_REGION_URL = "https://169.254.169.254/region"; @@ -106,10 +107,7 @@ public class AwsCredentialsTest extends BaseSerializationTest { .build(); private static final AwsSecurityCredentials ProgrammaticAwsCreds = - new AwsSecurityCredentials( - "testAccessKey", - "testSecretAccessKey", - null); + new AwsSecurityCredentials("testAccessKey", "testSecretAccessKey", null); private static final Supplier CredentialSupplier = () -> ProgrammaticAwsCreds; @@ -222,18 +220,21 @@ public void refreshAccessToken_withServiceAccountImpersonationOptions() throws I } @Test - public void refreshAccessTokenProgrammaticRefresh_withoutServiceAccountImpersonation() throws IOException { + public void refreshAccessTokenProgrammaticRefresh_withoutServiceAccountImpersonation() + throws IOException { MockExternalAccountCredentialsTransportFactory transportFactory = new MockExternalAccountCredentialsTransportFactory(); - AwsCredentials awsCredential = (AwsCredentials) AwsCredentials.newBuilder() - .setRegion("test") - .setAwsSecurityCredentialsSupplier(CredentialSupplier) - .setHttpTransportFactory(transportFactory) - .setAudience("audience") - .setTokenUrl(STS_URL) - .setSubjectTokenType("subjectTokenType") - .build(); + AwsCredentials awsCredential = + (AwsCredentials) + AwsCredentials.newBuilder() + .setRegion("test") + .setAwsSecurityCredentialsSupplier(CredentialSupplier) + .setHttpTransportFactory(transportFactory) + .setAudience("audience") + .setTokenUrl(STS_URL) + .setSubjectTokenType("subjectTokenType") + .build(); AccessToken accessToken = awsCredential.refreshAccessToken(); @@ -246,26 +247,30 @@ public void refreshAccessTokenProgrammaticRefresh_withoutServiceAccountImpersona } @Test - public void refreshAccessTokenProgrammaticRefresh_withServiceAccountImpersonation() throws IOException { + public void refreshAccessTokenProgrammaticRefresh_withServiceAccountImpersonation() + throws IOException { MockExternalAccountCredentialsTransportFactory transportFactory = new MockExternalAccountCredentialsTransportFactory(); transportFactory.transport.setExpireTime(TestUtils.getDefaultExpireTime()); - AwsCredentials awsCredential = (AwsCredentials) AwsCredentials.newBuilder() - .setRegion("test") - .setAwsSecurityCredentialsSupplier(CredentialSupplier) - .setHttpTransportFactory(transportFactory) - .setAudience("audience") - .setTokenUrl(STS_URL) - .setSubjectTokenType("subjectTokenType") - .setServiceAccountImpersonationUrl( - transportFactory.transport.getServiceAccountImpersonationUrl()) - .build(); + AwsCredentials awsCredential = + (AwsCredentials) + AwsCredentials.newBuilder() + .setRegion("test") + .setAwsSecurityCredentialsSupplier(CredentialSupplier) + .setHttpTransportFactory(transportFactory) + .setAudience("audience") + .setTokenUrl(STS_URL) + .setSubjectTokenType("subjectTokenType") + .setServiceAccountImpersonationUrl( + transportFactory.transport.getServiceAccountImpersonationUrl()) + .build(); AccessToken accessToken = awsCredential.refreshAccessToken(); - assertEquals(transportFactory.transport.getServiceAccountAccessToken(), accessToken.getTokenValue()); + assertEquals( + transportFactory.transport.getServiceAccountAccessToken(), accessToken.getTokenValue()); // Validate metrics header is set correctly on the sts request. Map> headers = @@ -608,14 +613,16 @@ public void retrieveSubjectToken_withProgrammaticRefresh() throws IOException { MockExternalAccountCredentialsTransportFactory transportFactory = new MockExternalAccountCredentialsTransportFactory(); - AwsCredentials awsCredential = (AwsCredentials) AwsCredentials.newBuilder() - .setRegion("test") - .setAwsSecurityCredentialsSupplier(CredentialSupplier) - .setHttpTransportFactory(transportFactory) - .setAudience("audience") - .setTokenUrl(STS_URL) - .setSubjectTokenType("subjectTokenType") - .build(); + AwsCredentials awsCredential = + (AwsCredentials) + AwsCredentials.newBuilder() + .setRegion("test") + .setAwsSecurityCredentialsSupplier(CredentialSupplier) + .setHttpTransportFactory(transportFactory) + .setAudience("audience") + .setTokenUrl(STS_URL) + .setSubjectTokenType("subjectTokenType") + .build(); String subjectToken = URLDecoder.decode(awsCredential.retrieveSubjectToken(), "UTF-8"); @@ -628,8 +635,8 @@ public void retrieveSubjectToken_withProgrammaticRefresh() throws IOException { headers.put(header.get("key"), header.get("value")); } - String expectedCredentialVerificationUrl = DEFAULT_REGIONAL_CREDENTIAL_VERIFICATION_URL.replace( - "{region}", "test"); + String expectedCredentialVerificationUrl = + DEFAULT_REGIONAL_CREDENTIAL_VERIFICATION_URL.replace("{region}", "test"); assertEquals("POST", json.get("method")); assertEquals(expectedCredentialVerificationUrl, json.get("url")); @@ -647,17 +654,19 @@ public void retrieveSubjectToken_withProgrammaticRefreshSessionToken() throws IO AwsSecurityCredentials securityCredentialsWithToken = new AwsSecurityCredentials("accessToken", "secretAccessKey", "token"); - Supplier awsSecurityCredentialsSupplierWithToken - = () -> securityCredentialsWithToken; + Supplier awsSecurityCredentialsSupplierWithToken = + () -> securityCredentialsWithToken; - AwsCredentials awsCredential = (AwsCredentials) AwsCredentials.newBuilder() - .setRegion("test") - .setAwsSecurityCredentialsSupplier(awsSecurityCredentialsSupplierWithToken) - .setHttpTransportFactory(transportFactory) - .setAudience("audience") - .setTokenUrl(STS_URL) - .setSubjectTokenType("subjectTokenType") - .build(); + AwsCredentials awsCredential = + (AwsCredentials) + AwsCredentials.newBuilder() + .setRegion("test") + .setAwsSecurityCredentialsSupplier(awsSecurityCredentialsSupplierWithToken) + .setHttpTransportFactory(transportFactory) + .setAudience("audience") + .setTokenUrl(STS_URL) + .setSubjectTokenType("subjectTokenType") + .build(); String subjectToken = URLDecoder.decode(awsCredential.retrieveSubjectToken(), "UTF-8"); @@ -670,8 +679,8 @@ public void retrieveSubjectToken_withProgrammaticRefreshSessionToken() throws IO headers.put(header.get("key"), header.get("value")); } - String expectedCredentialVerificationUrl = DEFAULT_REGIONAL_CREDENTIAL_VERIFICATION_URL.replace( - "{region}", "test"); + String expectedCredentialVerificationUrl = + DEFAULT_REGIONAL_CREDENTIAL_VERIFICATION_URL.replace("{region}", "test"); assertEquals("POST", json.get("method")); assertEquals(expectedCredentialVerificationUrl, json.get("url")); @@ -682,6 +691,39 @@ public void retrieveSubjectToken_withProgrammaticRefreshSessionToken() throws IO assertNotNull(headers.get("Authorization")); } + @Test + public void retrieveSubjectToken_withProgrammaticRefreshWrapsError() throws IOException { + MockExternalAccountCredentialsTransportFactory transportFactory = + new MockExternalAccountCredentialsTransportFactory(); + + RuntimeException testException = new RuntimeException("test"); + + Supplier errorSupplier = + () -> { + throw testException; + }; + + AwsCredentials awsCredential = + (AwsCredentials) + AwsCredentials.newBuilder() + .setRegion("test") + .setAwsSecurityCredentialsSupplier(errorSupplier) + .setHttpTransportFactory(transportFactory) + .setAudience("audience") + .setTokenUrl(STS_URL) + .setSubjectTokenType("subjectTokenType") + .build(); + + try { + String subjectToken = URLDecoder.decode(awsCredential.retrieveSubjectToken(), "UTF-8"); + fail("retrieveSubjectToken should not succeed"); + } catch (GoogleAuthException e) { + assertEquals( + "Error retrieving token from aws security credentials supplier.", e.getMessage()); + assertEquals(testException, e.getCause()); + } + } + @Test public void getAwsSecurityCredentials_fromEnvironmentVariablesNoToken() throws IOException { TestEnvironmentProvider environmentProvider = new TestEnvironmentProvider(); @@ -1105,12 +1147,17 @@ public void shouldUseMetadataServer_noEnvironmentVars() { } @Test - public void builder() { + public void builder() throws IOException { List scopes = Arrays.asList("scope1", "scope2"); + Supplier testSupplier = () -> null; + AwsCredentials credentials = (AwsCredentials) AwsCredentials.newBuilder() + .setRegionalCredentialVerificationUrlOverride("https://test.com") + .setRegion("region") + .setAwsSecurityCredentialsSupplier(testSupplier) .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) .setAudience("audience") .setSubjectTokenType("subjectTokenType") @@ -1125,6 +1172,9 @@ public void builder() { .setScopes(scopes) .build(); + assertEquals("region", credentials.getRegion()); + assertEquals("https://test.com", credentials.getRegionalCredentialVerificationUrlOverride()); + assertEquals(testSupplier, credentials.getAwsSecurityCredentialsSupplier()); assertEquals("audience", credentials.getAudience()); assertEquals("subjectTokenType", credentials.getSubjectTokenType()); assertEquals(credentials.getTokenUrl(), STS_URL); @@ -1139,6 +1189,69 @@ public void builder() { assertEquals(credentials.getEnvironmentProvider(), SystemEnvironmentProvider.getInstance()); } + @Test + public void builder_defaultRegionalCredentialVerificationUrlOverride() throws IOException { + List scopes = Arrays.asList("scope1", "scope2"); + + Supplier testSupplier = () -> null; + + AwsCredentials credentials = + (AwsCredentials) + AwsCredentials.newBuilder() + .setRegion("region") + .setAwsSecurityCredentialsSupplier(testSupplier) + .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) + .setAudience("audience") + .setSubjectTokenType("subjectTokenType") + .setTokenUrl(STS_URL) + .setTokenInfoUrl("tokenInfoUrl") + .setCredentialSource(AWS_CREDENTIAL_SOURCE) + .setTokenInfoUrl("tokenInfoUrl") + .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) + .setQuotaProjectId("quotaProjectId") + .setClientId("clientId") + .setClientSecret("clientSecret") + .setScopes(scopes) + .build(); + + assertNull(credentials.getRegionalCredentialVerificationUrlOverride()); + assertEquals( + DEFAULT_REGIONAL_CREDENTIAL_VERIFICATION_URL, + credentials.getRegionalCredentialVerificationUrl()); + } + + @Test + public void builder_regionRequiredWithSupplier() throws IOException { + List scopes = Arrays.asList("scope1", "scope2"); + + Supplier testSupplier = () -> null; + + try { + AwsCredentials credentials = + (AwsCredentials) + AwsCredentials.newBuilder() + .setAwsSecurityCredentialsSupplier(testSupplier) + .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) + .setAudience("audience") + .setSubjectTokenType("subjectTokenType") + .setTokenUrl(STS_URL) + .setTokenInfoUrl("tokenInfoUrl") + .setCredentialSource(AWS_CREDENTIAL_SOURCE) + .setTokenInfoUrl("tokenInfoUrl") + .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) + .setQuotaProjectId("quotaProjectId") + .setClientId("clientId") + .setClientSecret("clientSecret") + .setScopes(scopes) + .build(); + fail("Should not be able to continue without exception."); + } catch (IllegalArgumentException exception) { + assertEquals( + "A region must be specified when using an aws security credential supplier.", + exception.getMessage()); + } + } + @Test public void serialize() throws IOException, ClassNotFoundException { List scopes = Arrays.asList("scope1", "scope2"); diff --git a/oauth2_http/javatests/com/google/auth/oauth2/ExternalAccountCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/ExternalAccountCredentialsTest.java index 01c676d42..d951da409 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/ExternalAccountCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/ExternalAccountCredentialsTest.java @@ -49,7 +49,6 @@ import java.io.IOException; import java.math.BigDecimal; import java.net.URI; -import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.HashMap; @@ -65,7 +64,7 @@ @RunWith(JUnit4.class) public class ExternalAccountCredentialsTest extends BaseSerializationTest { - private static final String STS_URL = "https://sts.googleapis.com"; + private static final String STS_URL = "https://sts.googleapis.com/v1/token"; private static final Map FILE_CREDENTIAL_SOURCE_MAP = new HashMap() { @@ -558,6 +557,23 @@ public void constructor_builder() { assertNotNull(credentials.getCredentialSource()); } + @Test + public void constructor_builder_defaultTokenUrl() { + HashMap credentialSource = new HashMap<>(); + credentialSource.put("file", "file"); + + ExternalAccountCredentials credentials = + IdentityPoolCredentials.newBuilder() + .setHttpTransportFactory(transportFactory) + .setAudience( + "//iam.googleapis.com/locations/global/workforcePools/pool/providers/provider") + .setSubjectTokenType("subjectTokenType") + .setCredentialSource(new TestCredentialSource(credentialSource)) + .build(); + + assertEquals(STS_URL, credentials.getTokenUrl()); + } + @Test public void constructor_builder_invalidTokenUrl() { try { diff --git a/oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java index 387430d42..e99c74d21 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java @@ -49,6 +49,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.function.Supplier; import javax.annotation.Nullable; import org.junit.Test; import org.junit.runner.RunWith; @@ -58,7 +59,7 @@ @RunWith(JUnit4.class) public class IdentityPoolCredentialsTest extends BaseSerializationTest { - private static final String STS_URL = "https://sts.googleapis.com"; + private static final String STS_URL = "https://sts.googleapis.com/v1/token"; private static final Map FILE_CREDENTIAL_SOURCE_MAP = new HashMap() { @@ -82,6 +83,8 @@ public class IdentityPoolCredentialsTest extends BaseSerializationTest { .setCredentialSource(FILE_CREDENTIAL_SOURCE) .build(); + private static final Supplier testSupplier = () -> "testSubjectToken"; + static class MockExternalAccountCredentialsTransportFactory implements HttpTransportFactory { MockExternalAccountCredentialsTransport transport = @@ -310,6 +313,41 @@ public void retrieveSubjectToken_urlSourcedCredential_throws() { } } + @Test + public void retrieveSubjectToken_supplier() throws IOException { + + IdentityPoolCredentials credentials = + IdentityPoolCredentials.newBuilder(FILE_SOURCED_CREDENTIAL) + .setSubjectTokenSupplier(testSupplier) + .build(); + + String subjectToken = credentials.retrieveSubjectToken(); + + assertEquals(testSupplier.get(), subjectToken); + } + + @Test + public void retrieveSubjectToken_supplierWrapsError() throws IOException { + RuntimeException testException = new RuntimeException("test"); + + Supplier errorSupplier = + () -> { + throw testException; + }; + IdentityPoolCredentials credentials = + IdentityPoolCredentials.newBuilder(FILE_SOURCED_CREDENTIAL) + .setSubjectTokenSupplier(errorSupplier) + .build(); + + try { + String subjectToken = credentials.retrieveSubjectToken(); + fail("retrieveSubjectToken should fail."); + } catch (GoogleAuthException e) { + assertEquals("Error retrieving token from subject token supplier.", e.getMessage()); + assertEquals(testException, e.getCause()); + } + } + @Test public void refreshAccessToken_withoutServiceAccountImpersonation() throws IOException { MockExternalAccountCredentialsTransportFactory transportFactory = @@ -447,6 +485,65 @@ public void refreshAccessToken_withServiceAccountImpersonationOptions() throws I ExternalAccountCredentialsTest.validateMetricsHeader(headers, "url", true, true); } + @Test + public void refreshAccessToken_supplier() throws IOException { + MockExternalAccountCredentialsTransportFactory transportFactory = + new MockExternalAccountCredentialsTransportFactory(); + + transportFactory.transport.setExpireTime(TestUtils.getDefaultExpireTime()); + IdentityPoolCredentials credential = + (IdentityPoolCredentials) + IdentityPoolCredentials.newBuilder() + .setSubjectTokenSupplier(testSupplier) + .setAudience( + "//iam.googleapis.com/projects/123/locations/global/workloadIdentityPools/pool/providers/provider") + .setSubjectTokenType("subjectTokenType") + .setTokenInfoUrl("tokenInfoUrl") + .setTokenUrl(transportFactory.transport.getStsUrl()) + .setHttpTransportFactory(transportFactory) + .build(); + + AccessToken accessToken = credential.refreshAccessToken(); + + assertEquals(transportFactory.transport.getAccessToken(), accessToken.getTokenValue()); + + // Validate metrics header is set correctly on the sts request. + Map> headers = + transportFactory.transport.getRequests().get(0).getHeaders(); + ExternalAccountCredentialsTest.validateMetricsHeader(headers, "programmatic", false, false); + } + + @Test + public void refreshAccessToken_supplierWithServiceAccountImpersonation() throws IOException { + MockExternalAccountCredentialsTransportFactory transportFactory = + new MockExternalAccountCredentialsTransportFactory(); + + transportFactory.transport.setExpireTime(TestUtils.getDefaultExpireTime()); + IdentityPoolCredentials credential = + (IdentityPoolCredentials) + IdentityPoolCredentials.newBuilder() + .setSubjectTokenSupplier(testSupplier) + .setAudience( + "//iam.googleapis.com/projects/123/locations/global/workloadIdentityPools/pool/providers/provider") + .setSubjectTokenType("subjectTokenType") + .setTokenInfoUrl("tokenInfoUrl") + .setServiceAccountImpersonationUrl( + transportFactory.transport.getServiceAccountImpersonationUrl()) + .setTokenUrl(transportFactory.transport.getStsUrl()) + .setHttpTransportFactory(transportFactory) + .build(); + + AccessToken accessToken = credential.refreshAccessToken(); + + assertEquals( + transportFactory.transport.getServiceAccountAccessToken(), accessToken.getTokenValue()); + + // Validate metrics header is set correctly on the sts request. + Map> headers = + transportFactory.transport.getRequests().get(0).getHeaders(); + ExternalAccountCredentialsTest.validateMetricsHeader(headers, "programmatic", true, false); + } + @Test public void refreshAccessToken_workforceWithServiceAccountImpersonation() throws IOException { MockExternalAccountCredentialsTransportFactory transportFactory = @@ -705,6 +802,29 @@ public void builder() { assertEquals(credentials.getEnvironmentProvider(), SystemEnvironmentProvider.getInstance()); } + @Test + public void builder_subjectTokenSupplier() { + List scopes = Arrays.asList("scope1", "scope2"); + + IdentityPoolCredentials credentials = + (IdentityPoolCredentials) + IdentityPoolCredentials.newBuilder() + .setSubjectTokenSupplier(testSupplier) + .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) + .setAudience("audience") + .setSubjectTokenType("subjectTokenType") + .setTokenUrl(STS_URL) + .setTokenInfoUrl("tokenInfoUrl") + .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) + .setQuotaProjectId("quotaProjectId") + .setClientId("clientId") + .setClientSecret("clientSecret") + .setScopes(scopes) + .build(); + + assertEquals(testSupplier, credentials.getSubjectTokenSupplier()); + } + @Test public void builder_invalidWorkforceAudiences_throws() { List invalidAudiences = diff --git a/oauth2_http/javatests/com/google/auth/oauth2/MockExternalAccountCredentialsTransport.java b/oauth2_http/javatests/com/google/auth/oauth2/MockExternalAccountCredentialsTransport.java index 16c82c43b..16665a020 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/MockExternalAccountCredentialsTransport.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/MockExternalAccountCredentialsTransport.java @@ -69,7 +69,7 @@ public class MockExternalAccountCredentialsTransport extends MockHttpTransport { private static final String AWS_REGION_URL = "https://169.254.169.254/region"; private static final String AWS_IMDSV2_SESSION_TOKEN_URL = "https://169.254.169.254/imdsv2"; private static final String METADATA_SERVER_URL = "https://www.metadata.google.com"; - private static final String STS_URL = "https://sts.googleapis.com"; + private static final String STS_URL = "https://sts.googleapis.com/v1/token"; private static final String SUBJECT_TOKEN = "subjectToken"; private static final String TOKEN_TYPE = "Bearer"; From 59eb85619ab7fa242663c281d30200b834d7e1d5 Mon Sep 17 00:00:00 2001 From: aeitzman Date: Mon, 27 Nov 2023 09:31:37 -0800 Subject: [PATCH 04/32] fix: add formatting --- .../oauth2/ExternalAccountCredentials.java | 2 +- .../auth/oauth2/IdentityPoolCredentials.java | 4 +- .../auth/oauth2/PluggableAuthCredentials.java | 1 + .../auth/oauth2/AwsCredentialsTest.java | 332 +++++++++--------- .../oauth2/IdentityPoolCredentialsTest.java | 282 +++++++-------- .../oauth2/PluggableAuthCredentialsTest.java | 157 ++++----- 6 files changed, 388 insertions(+), 390 deletions(-) diff --git a/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java b/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java index eb80e384c..d530f0beb 100644 --- a/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java @@ -66,7 +66,7 @@ public abstract class ExternalAccountCredentials extends GoogleCredentials { /** * Enum to specify values for the subjectTokenType field in {@code ExternalAccountCredentials}. */ - public enum SubjectTokenTypes{ + public enum SubjectTokenTypes { AWS4("urn:ietf:params:aws:token-type:aws4_request"), JWT("urn:ietf:params:oauth:token-type:jwt"), SAML2("urn:ietf:params:oauth:token-type:saml2"); diff --git a/oauth2_http/java/com/google/auth/oauth2/IdentityPoolCredentials.java b/oauth2_http/java/com/google/auth/oauth2/IdentityPoolCredentials.java index b21078ad3..949cccdc1 100644 --- a/oauth2_http/java/com/google/auth/oauth2/IdentityPoolCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/IdentityPoolCredentials.java @@ -184,7 +184,6 @@ public static class Builder extends ExternalAccountCredentials.Builder { super(credentials); } - public Builder setHttpTransportFactory(HttpTransportFactory transportFactory) { super.setHttpTransportFactory(transportFactory); return this; @@ -250,8 +249,7 @@ public Builder setWorkforcePoolUserProject(String workforcePoolUserProject) { return this; } - public Builder setServiceAccountImpersonationOptions( - Map optionsMap) { + public Builder setServiceAccountImpersonationOptions(Map optionsMap) { super.setServiceAccountImpersonationOptions(optionsMap); return this; } diff --git a/oauth2_http/java/com/google/auth/oauth2/PluggableAuthCredentials.java b/oauth2_http/java/com/google/auth/oauth2/PluggableAuthCredentials.java index 26f9b9f48..da1a47c12 100644 --- a/oauth2_http/java/com/google/auth/oauth2/PluggableAuthCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/PluggableAuthCredentials.java @@ -304,6 +304,7 @@ Builder setEnvironmentProvider(EnvironmentProvider environmentProvider) { super.setEnvironmentProvider(environmentProvider); return this; } + @Override public PluggableAuthCredentials build() { return new PluggableAuthCredentials(this); diff --git a/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java index f0781e175..1b08ef431 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java @@ -92,14 +92,14 @@ public class AwsCredentialsTest extends BaseSerializationTest { new AwsCredentialSource(AWS_CREDENTIAL_SOURCE_MAP); private static final AwsCredentials AWS_CREDENTIAL = - AwsCredentials.newBuilder() - .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) - .setAudience("audience") - .setSubjectTokenType("subjectTokenType") - .setTokenUrl(STS_URL) - .setTokenInfoUrl("tokenInfoUrl") - .setCredentialSource(AWS_CREDENTIAL_SOURCE) - .build(); + AwsCredentials.newBuilder() + .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) + .setAudience("audience") + .setSubjectTokenType("subjectTokenType") + .setTokenUrl(STS_URL) + .setTokenInfoUrl("tokenInfoUrl") + .setCredentialSource(AWS_CREDENTIAL_SOURCE) + .build(); @Test public void test_awsCredentialSource() { @@ -119,11 +119,11 @@ public void refreshAccessToken_withoutServiceAccountImpersonation() throws IOExc new MockExternalAccountCredentialsTransportFactory(); AwsCredentials awsCredential = - AwsCredentials.newBuilder(AWS_CREDENTIAL) - .setTokenUrl(transportFactory.transport.getStsUrl()) - .setHttpTransportFactory(transportFactory) - .setCredentialSource(buildAwsCredentialSource(transportFactory)) - .build(); + AwsCredentials.newBuilder(AWS_CREDENTIAL) + .setTokenUrl(transportFactory.transport.getStsUrl()) + .setHttpTransportFactory(transportFactory) + .setCredentialSource(buildAwsCredentialSource(transportFactory)) + .build(); AccessToken accessToken = awsCredential.refreshAccessToken(); @@ -143,16 +143,16 @@ public void refreshAccessToken_withServiceAccountImpersonation() throws IOExcept transportFactory.transport.setExpireTime(TestUtils.getDefaultExpireTime()); AwsCredentials awsCredential = - AwsCredentials.newBuilder() - .setHttpTransportFactory(transportFactory) - .setAudience("audience") - .setSubjectTokenType("subjectTokenType") - .setTokenUrl(transportFactory.transport.getStsUrl()) - .setTokenInfoUrl("tokenInfoUrl") - .setCredentialSource(buildAwsCredentialSource(transportFactory)) - .setServiceAccountImpersonationUrl( - transportFactory.transport.getServiceAccountImpersonationUrl()) - .build(); + AwsCredentials.newBuilder() + .setHttpTransportFactory(transportFactory) + .setAudience("audience") + .setSubjectTokenType("subjectTokenType") + .setTokenUrl(transportFactory.transport.getStsUrl()) + .setTokenInfoUrl("tokenInfoUrl") + .setCredentialSource(buildAwsCredentialSource(transportFactory)) + .setServiceAccountImpersonationUrl( + transportFactory.transport.getServiceAccountImpersonationUrl()) + .build(); AccessToken accessToken = awsCredential.refreshAccessToken(); @@ -173,18 +173,18 @@ public void refreshAccessToken_withServiceAccountImpersonationOptions() throws I transportFactory.transport.setExpireTime(TestUtils.getDefaultExpireTime()); AwsCredentials awsCredential = - AwsCredentials.newBuilder() - .setHttpTransportFactory(transportFactory) - .setAudience("audience") - .setSubjectTokenType("subjectTokenType") - .setTokenUrl(transportFactory.transport.getStsUrl()) - .setTokenInfoUrl("tokenInfoUrl") - .setCredentialSource(buildAwsCredentialSource(transportFactory)) - .setServiceAccountImpersonationUrl( - transportFactory.transport.getServiceAccountImpersonationUrl()) - .setServiceAccountImpersonationOptions( - ExternalAccountCredentialsTest.buildServiceAccountImpersonationOptions(2800)) - .build(); + AwsCredentials.newBuilder() + .setHttpTransportFactory(transportFactory) + .setAudience("audience") + .setSubjectTokenType("subjectTokenType") + .setTokenUrl(transportFactory.transport.getStsUrl()) + .setTokenInfoUrl("tokenInfoUrl") + .setCredentialSource(buildAwsCredentialSource(transportFactory)) + .setServiceAccountImpersonationUrl( + transportFactory.transport.getServiceAccountImpersonationUrl()) + .setServiceAccountImpersonationOptions( + ExternalAccountCredentialsTest.buildServiceAccountImpersonationOptions(2800)) + .build(); AccessToken accessToken = awsCredential.refreshAccessToken(); @@ -211,10 +211,10 @@ public void retrieveSubjectToken() throws IOException { new MockExternalAccountCredentialsTransportFactory(); AwsCredentials awsCredential = - AwsCredentials.newBuilder(AWS_CREDENTIAL) - .setHttpTransportFactory(transportFactory) - .setCredentialSource(buildAwsCredentialSource(transportFactory)) - .build(); + AwsCredentials.newBuilder(AWS_CREDENTIAL) + .setHttpTransportFactory(transportFactory) + .setCredentialSource(buildAwsCredentialSource(transportFactory)) + .build(); String subjectToken = URLDecoder.decode(awsCredential.retrieveSubjectToken(), "UTF-8"); @@ -254,10 +254,10 @@ public void retrieveSubjectTokenWithSessionTokenUrl() throws IOException { new MockExternalAccountCredentialsTransportFactory(); AwsCredentials awsCredential = - AwsCredentials.newBuilder(AWS_CREDENTIAL) - .setHttpTransportFactory(transportFactory) - .setCredentialSource(buildAwsImdsv2CredentialSource(transportFactory)) - .build(); + AwsCredentials.newBuilder(AWS_CREDENTIAL) + .setHttpTransportFactory(transportFactory) + .setCredentialSource(buildAwsImdsv2CredentialSource(transportFactory)) + .build(); String subjectToken = URLDecoder.decode(awsCredential.retrieveSubjectToken(), "UTF-8"); @@ -325,11 +325,11 @@ public void retrieveSubjectToken_imdsv1EnvVariablesSet_metadataServerNotCalled() .setEnv("AWS_SESSION_TOKEN", "awsToken"); AwsCredentials awsCredential = - AwsCredentials.newBuilder(AWS_CREDENTIAL) - .setHttpTransportFactory(transportFactory) - .setCredentialSource(buildAwsCredentialSource(transportFactory)) - .setEnvironmentProvider(environmentProvider) - .build(); + AwsCredentials.newBuilder(AWS_CREDENTIAL) + .setHttpTransportFactory(transportFactory) + .setCredentialSource(buildAwsCredentialSource(transportFactory)) + .setEnvironmentProvider(environmentProvider) + .build(); String subjectToken = URLDecoder.decode(awsCredential.retrieveSubjectToken(), "UTF-8"); @@ -371,11 +371,11 @@ public void retrieveSubjectToken_imdsv2EnvVariablesSet_metadataServerNotCalled() .setEnv("AWS_SESSION_TOKEN", "awsToken"); AwsCredentials awsCredential = - AwsCredentials.newBuilder(AWS_CREDENTIAL) - .setHttpTransportFactory(transportFactory) - .setCredentialSource(buildAwsImdsv2CredentialSource(transportFactory)) - .setEnvironmentProvider(environmentProvider) - .build(); + AwsCredentials.newBuilder(AWS_CREDENTIAL) + .setHttpTransportFactory(transportFactory) + .setCredentialSource(buildAwsImdsv2CredentialSource(transportFactory)) + .setEnvironmentProvider(environmentProvider) + .build(); String subjectToken = URLDecoder.decode(awsCredential.retrieveSubjectToken(), "UTF-8"); @@ -411,10 +411,10 @@ public void retrieveSubjectToken_noRegion_expectThrows() { transportFactory.transport.addResponseErrorSequence(response); AwsCredentials awsCredential = - AwsCredentials.newBuilder(AWS_CREDENTIAL) - .setHttpTransportFactory(transportFactory) - .setCredentialSource(buildAwsCredentialSource(transportFactory)) - .build(); + AwsCredentials.newBuilder(AWS_CREDENTIAL) + .setHttpTransportFactory(transportFactory) + .setCredentialSource(buildAwsCredentialSource(transportFactory)) + .build(); try { awsCredential.retrieveSubjectToken(); @@ -440,10 +440,10 @@ public void retrieveSubjectToken_noRole_expectThrows() { transportFactory.transport.addResponseSequence(true, false); AwsCredentials awsCredential = - AwsCredentials.newBuilder(AWS_CREDENTIAL) - .setHttpTransportFactory(transportFactory) - .setCredentialSource(buildAwsCredentialSource(transportFactory)) - .build(); + AwsCredentials.newBuilder(AWS_CREDENTIAL) + .setHttpTransportFactory(transportFactory) + .setCredentialSource(buildAwsCredentialSource(transportFactory)) + .build(); try { awsCredential.retrieveSubjectToken(); @@ -472,10 +472,10 @@ public void retrieveSubjectToken_noCredentials_expectThrows() { transportFactory.transport.addResponseSequence(true, true, false); AwsCredentials awsCredential = - AwsCredentials.newBuilder(AWS_CREDENTIAL) - .setHttpTransportFactory(transportFactory) - .setCredentialSource(buildAwsCredentialSource(transportFactory)) - .build(); + AwsCredentials.newBuilder(AWS_CREDENTIAL) + .setHttpTransportFactory(transportFactory) + .setCredentialSource(buildAwsCredentialSource(transportFactory)) + .build(); try { awsCredential.retrieveSubjectToken(); @@ -507,10 +507,10 @@ public void retrieveSubjectToken_noRegionUrlProvided() { credentialSource.put("regional_cred_verification_url", GET_CALLER_IDENTITY_URL); AwsCredentials awsCredential = - AwsCredentials.newBuilder(AWS_CREDENTIAL) - .setHttpTransportFactory(transportFactory) - .setCredentialSource(new AwsCredentialSource(credentialSource)) - .build(); + AwsCredentials.newBuilder(AWS_CREDENTIAL) + .setHttpTransportFactory(transportFactory) + .setCredentialSource(new AwsCredentialSource(credentialSource)) + .build(); try { awsCredential.retrieveSubjectToken(); @@ -535,9 +535,9 @@ public void getAwsSecurityCredentials_fromEnvironmentVariablesNoToken() throws I .setEnv("AWS_SECRET_ACCESS_KEY", "awsSecretAccessKey"); AwsCredentials testAwsCredentials = - AwsCredentials.newBuilder(AWS_CREDENTIAL) - .setEnvironmentProvider(environmentProvider) - .build(); + AwsCredentials.newBuilder(AWS_CREDENTIAL) + .setEnvironmentProvider(environmentProvider) + .build(); AwsSecurityCredentials credentials = testAwsCredentials.getAwsSecurityCredentials(EMPTY_METADATA_HEADERS); @@ -567,10 +567,10 @@ public void getAwsSecurityCredentials_fromEnvironmentVariablesWithToken() throws }); AwsCredentials testAwsCredentials = - AwsCredentials.newBuilder(AWS_CREDENTIAL) - .setEnvironmentProvider(environmentProvider) - .setCredentialSource(credSource) - .build(); + AwsCredentials.newBuilder(AWS_CREDENTIAL) + .setEnvironmentProvider(environmentProvider) + .setCredentialSource(credSource) + .build(); AwsSecurityCredentials credentials = testAwsCredentials.getAwsSecurityCredentials(EMPTY_METADATA_HEADERS); @@ -590,9 +590,9 @@ public void getAwsSecurityCredentials_fromEnvironmentVariables_noMetadataServerC .setEnv("AWS_SESSION_TOKEN", "awsSessionToken"); AwsCredentials testAwsCredentials = - AwsCredentials.newBuilder(AWS_CREDENTIAL) - .setEnvironmentProvider(environmentProvider) - .build(); + AwsCredentials.newBuilder(AWS_CREDENTIAL) + .setEnvironmentProvider(environmentProvider) + .build(); AwsSecurityCredentials credentials = testAwsCredentials.getAwsSecurityCredentials(EMPTY_METADATA_HEADERS); @@ -608,10 +608,10 @@ public void getAwsSecurityCredentials_fromMetadataServer() throws IOException { new MockExternalAccountCredentialsTransportFactory(); AwsCredentials awsCredential = - AwsCredentials.newBuilder(AWS_CREDENTIAL) - .setHttpTransportFactory(transportFactory) - .setCredentialSource(buildAwsCredentialSource(transportFactory)) - .build(); + AwsCredentials.newBuilder(AWS_CREDENTIAL) + .setHttpTransportFactory(transportFactory) + .setCredentialSource(buildAwsCredentialSource(transportFactory)) + .build(); AwsSecurityCredentials credentials = awsCredential.getAwsSecurityCredentials(EMPTY_METADATA_HEADERS); @@ -640,10 +640,10 @@ public void getAwsSecurityCredentials_fromMetadataServer_noUrlProvided() { credentialSource.put("regional_cred_verification_url", GET_CALLER_IDENTITY_URL); AwsCredentials awsCredential = - AwsCredentials.newBuilder(AWS_CREDENTIAL) - .setHttpTransportFactory(transportFactory) - .setCredentialSource(new AwsCredentialSource(credentialSource)) - .build(); + AwsCredentials.newBuilder(AWS_CREDENTIAL) + .setHttpTransportFactory(transportFactory) + .setCredentialSource(new AwsCredentialSource(credentialSource)) + .build(); try { awsCredential.getAwsSecurityCredentials(EMPTY_METADATA_HEADERS); @@ -668,11 +668,11 @@ public void getAwsRegion_awsRegionEnvironmentVariable() throws IOException { MockExternalAccountCredentialsTransportFactory transportFactory = new MockExternalAccountCredentialsTransportFactory(); AwsCredentials awsCredentials = - AwsCredentials.newBuilder(AWS_CREDENTIAL) - .setHttpTransportFactory(transportFactory) - .setCredentialSource(buildAwsCredentialSource(transportFactory)) - .setEnvironmentProvider(environmentProvider) - .build(); + AwsCredentials.newBuilder(AWS_CREDENTIAL) + .setHttpTransportFactory(transportFactory) + .setCredentialSource(buildAwsCredentialSource(transportFactory)) + .setEnvironmentProvider(environmentProvider) + .build(); String region = awsCredentials.getAwsRegion(EMPTY_METADATA_HEADERS); @@ -693,11 +693,11 @@ public void getAwsRegion_awsDefaultRegionEnvironmentVariable() throws IOExceptio MockExternalAccountCredentialsTransportFactory transportFactory = new MockExternalAccountCredentialsTransportFactory(); AwsCredentials awsCredentials = - AwsCredentials.newBuilder(AWS_CREDENTIAL) - .setHttpTransportFactory(transportFactory) - .setCredentialSource(buildAwsCredentialSource(transportFactory)) - .setEnvironmentProvider(environmentProvider) - .build(); + AwsCredentials.newBuilder(AWS_CREDENTIAL) + .setHttpTransportFactory(transportFactory) + .setCredentialSource(buildAwsCredentialSource(transportFactory)) + .setEnvironmentProvider(environmentProvider) + .build(); String region = awsCredentials.getAwsRegion(EMPTY_METADATA_HEADERS); @@ -715,10 +715,10 @@ public void getAwsRegion_metadataServer() throws IOException { MockExternalAccountCredentialsTransportFactory transportFactory = new MockExternalAccountCredentialsTransportFactory(); AwsCredentials awsCredentials = - AwsCredentials.newBuilder(AWS_CREDENTIAL) - .setHttpTransportFactory(transportFactory) - .setCredentialSource(buildAwsCredentialSource(transportFactory)) - .build(); + AwsCredentials.newBuilder(AWS_CREDENTIAL) + .setHttpTransportFactory(transportFactory) + .setCredentialSource(buildAwsCredentialSource(transportFactory)) + .build(); String region = awsCredentials.getAwsRegion(EMPTY_METADATA_HEADERS); @@ -740,13 +740,13 @@ public void getAwsRegion_metadataServer() throws IOException { @Test public void createdScoped_clonedCredentialWithAddedScopes() { AwsCredentials credentials = - AwsCredentials.newBuilder(AWS_CREDENTIAL) - .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) - .setQuotaProjectId("quotaProjectId") - .setClientId("clientId") - .setClientSecret("clientSecret") - .setUniverseDomain("universeDomain") - .build(); + AwsCredentials.newBuilder(AWS_CREDENTIAL) + .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) + .setQuotaProjectId("quotaProjectId") + .setClientId("clientId") + .setClientSecret("clientSecret") + .setUniverseDomain("universeDomain") + .build(); List newScopes = Arrays.asList("scope1", "scope2"); @@ -827,11 +827,11 @@ public void shouldUseMetadataServer_withRequiredEnvironmentVariables() { .setEnv("AWS_ACCESS_KEY_ID", "awsAccessKeyId") .setEnv("AWS_SECRET_ACCESS_KEY", "awsSecretAccessKey"); AwsCredentials awsCredential = - AwsCredentials.newBuilder(AWS_CREDENTIAL) - .setHttpTransportFactory(transportFactory) - .setCredentialSource(buildAwsImdsv2CredentialSource(transportFactory)) - .setEnvironmentProvider(environmentProvider) - .build(); + AwsCredentials.newBuilder(AWS_CREDENTIAL) + .setHttpTransportFactory(transportFactory) + .setCredentialSource(buildAwsImdsv2CredentialSource(transportFactory)) + .setEnvironmentProvider(environmentProvider) + .build(); assertFalse(awsCredential.shouldUseMetadataServer()); } } @@ -846,11 +846,11 @@ public void shouldUseMetadataServer_missingRegion() { .setEnv("AWS_ACCESS_KEY_ID", "awsAccessKeyId") .setEnv("AWS_SECRET_ACCESS_KEY", "awsSecretAccessKey"); AwsCredentials awsCredential = - AwsCredentials.newBuilder(AWS_CREDENTIAL) - .setHttpTransportFactory(transportFactory) - .setCredentialSource(buildAwsImdsv2CredentialSource(transportFactory)) - .setEnvironmentProvider(environmentProvider) - .build(); + AwsCredentials.newBuilder(AWS_CREDENTIAL) + .setHttpTransportFactory(transportFactory) + .setCredentialSource(buildAwsImdsv2CredentialSource(transportFactory)) + .setEnvironmentProvider(environmentProvider) + .build(); assertTrue(awsCredential.shouldUseMetadataServer()); } @@ -868,11 +868,11 @@ public void shouldUseMetadataServer_missingAwsAccessKeyId() { .setEnv(regionKey, "awsRegion") .setEnv("AWS_SECRET_ACCESS_KEY", "awsSecretAccessKey"); AwsCredentials awsCredential = - AwsCredentials.newBuilder(AWS_CREDENTIAL) - .setHttpTransportFactory(transportFactory) - .setCredentialSource(buildAwsImdsv2CredentialSource(transportFactory)) - .setEnvironmentProvider(environmentProvider) - .build(); + AwsCredentials.newBuilder(AWS_CREDENTIAL) + .setHttpTransportFactory(transportFactory) + .setCredentialSource(buildAwsImdsv2CredentialSource(transportFactory)) + .setEnvironmentProvider(environmentProvider) + .build(); assertTrue(awsCredential.shouldUseMetadataServer()); } } @@ -891,11 +891,11 @@ public void shouldUseMetadataServer_missingAwsSecretAccessKey() { .setEnv(regionKey, "awsRegion") .setEnv("AWS_ACCESS_KEY_ID", "awsAccessKeyId"); AwsCredentials awsCredential = - AwsCredentials.newBuilder(AWS_CREDENTIAL) - .setHttpTransportFactory(transportFactory) - .setCredentialSource(buildAwsImdsv2CredentialSource(transportFactory)) - .setEnvironmentProvider(environmentProvider) - .build(); + AwsCredentials.newBuilder(AWS_CREDENTIAL) + .setHttpTransportFactory(transportFactory) + .setCredentialSource(buildAwsImdsv2CredentialSource(transportFactory)) + .setEnvironmentProvider(environmentProvider) + .build(); assertTrue(awsCredential.shouldUseMetadataServer()); } } @@ -913,11 +913,11 @@ public void shouldUseMetadataServer_missingAwsSecurityCreds() { // Not set here. environmentProvider.setEnv(regionKey, "awsRegion"); AwsCredentials awsCredential = - AwsCredentials.newBuilder(AWS_CREDENTIAL) - .setHttpTransportFactory(transportFactory) - .setCredentialSource(buildAwsImdsv2CredentialSource(transportFactory)) - .setEnvironmentProvider(environmentProvider) - .build(); + AwsCredentials.newBuilder(AWS_CREDENTIAL) + .setHttpTransportFactory(transportFactory) + .setCredentialSource(buildAwsImdsv2CredentialSource(transportFactory)) + .setEnvironmentProvider(environmentProvider) + .build(); assertTrue(awsCredential.shouldUseMetadataServer()); } } @@ -927,10 +927,10 @@ public void shouldUseMetadataServer_noEnvironmentVars() { MockExternalAccountCredentialsTransportFactory transportFactory = new MockExternalAccountCredentialsTransportFactory(); AwsCredentials awsCredential = - AwsCredentials.newBuilder(AWS_CREDENTIAL) - .setHttpTransportFactory(transportFactory) - .setCredentialSource(buildAwsImdsv2CredentialSource(transportFactory)) - .build(); + AwsCredentials.newBuilder(AWS_CREDENTIAL) + .setHttpTransportFactory(transportFactory) + .setCredentialSource(buildAwsImdsv2CredentialSource(transportFactory)) + .build(); assertTrue(awsCredential.shouldUseMetadataServer()); } @@ -939,20 +939,20 @@ public void builder() { List scopes = Arrays.asList("scope1", "scope2"); AwsCredentials credentials = - AwsCredentials.newBuilder() - .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) - .setAudience("audience") - .setSubjectTokenType("subjectTokenType") - .setTokenUrl(STS_URL) - .setTokenInfoUrl("tokenInfoUrl") - .setCredentialSource(AWS_CREDENTIAL_SOURCE) - .setTokenInfoUrl("tokenInfoUrl") - .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) - .setQuotaProjectId("quotaProjectId") - .setClientId("clientId") - .setClientSecret("clientSecret") - .setScopes(scopes) - .build(); + AwsCredentials.newBuilder() + .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) + .setAudience("audience") + .setSubjectTokenType("subjectTokenType") + .setTokenUrl(STS_URL) + .setTokenInfoUrl("tokenInfoUrl") + .setCredentialSource(AWS_CREDENTIAL_SOURCE) + .setTokenInfoUrl("tokenInfoUrl") + .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) + .setQuotaProjectId("quotaProjectId") + .setClientId("clientId") + .setClientSecret("clientSecret") + .setScopes(scopes) + .build(); assertEquals("audience", credentials.getAudience()); assertEquals("subjectTokenType", credentials.getSubjectTokenType()); @@ -973,21 +973,21 @@ public void serialize() throws IOException, ClassNotFoundException { List scopes = Arrays.asList("scope1", "scope2"); AwsCredentials testCredentials = - AwsCredentials.newBuilder() - .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) - .setAudience("audience") - .setSubjectTokenType("subjectTokenType") - .setTokenUrl(STS_URL) - .setTokenInfoUrl("tokenInfoUrl") - .setCredentialSource(AWS_CREDENTIAL_SOURCE) - .setTokenInfoUrl("tokenInfoUrl") - .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) - .setQuotaProjectId("quotaProjectId") - .setClientId("clientId") - .setClientSecret("clientSecret") - .setUniverseDomain("universeDomain") - .setScopes(scopes) - .build(); + AwsCredentials.newBuilder() + .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) + .setAudience("audience") + .setSubjectTokenType("subjectTokenType") + .setTokenUrl(STS_URL) + .setTokenInfoUrl("tokenInfoUrl") + .setCredentialSource(AWS_CREDENTIAL_SOURCE) + .setTokenInfoUrl("tokenInfoUrl") + .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) + .setQuotaProjectId("quotaProjectId") + .setClientId("clientId") + .setClientSecret("clientSecret") + .setUniverseDomain("universeDomain") + .setScopes(scopes) + .build(); AwsCredentials deserializedCredentials = serializeAndDeserialize(testCredentials); assertEquals(testCredentials, deserializedCredentials); diff --git a/oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java index e86d7a8f2..7fd625740 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java @@ -71,15 +71,15 @@ public class IdentityPoolCredentialsTest extends BaseSerializationTest { new IdentityPoolCredentialSource(FILE_CREDENTIAL_SOURCE_MAP); private static final IdentityPoolCredentials FILE_SOURCED_CREDENTIAL = - IdentityPoolCredentials.newBuilder() - .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) - .setAudience( - "//iam.googleapis.com/projects/123/locations/global/workloadIdentityPools/pool/providers/provider") - .setSubjectTokenType("subjectTokenType") - .setTokenUrl(STS_URL) - .setTokenInfoUrl("tokenInfoUrl") - .setCredentialSource(FILE_CREDENTIAL_SOURCE) - .build(); + IdentityPoolCredentials.newBuilder() + .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) + .setAudience( + "//iam.googleapis.com/projects/123/locations/global/workloadIdentityPools/pool/providers/provider") + .setSubjectTokenType("subjectTokenType") + .setTokenUrl(STS_URL) + .setTokenInfoUrl("tokenInfoUrl") + .setCredentialSource(FILE_CREDENTIAL_SOURCE) + .build(); static class MockExternalAccountCredentialsTransportFactory implements HttpTransportFactory { @@ -95,13 +95,13 @@ public HttpTransport create() { @Test public void createdScoped_clonedCredentialWithAddedScopes() { IdentityPoolCredentials credentials = - IdentityPoolCredentials.newBuilder(FILE_SOURCED_CREDENTIAL) - .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) - .setQuotaProjectId("quotaProjectId") - .setClientId("clientId") - .setClientSecret("clientSecret") - .setUniverseDomain("universeDomain") - .build(); + IdentityPoolCredentials.newBuilder(FILE_SOURCED_CREDENTIAL) + .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) + .setQuotaProjectId("quotaProjectId") + .setClientId("clientId") + .setClientSecret("clientSecret") + .setUniverseDomain("universeDomain") + .build(); List newScopes = Arrays.asList("scope1", "scope2"); @@ -140,9 +140,9 @@ public void retrieveSubjectToken_fileSourced() throws IOException { new IdentityPoolCredentialSource(credentialSourceMap); IdentityPoolCredentials credentials = - IdentityPoolCredentials.newBuilder(FILE_SOURCED_CREDENTIAL) - .setCredentialSource(credentialSource) - .build(); + IdentityPoolCredentials.newBuilder(FILE_SOURCED_CREDENTIAL) + .setCredentialSource(credentialSource) + .build(); String subjectToken = credentials.retrieveSubjectToken(); @@ -180,10 +180,10 @@ public void retrieveSubjectToken_fileSourcedWithJsonFormat() throws IOException file.getAbsolutePath()); IdentityPoolCredentials credential = - IdentityPoolCredentials.newBuilder(FILE_SOURCED_CREDENTIAL) - .setHttpTransportFactory(transportFactory) - .setCredentialSource(credentialSource) - .build(); + IdentityPoolCredentials.newBuilder(FILE_SOURCED_CREDENTIAL) + .setHttpTransportFactory(transportFactory) + .setCredentialSource(credentialSource) + .build(); String subjectToken = credential.retrieveSubjectToken(); @@ -220,9 +220,9 @@ public void retrieveSubjectToken_noFile_throws() { new IdentityPoolCredentialSource(credentialSourceMap); IdentityPoolCredentials credentials = - IdentityPoolCredentials.newBuilder(FILE_SOURCED_CREDENTIAL) - .setCredentialSource(credentialSource) - .build(); + IdentityPoolCredentials.newBuilder(FILE_SOURCED_CREDENTIAL) + .setCredentialSource(credentialSource) + .build(); try { credentials.retrieveSubjectToken(); @@ -240,11 +240,11 @@ public void retrieveSubjectToken_urlSourced() throws IOException { new MockExternalAccountCredentialsTransportFactory(); IdentityPoolCredentials credential = - IdentityPoolCredentials.newBuilder(FILE_SOURCED_CREDENTIAL) - .setHttpTransportFactory(transportFactory) - .setCredentialSource( - buildUrlBasedCredentialSource(transportFactory.transport.getMetadataUrl())) - .build(); + IdentityPoolCredentials.newBuilder(FILE_SOURCED_CREDENTIAL) + .setHttpTransportFactory(transportFactory) + .setCredentialSource( + buildUrlBasedCredentialSource(transportFactory.transport.getMetadataUrl())) + .build(); String subjectToken = credential.retrieveSubjectToken(); @@ -266,10 +266,10 @@ public void retrieveSubjectToken_urlSourcedWithJsonFormat() throws IOException { buildUrlBasedCredentialSource(transportFactory.transport.getMetadataUrl(), formatMap); IdentityPoolCredentials credential = - IdentityPoolCredentials.newBuilder(FILE_SOURCED_CREDENTIAL) - .setHttpTransportFactory(transportFactory) - .setCredentialSource(credentialSource) - .build(); + IdentityPoolCredentials.newBuilder(FILE_SOURCED_CREDENTIAL) + .setHttpTransportFactory(transportFactory) + .setCredentialSource(credentialSource) + .build(); String subjectToken = credential.retrieveSubjectToken(); @@ -285,11 +285,11 @@ public void retrieveSubjectToken_urlSourcedCredential_throws() { transportFactory.transport.addResponseErrorSequence(response); IdentityPoolCredentials credential = - IdentityPoolCredentials.newBuilder(FILE_SOURCED_CREDENTIAL) - .setHttpTransportFactory(transportFactory) - .setCredentialSource( - buildUrlBasedCredentialSource(transportFactory.transport.getMetadataUrl())) - .build(); + IdentityPoolCredentials.newBuilder(FILE_SOURCED_CREDENTIAL) + .setHttpTransportFactory(transportFactory) + .setCredentialSource( + buildUrlBasedCredentialSource(transportFactory.transport.getMetadataUrl())) + .build(); try { credential.retrieveSubjectToken(); @@ -308,17 +308,17 @@ public void refreshAccessToken_withoutServiceAccountImpersonation() throws IOExc new MockExternalAccountCredentialsTransportFactory(); IdentityPoolCredentials credential = - IdentityPoolCredentials.newBuilder() - .setAudience( - "//iam.googleapis.com/projects/123/locations/global/workloadIdentityPools/pool/providers/provider") - .setSubjectTokenType("subjectTokenType") - .setTokenInfoUrl("tokenInfoUrl") - .setCredentialSource(FILE_CREDENTIAL_SOURCE) - .setTokenUrl(transportFactory.transport.getStsUrl()) - .setHttpTransportFactory(transportFactory) - .setCredentialSource( - buildUrlBasedCredentialSource(transportFactory.transport.getMetadataUrl())) - .build(); + IdentityPoolCredentials.newBuilder() + .setAudience( + "//iam.googleapis.com/projects/123/locations/global/workloadIdentityPools/pool/providers/provider") + .setSubjectTokenType("subjectTokenType") + .setTokenInfoUrl("tokenInfoUrl") + .setCredentialSource(FILE_CREDENTIAL_SOURCE) + .setTokenUrl(transportFactory.transport.getStsUrl()) + .setHttpTransportFactory(transportFactory) + .setCredentialSource( + buildUrlBasedCredentialSource(transportFactory.transport.getMetadataUrl())) + .build(); AccessToken accessToken = credential.refreshAccessToken(); @@ -336,15 +336,15 @@ public void refreshAccessToken_internalOptionsSet() throws IOException { new MockExternalAccountCredentialsTransportFactory(); IdentityPoolCredentials credential = - IdentityPoolCredentials.newBuilder(FILE_SOURCED_CREDENTIAL) - .setWorkforcePoolUserProject("userProject") - .setAudience( - "//iam.googleapis.com/locations/global/workforcePools/pool/providers/provider") - .setTokenUrl(transportFactory.transport.getStsUrl()) - .setHttpTransportFactory(transportFactory) - .setCredentialSource( - buildUrlBasedCredentialSource(transportFactory.transport.getMetadataUrl())) - .build(); + IdentityPoolCredentials.newBuilder(FILE_SOURCED_CREDENTIAL) + .setWorkforcePoolUserProject("userProject") + .setAudience( + "//iam.googleapis.com/locations/global/workforcePools/pool/providers/provider") + .setTokenUrl(transportFactory.transport.getStsUrl()) + .setHttpTransportFactory(transportFactory) + .setCredentialSource( + buildUrlBasedCredentialSource(transportFactory.transport.getMetadataUrl())) + .build(); AccessToken accessToken = credential.refreshAccessToken(); @@ -370,18 +370,18 @@ public void refreshAccessToken_withServiceAccountImpersonation() throws IOExcept transportFactory.transport.setExpireTime(TestUtils.getDefaultExpireTime()); IdentityPoolCredentials credential = - IdentityPoolCredentials.newBuilder() - .setAudience( - "//iam.googleapis.com/projects/123/locations/global/workloadIdentityPools/pool/providers/provider") - .setSubjectTokenType("subjectTokenType") - .setTokenInfoUrl("tokenInfoUrl") - .setServiceAccountImpersonationUrl( - transportFactory.transport.getServiceAccountImpersonationUrl()) - .setTokenUrl(transportFactory.transport.getStsUrl()) - .setHttpTransportFactory(transportFactory) - .setCredentialSource( - buildUrlBasedCredentialSource(transportFactory.transport.getMetadataUrl())) - .build(); + IdentityPoolCredentials.newBuilder() + .setAudience( + "//iam.googleapis.com/projects/123/locations/global/workloadIdentityPools/pool/providers/provider") + .setSubjectTokenType("subjectTokenType") + .setTokenInfoUrl("tokenInfoUrl") + .setServiceAccountImpersonationUrl( + transportFactory.transport.getServiceAccountImpersonationUrl()) + .setTokenUrl(transportFactory.transport.getStsUrl()) + .setHttpTransportFactory(transportFactory) + .setCredentialSource( + buildUrlBasedCredentialSource(transportFactory.transport.getMetadataUrl())) + .build(); AccessToken accessToken = credential.refreshAccessToken(); @@ -401,20 +401,20 @@ public void refreshAccessToken_withServiceAccountImpersonationOptions() throws I transportFactory.transport.setExpireTime(TestUtils.getDefaultExpireTime()); IdentityPoolCredentials credential = - IdentityPoolCredentials.newBuilder() - .setAudience( - "//iam.googleapis.com/projects/123/locations/global/workloadIdentityPools/pool/providers/provider") - .setSubjectTokenType("subjectTokenType") - .setTokenInfoUrl("tokenInfoUrl") - .setTokenUrl(transportFactory.transport.getStsUrl()) - .setHttpTransportFactory(transportFactory) - .setServiceAccountImpersonationUrl( - transportFactory.transport.getServiceAccountImpersonationUrl()) - .setCredentialSource( - buildUrlBasedCredentialSource(transportFactory.transport.getMetadataUrl())) - .setServiceAccountImpersonationOptions( - ExternalAccountCredentialsTest.buildServiceAccountImpersonationOptions(2800)) - .build(); + IdentityPoolCredentials.newBuilder() + .setAudience( + "//iam.googleapis.com/projects/123/locations/global/workloadIdentityPools/pool/providers/provider") + .setSubjectTokenType("subjectTokenType") + .setTokenInfoUrl("tokenInfoUrl") + .setTokenUrl(transportFactory.transport.getStsUrl()) + .setHttpTransportFactory(transportFactory) + .setServiceAccountImpersonationUrl( + transportFactory.transport.getServiceAccountImpersonationUrl()) + .setCredentialSource( + buildUrlBasedCredentialSource(transportFactory.transport.getMetadataUrl())) + .setServiceAccountImpersonationOptions( + ExternalAccountCredentialsTest.buildServiceAccountImpersonationOptions(2800)) + .build(); AccessToken accessToken = credential.refreshAccessToken(); @@ -442,17 +442,17 @@ public void refreshAccessToken_workforceWithServiceAccountImpersonation() throws transportFactory.transport.setExpireTime(TestUtils.getDefaultExpireTime()); IdentityPoolCredentials credential = - IdentityPoolCredentials.newBuilder(FILE_SOURCED_CREDENTIAL) - .setAudience( - "//iam.googleapis.com/locations/global/workforcePools/pool/providers/provider") - .setTokenUrl(transportFactory.transport.getStsUrl()) - .setServiceAccountImpersonationUrl( - transportFactory.transport.getServiceAccountImpersonationUrl()) - .setHttpTransportFactory(transportFactory) - .setCredentialSource( - buildUrlBasedCredentialSource(transportFactory.transport.getMetadataUrl())) - .setWorkforcePoolUserProject("userProject") - .build(); + IdentityPoolCredentials.newBuilder(FILE_SOURCED_CREDENTIAL) + .setAudience( + "//iam.googleapis.com/locations/global/workforcePools/pool/providers/provider") + .setTokenUrl(transportFactory.transport.getStsUrl()) + .setServiceAccountImpersonationUrl( + transportFactory.transport.getServiceAccountImpersonationUrl()) + .setHttpTransportFactory(transportFactory) + .setCredentialSource( + buildUrlBasedCredentialSource(transportFactory.transport.getMetadataUrl())) + .setWorkforcePoolUserProject("userProject") + .build(); AccessToken accessToken = credential.refreshAccessToken(); @@ -478,19 +478,19 @@ public void refreshAccessToken_workforceWithServiceAccountImpersonationOptions() transportFactory.transport.setExpireTime(TestUtils.getDefaultExpireTime()); IdentityPoolCredentials credential = - IdentityPoolCredentials.newBuilder(FILE_SOURCED_CREDENTIAL) - .setAudience( - "//iam.googleapis.com/locations/global/workforcePools/pool/providers/provider") - .setTokenUrl(transportFactory.transport.getStsUrl()) - .setServiceAccountImpersonationUrl( - transportFactory.transport.getServiceAccountImpersonationUrl()) - .setHttpTransportFactory(transportFactory) - .setCredentialSource( - buildUrlBasedCredentialSource(transportFactory.transport.getMetadataUrl())) - .setWorkforcePoolUserProject("userProject") - .setServiceAccountImpersonationOptions( - ExternalAccountCredentialsTest.buildServiceAccountImpersonationOptions(2800)) - .build(); + IdentityPoolCredentials.newBuilder(FILE_SOURCED_CREDENTIAL) + .setAudience( + "//iam.googleapis.com/locations/global/workforcePools/pool/providers/provider") + .setTokenUrl(transportFactory.transport.getStsUrl()) + .setServiceAccountImpersonationUrl( + transportFactory.transport.getServiceAccountImpersonationUrl()) + .setHttpTransportFactory(transportFactory) + .setCredentialSource( + buildUrlBasedCredentialSource(transportFactory.transport.getMetadataUrl())) + .setWorkforcePoolUserProject("userProject") + .setServiceAccountImpersonationOptions( + ExternalAccountCredentialsTest.buildServiceAccountImpersonationOptions(2800)) + .build(); AccessToken accessToken = credential.refreshAccessToken(); @@ -662,19 +662,19 @@ public void builder() { List scopes = Arrays.asList("scope1", "scope2"); IdentityPoolCredentials credentials = - IdentityPoolCredentials.newBuilder() - .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) - .setAudience("audience") - .setSubjectTokenType("subjectTokenType") - .setTokenUrl(STS_URL) - .setTokenInfoUrl("tokenInfoUrl") - .setCredentialSource(FILE_CREDENTIAL_SOURCE) - .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) - .setQuotaProjectId("quotaProjectId") - .setClientId("clientId") - .setClientSecret("clientSecret") - .setScopes(scopes) - .build(); + IdentityPoolCredentials.newBuilder() + .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) + .setAudience("audience") + .setSubjectTokenType("subjectTokenType") + .setTokenUrl(STS_URL) + .setTokenInfoUrl("tokenInfoUrl") + .setCredentialSource(FILE_CREDENTIAL_SOURCE) + .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) + .setQuotaProjectId("quotaProjectId") + .setClientId("clientId") + .setClientSecret("clientSecret") + .setScopes(scopes) + .build(); assertEquals("audience", credentials.getAudience()); assertEquals("subjectTokenType", credentials.getSubjectTokenType()); @@ -729,17 +729,17 @@ public void builder_invalidWorkforceAudiences_throws() { public void builder_emptyWorkforceUserProjectWithWorkforceAudience() { // No exception should be thrown. IdentityPoolCredentials credentials = - IdentityPoolCredentials.newBuilder() - .setWorkforcePoolUserProject("") - .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) - .setAudience( - "//iam.googleapis.com/locations/global/workforcePools/pool/providers/provider") - .setSubjectTokenType("subjectTokenType") - .setTokenUrl(STS_URL) - .setTokenInfoUrl("tokenInfoUrl") - .setCredentialSource(FILE_CREDENTIAL_SOURCE) - .setQuotaProjectId("quotaProjectId") - .build(); + IdentityPoolCredentials.newBuilder() + .setWorkforcePoolUserProject("") + .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) + .setAudience( + "//iam.googleapis.com/locations/global/workforcePools/pool/providers/provider") + .setSubjectTokenType("subjectTokenType") + .setTokenUrl(STS_URL) + .setTokenInfoUrl("tokenInfoUrl") + .setCredentialSource(FILE_CREDENTIAL_SOURCE) + .setQuotaProjectId("quotaProjectId") + .build(); assertTrue(credentials.isWorkforcePoolConfiguration()); } @@ -747,13 +747,13 @@ public void builder_emptyWorkforceUserProjectWithWorkforceAudience() { @Test public void serialize() throws IOException, ClassNotFoundException { IdentityPoolCredentials testCredentials = - IdentityPoolCredentials.newBuilder(FILE_SOURCED_CREDENTIAL) - .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) - .setQuotaProjectId("quotaProjectId") - .setClientId("clientId") - .setClientSecret("clientSecret") - .setUniverseDomain("universeDomain") - .build(); + IdentityPoolCredentials.newBuilder(FILE_SOURCED_CREDENTIAL) + .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) + .setQuotaProjectId("quotaProjectId") + .setClientId("clientId") + .setClientSecret("clientSecret") + .setUniverseDomain("universeDomain") + .build(); IdentityPoolCredentials deserializedCredentials = serializeAndDeserialize(testCredentials); assertEquals(testCredentials, deserializedCredentials); diff --git a/oauth2_http/javatests/com/google/auth/oauth2/PluggableAuthCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/PluggableAuthCredentialsTest.java index 1607e2760..3d3f3530e 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/PluggableAuthCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/PluggableAuthCredentialsTest.java @@ -39,7 +39,6 @@ import com.google.auth.TestUtils; import com.google.auth.http.HttpTransportFactory; import com.google.auth.oauth2.ExecutableHandler.ExecutableOptions; -import com.google.auth.oauth2.ExternalAccountCredentials.CredentialSource; import java.io.IOException; import java.io.InputStream; import java.io.NotSerializableException; @@ -62,15 +61,15 @@ public class PluggableAuthCredentialsTest extends BaseSerializationTest { private static final String STS_URL = "https://sts.googleapis.com"; private static final PluggableAuthCredentials CREDENTIAL = - PluggableAuthCredentials.newBuilder() - .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) - .setAudience( - "//iam.googleapis.com/projects/123/locations/global/workloadIdentityPools/pool/providers/provider") - .setSubjectTokenType("subjectTokenType") - .setTokenUrl(STS_URL) - .setTokenInfoUrl("tokenInfoUrl") - .setCredentialSource(buildCredentialSource()) - .build(); + PluggableAuthCredentials.newBuilder() + .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) + .setAudience( + "//iam.googleapis.com/projects/123/locations/global/workloadIdentityPools/pool/providers/provider") + .setSubjectTokenType("subjectTokenType") + .setTokenUrl(STS_URL) + .setTokenInfoUrl("tokenInfoUrl") + .setCredentialSource(buildCredentialSource()) + .build(); static class MockExternalAccountCredentialsTransportFactory implements HttpTransportFactory { @@ -107,11 +106,11 @@ public void retrieveSubjectToken_shouldPassAllOptionsToHandler() throws IOExcept }; PluggableAuthCredentials credential = - PluggableAuthCredentials.newBuilder(CREDENTIAL) - .setExecutableHandler(executableHandler) - .setCredentialSource(buildCredentialSource(command, timeout, outputFile)) - .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) - .build(); + PluggableAuthCredentials.newBuilder(CREDENTIAL) + .setExecutableHandler(executableHandler) + .setCredentialSource(buildCredentialSource(command, timeout, outputFile)) + .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) + .build(); String subjectToken = credential.retrieveSubjectToken(); @@ -147,11 +146,11 @@ public void retrieveSubjectToken_shouldPassMinimalOptionsToHandler() throws IOEx }; PluggableAuthCredentials credential = - PluggableAuthCredentials.newBuilder(CREDENTIAL) - .setExecutableHandler(executableHandler) - .setCredentialSource( - buildCredentialSource(command, /* timeoutMs= */ null, /* outputFile= */ null)) - .build(); + PluggableAuthCredentials.newBuilder(CREDENTIAL) + .setExecutableHandler(executableHandler) + .setCredentialSource( + buildCredentialSource(command, /* timeoutMs= */ null, /* outputFile= */ null)) + .build(); String subjectToken = credential.retrieveSubjectToken(); @@ -181,11 +180,11 @@ public void refreshAccessToken_withoutServiceAccountImpersonation() throws IOExc transportFactory.transport.setExpireTime(TestUtils.getDefaultExpireTime()); PluggableAuthCredentials credential = - PluggableAuthCredentials.newBuilder(CREDENTIAL) - .setExecutableHandler(options -> "pluggableAuthToken") - .setTokenUrl(transportFactory.transport.getStsUrl()) - .setHttpTransportFactory(transportFactory) - .build(); + PluggableAuthCredentials.newBuilder(CREDENTIAL) + .setExecutableHandler(options -> "pluggableAuthToken") + .setTokenUrl(transportFactory.transport.getStsUrl()) + .setHttpTransportFactory(transportFactory) + .build(); AccessToken accessToken = credential.refreshAccessToken(); @@ -210,17 +209,17 @@ public void refreshAccessToken_withServiceAccountImpersonation() throws IOExcept transportFactory.transport.setExpireTime(TestUtils.getDefaultExpireTime()); PluggableAuthCredentials credential = - PluggableAuthCredentials.newBuilder() - .setAudience( - "//iam.googleapis.com/projects/123/locations/global/workloadIdentityPools/pool/providers/provider") - .setSubjectTokenType("subjectTokenType") - .setTokenInfoUrl("tokenInfoUrl") - .setTokenUrl(transportFactory.transport.getStsUrl()) - .setCredentialSource(buildCredentialSource()) - .setServiceAccountImpersonationUrl( - transportFactory.transport.getServiceAccountImpersonationUrl()) - .setHttpTransportFactory(transportFactory) - .build(); + PluggableAuthCredentials.newBuilder() + .setAudience( + "//iam.googleapis.com/projects/123/locations/global/workloadIdentityPools/pool/providers/provider") + .setSubjectTokenType("subjectTokenType") + .setTokenInfoUrl("tokenInfoUrl") + .setTokenUrl(transportFactory.transport.getStsUrl()) + .setCredentialSource(buildCredentialSource()) + .setServiceAccountImpersonationUrl( + transportFactory.transport.getServiceAccountImpersonationUrl()) + .setHttpTransportFactory(transportFactory) + .build(); credential = PluggableAuthCredentials.newBuilder(credential) @@ -251,19 +250,19 @@ public void refreshAccessToken_withServiceAccountImpersonationOptions() throws I transportFactory.transport.setExpireTime(TestUtils.getDefaultExpireTime()); PluggableAuthCredentials credential = - PluggableAuthCredentials.newBuilder() - .setAudience( - "//iam.googleapis.com/projects/123/locations/global/workloadIdentityPools/pool/providers/provider") - .setSubjectTokenType("subjectTokenType") - .setTokenInfoUrl("tokenInfoUrl") - .setTokenUrl(transportFactory.transport.getStsUrl()) - .setCredentialSource(buildCredentialSource()) - .setServiceAccountImpersonationUrl( - transportFactory.transport.getServiceAccountImpersonationUrl()) - .setServiceAccountImpersonationOptions( - ExternalAccountCredentialsTest.buildServiceAccountImpersonationOptions(2800)) - .setHttpTransportFactory(transportFactory) - .build(); + PluggableAuthCredentials.newBuilder() + .setAudience( + "//iam.googleapis.com/projects/123/locations/global/workloadIdentityPools/pool/providers/provider") + .setSubjectTokenType("subjectTokenType") + .setTokenInfoUrl("tokenInfoUrl") + .setTokenUrl(transportFactory.transport.getStsUrl()) + .setCredentialSource(buildCredentialSource()) + .setServiceAccountImpersonationUrl( + transportFactory.transport.getServiceAccountImpersonationUrl()) + .setServiceAccountImpersonationOptions( + ExternalAccountCredentialsTest.buildServiceAccountImpersonationOptions(2800)) + .setHttpTransportFactory(transportFactory) + .build(); credential = PluggableAuthCredentials.newBuilder(credential) @@ -399,20 +398,20 @@ public void builder_allFields() { ExecutableHandler handler = options -> "Token"; PluggableAuthCredentials credentials = - PluggableAuthCredentials.newBuilder() - .setExecutableHandler(handler) - .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) - .setAudience("audience") - .setSubjectTokenType("subjectTokenType") - .setTokenUrl(STS_URL) - .setTokenInfoUrl("tokenInfoUrl") - .setCredentialSource(source) - .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) - .setQuotaProjectId("quotaProjectId") - .setClientId("clientId") - .setClientSecret("clientSecret") - .setScopes(scopes) - .build(); + PluggableAuthCredentials.newBuilder() + .setExecutableHandler(handler) + .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) + .setAudience("audience") + .setSubjectTokenType("subjectTokenType") + .setTokenUrl(STS_URL) + .setTokenInfoUrl("tokenInfoUrl") + .setCredentialSource(source) + .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) + .setQuotaProjectId("quotaProjectId") + .setClientId("clientId") + .setClientSecret("clientSecret") + .setScopes(scopes) + .build(); assertEquals(credentials.getExecutableHandler(), handler); assertEquals("audience", credentials.getAudience()); @@ -432,14 +431,14 @@ public void builder_allFields() { @Test public void createdScoped_clonedCredentialWithAddedScopes() { PluggableAuthCredentials credentials = - PluggableAuthCredentials.newBuilder(CREDENTIAL) - .setExecutableHandler(options -> "pluggableAuthToken") - .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) - .setQuotaProjectId("quotaProjectId") - .setClientId("clientId") - .setClientSecret("clientSecret") - .setUniverseDomain("universeDomain") - .build(); + PluggableAuthCredentials.newBuilder(CREDENTIAL) + .setExecutableHandler(options -> "pluggableAuthToken") + .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) + .setQuotaProjectId("quotaProjectId") + .setClientId("clientId") + .setClientSecret("clientSecret") + .setUniverseDomain("universeDomain") + .build(); List newScopes = Arrays.asList("scope1", "scope2"); @@ -465,14 +464,14 @@ public void createdScoped_clonedCredentialWithAddedScopes() { @Test public void serialize() throws IOException, ClassNotFoundException { PluggableAuthCredentials testCredentials = - PluggableAuthCredentials.newBuilder(CREDENTIAL) - .setExecutableHandler(options -> "pluggableAuthToken") - .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) - .setQuotaProjectId("quotaProjectId") - .setClientId("clientId") - .setClientSecret("clientSecret") - .setUniverseDomain("universeDomain") - .build(); + PluggableAuthCredentials.newBuilder(CREDENTIAL) + .setExecutableHandler(options -> "pluggableAuthToken") + .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) + .setQuotaProjectId("quotaProjectId") + .setClientId("clientId") + .setClientSecret("clientSecret") + .setUniverseDomain("universeDomain") + .build(); // PluggableAuthCredentials are not serializable assertThrows(NotSerializableException.class, () -> serializeAndDeserialize(testCredentials)); From 0495b7fcbc1cabce363669ffbc1828e17f70debd Mon Sep 17 00:00:00 2001 From: aeitzman Date: Mon, 27 Nov 2023 09:49:46 -0800 Subject: [PATCH 05/32] Adds @CanIgnoreReturnValue on new builder methods --- oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java | 4 ++++ .../java/com/google/auth/oauth2/IdentityPoolCredentials.java | 2 ++ 2 files changed, 6 insertions(+) diff --git a/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java b/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java index 295875ddd..cb5f88045 100644 --- a/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java @@ -42,6 +42,7 @@ import com.google.api.client.json.JsonParser; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableList; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; @@ -438,6 +439,7 @@ public static class Builder extends ExternalAccountCredentials.Builder { * @param awsSecurityCredentialsSupplier the supplier method to be called. * @return this {@code Builder} object */ + @CanIgnoreReturnValue public Builder setAwsSecurityCredentialsSupplier( Supplier awsSecurityCredentialsSupplier) { this.awsSecurityCredentialsSupplier = awsSecurityCredentialsSupplier; @@ -451,6 +453,7 @@ public Builder setAwsSecurityCredentialsSupplier( * @param region the aws region to set. * @return this {@code Builder} object */ + @CanIgnoreReturnValue public Builder setRegion(String region) { this.region = region; return this; @@ -465,6 +468,7 @@ public Builder setRegion(String region) { * @param regionalCredentialVerificationUrlOverride the AWS credential verification url to set. * @return this {@code Builder} object */ + @CanIgnoreReturnValue public Builder setRegionalCredentialVerificationUrlOverride( String regionalCredentialVerificationUrlOverride) { this.regionalCredentialVerificationUrlOverride = regionalCredentialVerificationUrlOverride; diff --git a/oauth2_http/java/com/google/auth/oauth2/IdentityPoolCredentials.java b/oauth2_http/java/com/google/auth/oauth2/IdentityPoolCredentials.java index e8445fb12..b261942d2 100644 --- a/oauth2_http/java/com/google/auth/oauth2/IdentityPoolCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/IdentityPoolCredentials.java @@ -40,6 +40,7 @@ import com.google.auth.oauth2.IdentityPoolCredentialSource.CredentialFormatType; import com.google.auth.oauth2.IdentityPoolCredentialSource.IdentityPoolCredentialSourceType; import com.google.common.io.CharStreams; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; @@ -220,6 +221,7 @@ public Builder setWorkforcePoolUserProject(String workforcePoolUserProject) { * @param subjectTokenSupplier the supplier method to be called. * @return this {@code Builder} object */ + @CanIgnoreReturnValue public Builder setSubjectTokenSupplier(Supplier subjectTokenSupplier) { this.subjectTokenSupplier = subjectTokenSupplier; return this; From a8b2f92ba582c47b11251902becde5bb84576967 Mon Sep 17 00:00:00 2001 From: aeitzman Date: Thu, 30 Nov 2023 16:29:50 -0800 Subject: [PATCH 06/32] Change test for impersonated credentials --- .../ExternalAccountCredentialsTest.java | 35 +++++++++++-------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/oauth2_http/javatests/com/google/auth/oauth2/ExternalAccountCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/ExternalAccountCredentialsTest.java index 7f93a05a8..112a1233b 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/ExternalAccountCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/ExternalAccountCredentialsTest.java @@ -464,21 +464,6 @@ public void fromJson_nullJson_throws() { } } - @Test - public void fromJson_invalidServiceAccountImpersonationUrl_throws() { - GenericJson json = buildJsonIdentityPoolCredential(); - json.put("service_account_impersonation_url", "https://iamcredentials.googleapis.com"); - - try { - ExternalAccountCredentials.fromJson(json, OAuth2Utils.HTTP_TRANSPORT_FACTORY); - fail("Exception should be thrown."); - } catch (IllegalArgumentException e) { - assertEquals( - "Unable to determine target principal from service account impersonation URL.", - e.getMessage()); - } - } - @Test public void fromJson_nullTransport_throws() { try { @@ -1041,6 +1026,26 @@ public void exchangeExternalCredentialForAccessToken_throws() throws IOException } } + @Test + public void exchangeExternalCredentialForAccessToken_invalidImpersonatedCredentialsThrows() throws IOException { + GenericJson json = buildJsonIdentityPoolCredential(); + json.put("service_account_impersonation_url", "https://iamcredentials.googleapis.com"); + ExternalAccountCredentials credential = + ExternalAccountCredentials.fromJson(json, transportFactory); + + StsTokenExchangeRequest stsTokenExchangeRequest = + StsTokenExchangeRequest.newBuilder("credential", "subjectTokenType").build(); + + try { + credential.exchangeExternalCredentialForAccessToken(stsTokenExchangeRequest); + fail("Exception should be thrown."); + } catch (IllegalArgumentException e) { + assertEquals( + "Unable to determine target principal from service account impersonation URL.", + e.getMessage()); + } + } + @Test public void getRequestMetadata_withQuotaProjectId() throws IOException { TestExternalAccountCredentials testCredentials = From 616fb130c7d6e356331229e3e71bac666d58ccca Mon Sep 17 00:00:00 2001 From: aeitzman Date: Thu, 30 Nov 2023 16:31:28 -0800 Subject: [PATCH 07/32] formatting --- .../com/google/auth/oauth2/ExternalAccountCredentialsTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/oauth2_http/javatests/com/google/auth/oauth2/ExternalAccountCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/ExternalAccountCredentialsTest.java index 112a1233b..1a27d488c 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/ExternalAccountCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/ExternalAccountCredentialsTest.java @@ -1027,7 +1027,8 @@ public void exchangeExternalCredentialForAccessToken_throws() throws IOException } @Test - public void exchangeExternalCredentialForAccessToken_invalidImpersonatedCredentialsThrows() throws IOException { + public void exchangeExternalCredentialForAccessToken_invalidImpersonatedCredentialsThrows() + throws IOException { GenericJson json = buildJsonIdentityPoolCredential(); json.put("service_account_impersonation_url", "https://iamcredentials.googleapis.com"); ExternalAccountCredentials credential = From b2552eb85c2d0815d27b6d0e65488016fff6b279 Mon Sep 17 00:00:00 2001 From: aeitzman Date: Thu, 30 Nov 2023 16:36:06 -0800 Subject: [PATCH 08/32] adding id_token type --- .../com/google/auth/oauth2/ExternalAccountCredentials.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java b/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java index af63f5c2e..60f26054a 100644 --- a/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java @@ -70,7 +70,8 @@ public abstract class ExternalAccountCredentials extends GoogleCredentials { public enum SubjectTokenTypes { AWS4("urn:ietf:params:aws:token-type:aws4_request"), JWT("urn:ietf:params:oauth:token-type:jwt"), - SAML2("urn:ietf:params:oauth:token-type:saml2"); + SAML2("urn:ietf:params:oauth:token-type:saml2"), + ID_TOKEN("urn:ietf:params:oauth:token-type:id_token"); public final String value; From 6726160d190ea8d8dadcea92061180be02be645d Mon Sep 17 00:00:00 2001 From: aeitzman Date: Fri, 1 Dec 2023 10:32:08 -0800 Subject: [PATCH 09/32] formatting --- .../auth/oauth2/IdentityPoolCredentials.java | 2 +- .../auth/oauth2/AwsCredentialsTest.java | 180 +++++++++--------- .../ExternalAccountCredentialsTest.java | 25 ++- .../oauth2/IdentityPoolCredentialsTest.java | 69 ++++--- 4 files changed, 136 insertions(+), 140 deletions(-) diff --git a/oauth2_http/java/com/google/auth/oauth2/IdentityPoolCredentials.java b/oauth2_http/java/com/google/auth/oauth2/IdentityPoolCredentials.java index bcf043343..750ab84c8 100644 --- a/oauth2_http/java/com/google/auth/oauth2/IdentityPoolCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/IdentityPoolCredentials.java @@ -54,9 +54,9 @@ import java.nio.file.Paths; import java.util.ArrayList; import java.util.Collection; +import java.util.Map; import java.util.function.Supplier; import javax.annotation.Nullable; -import java.util.Map; /** * Url-sourced and file-sourced external account credentials. diff --git a/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java index 6e5330921..f0004707f 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java @@ -222,14 +222,14 @@ public void refreshAccessTokenProgrammaticRefresh_withoutServiceAccountImpersona new MockExternalAccountCredentialsTransportFactory(); AwsCredentials awsCredential = - AwsCredentials.newBuilder() - .setRegion("test") - .setAwsSecurityCredentialsSupplier(CredentialSupplier) - .setHttpTransportFactory(transportFactory) - .setAudience("audience") - .setTokenUrl(STS_URL) - .setSubjectTokenType("subjectTokenType") - .build(); + AwsCredentials.newBuilder() + .setRegion("test") + .setAwsSecurityCredentialsSupplier(CredentialSupplier) + .setHttpTransportFactory(transportFactory) + .setAudience("audience") + .setTokenUrl(STS_URL) + .setSubjectTokenType("subjectTokenType") + .build(); AccessToken accessToken = awsCredential.refreshAccessToken(); @@ -250,16 +250,16 @@ public void refreshAccessTokenProgrammaticRefresh_withServiceAccountImpersonatio transportFactory.transport.setExpireTime(TestUtils.getDefaultExpireTime()); AwsCredentials awsCredential = - AwsCredentials.newBuilder() - .setRegion("test") - .setAwsSecurityCredentialsSupplier(CredentialSupplier) - .setHttpTransportFactory(transportFactory) - .setAudience("audience") - .setTokenUrl(STS_URL) - .setSubjectTokenType("subjectTokenType") - .setServiceAccountImpersonationUrl( - transportFactory.transport.getServiceAccountImpersonationUrl()) - .build(); + AwsCredentials.newBuilder() + .setRegion("test") + .setAwsSecurityCredentialsSupplier(CredentialSupplier) + .setHttpTransportFactory(transportFactory) + .setAudience("audience") + .setTokenUrl(STS_URL) + .setSubjectTokenType("subjectTokenType") + .setServiceAccountImpersonationUrl( + transportFactory.transport.getServiceAccountImpersonationUrl()) + .build(); AccessToken accessToken = awsCredential.refreshAccessToken(); @@ -602,14 +602,14 @@ public void retrieveSubjectToken_withProgrammaticRefresh() throws IOException { new MockExternalAccountCredentialsTransportFactory(); AwsCredentials awsCredential = - AwsCredentials.newBuilder() - .setRegion("test") - .setAwsSecurityCredentialsSupplier(CredentialSupplier) - .setHttpTransportFactory(transportFactory) - .setAudience("audience") - .setTokenUrl(STS_URL) - .setSubjectTokenType("subjectTokenType") - .build(); + AwsCredentials.newBuilder() + .setRegion("test") + .setAwsSecurityCredentialsSupplier(CredentialSupplier) + .setHttpTransportFactory(transportFactory) + .setAudience("audience") + .setTokenUrl(STS_URL) + .setSubjectTokenType("subjectTokenType") + .build(); String subjectToken = URLDecoder.decode(awsCredential.retrieveSubjectToken(), "UTF-8"); @@ -645,14 +645,14 @@ public void retrieveSubjectToken_withProgrammaticRefreshSessionToken() throws IO () -> securityCredentialsWithToken; AwsCredentials awsCredential = - AwsCredentials.newBuilder() - .setRegion("test") - .setAwsSecurityCredentialsSupplier(awsSecurityCredentialsSupplierWithToken) - .setHttpTransportFactory(transportFactory) - .setAudience("audience") - .setTokenUrl(STS_URL) - .setSubjectTokenType("subjectTokenType") - .build(); + AwsCredentials.newBuilder() + .setRegion("test") + .setAwsSecurityCredentialsSupplier(awsSecurityCredentialsSupplierWithToken) + .setHttpTransportFactory(transportFactory) + .setAudience("audience") + .setTokenUrl(STS_URL) + .setSubjectTokenType("subjectTokenType") + .build(); String subjectToken = URLDecoder.decode(awsCredential.retrieveSubjectToken(), "UTF-8"); @@ -690,14 +690,14 @@ public void retrieveSubjectToken_withProgrammaticRefreshWrapsError() throws IOEx }; AwsCredentials awsCredential = - AwsCredentials.newBuilder() - .setRegion("test") - .setAwsSecurityCredentialsSupplier(errorSupplier) - .setHttpTransportFactory(transportFactory) - .setAudience("audience") - .setTokenUrl(STS_URL) - .setSubjectTokenType("subjectTokenType") - .build(); + AwsCredentials.newBuilder() + .setRegion("test") + .setAwsSecurityCredentialsSupplier(errorSupplier) + .setHttpTransportFactory(transportFactory) + .setAudience("audience") + .setTokenUrl(STS_URL) + .setSubjectTokenType("subjectTokenType") + .build(); try { String subjectToken = URLDecoder.decode(awsCredential.retrieveSubjectToken(), "UTF-8"); @@ -1123,23 +1123,23 @@ public void builder() throws IOException { Supplier testSupplier = () -> null; AwsCredentials credentials = - AwsCredentials.newBuilder() - .setRegionalCredentialVerificationUrlOverride("https://test.com") - .setRegion("region") - .setAwsSecurityCredentialsSupplier(testSupplier) - .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) - .setAudience("audience") - .setSubjectTokenType("subjectTokenType") - .setTokenUrl(STS_URL) - .setTokenInfoUrl("tokenInfoUrl") - .setCredentialSource(AWS_CREDENTIAL_SOURCE) - .setTokenInfoUrl("tokenInfoUrl") - .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) - .setQuotaProjectId("quotaProjectId") - .setClientId("clientId") - .setClientSecret("clientSecret") - .setScopes(scopes) - .build(); + AwsCredentials.newBuilder() + .setRegionalCredentialVerificationUrlOverride("https://test.com") + .setRegion("region") + .setAwsSecurityCredentialsSupplier(testSupplier) + .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) + .setAudience("audience") + .setSubjectTokenType("subjectTokenType") + .setTokenUrl(STS_URL) + .setTokenInfoUrl("tokenInfoUrl") + .setCredentialSource(AWS_CREDENTIAL_SOURCE) + .setTokenInfoUrl("tokenInfoUrl") + .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) + .setQuotaProjectId("quotaProjectId") + .setClientId("clientId") + .setClientSecret("clientSecret") + .setScopes(scopes) + .build(); assertEquals("region", credentials.getRegion()); assertEquals("https://test.com", credentials.getRegionalCredentialVerificationUrlOverride()); @@ -1165,22 +1165,22 @@ public void builder_defaultRegionalCredentialVerificationUrlOverride() throws IO Supplier testSupplier = () -> null; AwsCredentials credentials = - AwsCredentials.newBuilder() - .setRegion("region") - .setAwsSecurityCredentialsSupplier(testSupplier) - .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) - .setAudience("audience") - .setSubjectTokenType("subjectTokenType") - .setTokenUrl(STS_URL) - .setTokenInfoUrl("tokenInfoUrl") - .setCredentialSource(AWS_CREDENTIAL_SOURCE) - .setTokenInfoUrl("tokenInfoUrl") - .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) - .setQuotaProjectId("quotaProjectId") - .setClientId("clientId") - .setClientSecret("clientSecret") - .setScopes(scopes) - .build(); + AwsCredentials.newBuilder() + .setRegion("region") + .setAwsSecurityCredentialsSupplier(testSupplier) + .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) + .setAudience("audience") + .setSubjectTokenType("subjectTokenType") + .setTokenUrl(STS_URL) + .setTokenInfoUrl("tokenInfoUrl") + .setCredentialSource(AWS_CREDENTIAL_SOURCE) + .setTokenInfoUrl("tokenInfoUrl") + .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) + .setQuotaProjectId("quotaProjectId") + .setClientId("clientId") + .setClientSecret("clientSecret") + .setScopes(scopes) + .build(); assertNull(credentials.getRegionalCredentialVerificationUrlOverride()); assertEquals( @@ -1196,21 +1196,21 @@ public void builder_regionRequiredWithSupplier() throws IOException { try { AwsCredentials credentials = - AwsCredentials.newBuilder() - .setAwsSecurityCredentialsSupplier(testSupplier) - .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) - .setAudience("audience") - .setSubjectTokenType("subjectTokenType") - .setTokenUrl(STS_URL) - .setTokenInfoUrl("tokenInfoUrl") - .setCredentialSource(AWS_CREDENTIAL_SOURCE) - .setTokenInfoUrl("tokenInfoUrl") - .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) - .setQuotaProjectId("quotaProjectId") - .setClientId("clientId") - .setClientSecret("clientSecret") - .setScopes(scopes) - .build(); + AwsCredentials.newBuilder() + .setAwsSecurityCredentialsSupplier(testSupplier) + .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) + .setAudience("audience") + .setSubjectTokenType("subjectTokenType") + .setTokenUrl(STS_URL) + .setTokenInfoUrl("tokenInfoUrl") + .setCredentialSource(AWS_CREDENTIAL_SOURCE) + .setTokenInfoUrl("tokenInfoUrl") + .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) + .setQuotaProjectId("quotaProjectId") + .setClientId("clientId") + .setClientSecret("clientSecret") + .setScopes(scopes) + .build(); fail("Should not be able to continue without exception."); } catch (IllegalArgumentException exception) { assertEquals( diff --git a/oauth2_http/javatests/com/google/auth/oauth2/ExternalAccountCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/ExternalAccountCredentialsTest.java index f2eab54ae..037465f26 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/ExternalAccountCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/ExternalAccountCredentialsTest.java @@ -564,21 +564,20 @@ public void constructor_builder_defaultTokenUrl() { @Test public void constructor_builder_subjectTokenTypeEnum() { - HashMap credentialSource = new HashMap<>(); - credentialSource.put("file", "file"); - - ExternalAccountCredentials credentials = - IdentityPoolCredentials.newBuilder() - .setHttpTransportFactory(transportFactory) - .setAudience( - "//iam.googleapis.com/locations/global/workforcePools/pool/providers/provider") - .setSubjectTokenType(SubjectTokenTypes.SAML2) - .setTokenUrl(STS_URL) - .setCredentialSource(new TestCredentialSource(credentialSource)) - .build(); + HashMap credentialSource = new HashMap<>(); + credentialSource.put("file", "file"); - assertEquals(SubjectTokenTypes.SAML2.value, credentials.getSubjectTokenType()); + ExternalAccountCredentials credentials = + IdentityPoolCredentials.newBuilder() + .setHttpTransportFactory(transportFactory) + .setAudience( + "//iam.googleapis.com/locations/global/workforcePools/pool/providers/provider") + .setSubjectTokenType(SubjectTokenTypes.SAML2) + .setTokenUrl(STS_URL) + .setCredentialSource(new TestCredentialSource(credentialSource)) + .build(); + assertEquals(SubjectTokenTypes.SAML2.value, credentials.getSubjectTokenType()); } @Test diff --git a/oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java index 626d02d5b..af4794290 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java @@ -480,16 +480,15 @@ public void refreshAccessToken_supplier() throws IOException { transportFactory.transport.setExpireTime(TestUtils.getDefaultExpireTime()); IdentityPoolCredentials credential = - (IdentityPoolCredentials) - IdentityPoolCredentials.newBuilder() - .setSubjectTokenSupplier(testSupplier) - .setAudience( - "//iam.googleapis.com/projects/123/locations/global/workloadIdentityPools/pool/providers/provider") - .setSubjectTokenType("subjectTokenType") - .setTokenInfoUrl("tokenInfoUrl") - .setTokenUrl(transportFactory.transport.getStsUrl()) - .setHttpTransportFactory(transportFactory) - .build(); + IdentityPoolCredentials.newBuilder() + .setSubjectTokenSupplier(testSupplier) + .setAudience( + "//iam.googleapis.com/projects/123/locations/global/workloadIdentityPools/pool/providers/provider") + .setSubjectTokenType("subjectTokenType") + .setTokenInfoUrl("tokenInfoUrl") + .setTokenUrl(transportFactory.transport.getStsUrl()) + .setHttpTransportFactory(transportFactory) + .build(); AccessToken accessToken = credential.refreshAccessToken(); @@ -508,18 +507,17 @@ public void refreshAccessToken_supplierWithServiceAccountImpersonation() throws transportFactory.transport.setExpireTime(TestUtils.getDefaultExpireTime()); IdentityPoolCredentials credential = - (IdentityPoolCredentials) - IdentityPoolCredentials.newBuilder() - .setSubjectTokenSupplier(testSupplier) - .setAudience( - "//iam.googleapis.com/projects/123/locations/global/workloadIdentityPools/pool/providers/provider") - .setSubjectTokenType("subjectTokenType") - .setTokenInfoUrl("tokenInfoUrl") - .setServiceAccountImpersonationUrl( - transportFactory.transport.getServiceAccountImpersonationUrl()) - .setTokenUrl(transportFactory.transport.getStsUrl()) - .setHttpTransportFactory(transportFactory) - .build(); + IdentityPoolCredentials.newBuilder() + .setSubjectTokenSupplier(testSupplier) + .setAudience( + "//iam.googleapis.com/projects/123/locations/global/workloadIdentityPools/pool/providers/provider") + .setSubjectTokenType("subjectTokenType") + .setTokenInfoUrl("tokenInfoUrl") + .setServiceAccountImpersonationUrl( + transportFactory.transport.getServiceAccountImpersonationUrl()) + .setTokenUrl(transportFactory.transport.getStsUrl()) + .setHttpTransportFactory(transportFactory) + .build(); AccessToken accessToken = credential.refreshAccessToken(); @@ -792,20 +790,19 @@ public void builder_subjectTokenSupplier() { List scopes = Arrays.asList("scope1", "scope2"); IdentityPoolCredentials credentials = - (IdentityPoolCredentials) - IdentityPoolCredentials.newBuilder() - .setSubjectTokenSupplier(testSupplier) - .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) - .setAudience("audience") - .setSubjectTokenType("subjectTokenType") - .setTokenUrl(STS_URL) - .setTokenInfoUrl("tokenInfoUrl") - .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) - .setQuotaProjectId("quotaProjectId") - .setClientId("clientId") - .setClientSecret("clientSecret") - .setScopes(scopes) - .build(); + IdentityPoolCredentials.newBuilder() + .setSubjectTokenSupplier(testSupplier) + .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) + .setAudience("audience") + .setSubjectTokenType("subjectTokenType") + .setTokenUrl(STS_URL) + .setTokenInfoUrl("tokenInfoUrl") + .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) + .setQuotaProjectId("quotaProjectId") + .setClientId("clientId") + .setClientSecret("clientSecret") + .setScopes(scopes) + .build(); assertEquals(testSupplier, credentials.getSubjectTokenSupplier()); } From 2fc4f9964540d9135c6a6d8b0a57021c609c8a00 Mon Sep 17 00:00:00 2001 From: aeitzman <12433791+aeitzman@users.noreply.github.com> Date: Tue, 5 Dec 2023 09:51:07 -0800 Subject: [PATCH 10/32] Update oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java Co-authored-by: Leo <39062083+lsirac@users.noreply.github.com> --- oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java b/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java index 9d8f6056f..976d0909d 100644 --- a/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java @@ -322,7 +322,7 @@ AwsSecurityCredentials getAwsSecurityCredentials(Map metadataReq return this.awsSecurityCredentialsSupplier.get(); } catch (Throwable e) { throw new GoogleAuthException( - false, 0, "Error retrieving token from aws security credentials supplier.", e); + /* isRetryable= */ false, /* retryCount= */ 0, "Error retrieving token from AWS security credentials supplier.", e); } } From e5a9c590f05377b39ef5ba137ddcbf8876c88712 Mon Sep 17 00:00:00 2001 From: aeitzman Date: Tue, 5 Dec 2023 13:48:43 -0800 Subject: [PATCH 11/32] PR comments --- .../google/auth/oauth2/AwsCredentials.java | 14 +++- .../oauth2/ExternalAccountCredentials.java | 13 ++-- .../auth/oauth2/IdentityPoolCredentials.java | 9 +++ .../auth/oauth2/AwsCredentialsTest.java | 66 +++++++++++++++++-- .../oauth2/IdentityPoolCredentialsTest.java | 40 +++++++++++ 5 files changed, 130 insertions(+), 12 deletions(-) diff --git a/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java b/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java index 976d0909d..c226a0a30 100644 --- a/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java @@ -85,6 +85,15 @@ public class AwsCredentials extends ExternalAccountCredentials { /** Internal constructor. See {@link AwsCredentials.Builder}. */ AwsCredentials(Builder builder) { super(builder); + // Check that one and only one of supplier or credential source are provided. + if (builder.awsSecurityCredentialsSupplier != null && builder.credentialSource != null) { + throw new IllegalArgumentException( + "AwsCredentials cannot have both an awsSecurityCredentialsSupplier and a credentialSource."); + } + if (builder.awsSecurityCredentialsSupplier == null && builder.credentialSource == null) { + throw new IllegalArgumentException( + "An awsSecurityCredentialsSupplier or a credentialSource must be provided."); + } // If user has provided a security credential supplier, use that to retrieve the AWS security // credentials. if (builder.awsSecurityCredentialsSupplier != null) { @@ -322,7 +331,10 @@ AwsSecurityCredentials getAwsSecurityCredentials(Map metadataReq return this.awsSecurityCredentialsSupplier.get(); } catch (Throwable e) { throw new GoogleAuthException( - /* isRetryable= */ false, /* retryCount= */ 0, "Error retrieving token from AWS security credentials supplier.", e); + /* isRetryable= */ false, + /* retryCount= */ 0, + "Error retrieving token from AWS security credentials supplier.", + e); } } diff --git a/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java b/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java index 7bf362219..a83914079 100644 --- a/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java @@ -239,8 +239,6 @@ protected ExternalAccountCredentials( } this.metricsHandler = new ExternalAccountMetricsHandler(this); - - this.impersonatedCredentials = buildImpersonatedCredentials(); } /** @@ -490,6 +488,10 @@ private static boolean isAwsCredential(Map credentialSource) { && ((String) credentialSource.get("environment_id")).startsWith("aws"); } + private boolean isImpersonationEnabled() { + return this.serviceAccountImpersonationUrl != null; + } + /** * Exchanges the external credential for a Google Cloud access token. * @@ -500,10 +502,9 @@ private static boolean isAwsCredential(Map credentialSource) { protected AccessToken exchangeExternalCredentialForAccessToken( StsTokenExchangeRequest stsTokenExchangeRequest) throws IOException { // Handle service account impersonation if necessary. - this.impersonatedCredentials = - (this.impersonatedCredentials != null) - ? this.impersonatedCredentials - : this.buildImpersonatedCredentials(); + if (this.isImpersonationEnabled() && this.impersonatedCredentials == null) { + this.impersonatedCredentials = this.buildImpersonatedCredentials(); + } if (impersonatedCredentials != null) { return impersonatedCredentials.refreshAccessToken(); } diff --git a/oauth2_http/java/com/google/auth/oauth2/IdentityPoolCredentials.java b/oauth2_http/java/com/google/auth/oauth2/IdentityPoolCredentials.java index 750ab84c8..58daf4bf4 100644 --- a/oauth2_http/java/com/google/auth/oauth2/IdentityPoolCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/IdentityPoolCredentials.java @@ -74,6 +74,15 @@ public class IdentityPoolCredentials extends ExternalAccountCredentials { /** Internal constructor. See {@link Builder}. */ IdentityPoolCredentials(Builder builder) { super(builder); + // Check that one and only one of supplier or credential source are provided. + if (builder.subjectTokenSupplier != null && builder.credentialSource != null) { + throw new IllegalArgumentException( + "IdentityPoolCredentials cannot have both a subjectTokenSupplier and a credentialSource."); + } + if (builder.subjectTokenSupplier == null && builder.credentialSource == null) { + throw new IllegalArgumentException( + "A subjectTokenSupplier or a credentialSource must be provided."); + } if (builder.subjectTokenSupplier != null) { this.subjectTokenSupplier = builder.subjectTokenSupplier; this.identityPoolCredentialSource = null; diff --git a/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java index f0004707f..212d6c600 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java @@ -704,7 +704,7 @@ public void retrieveSubjectToken_withProgrammaticRefreshWrapsError() throws IOEx fail("retrieveSubjectToken should not succeed"); } catch (GoogleAuthException e) { assertEquals( - "Error retrieving token from aws security credentials supplier.", e.getMessage()); + "Error retrieving token from AWS security credentials supplier.", e.getMessage()); assertEquals(testException, e.getCause()); } } @@ -1126,7 +1126,6 @@ public void builder() throws IOException { AwsCredentials.newBuilder() .setRegionalCredentialVerificationUrlOverride("https://test.com") .setRegion("region") - .setAwsSecurityCredentialsSupplier(testSupplier) .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) .setAudience("audience") .setSubjectTokenType("subjectTokenType") @@ -1143,7 +1142,6 @@ public void builder() throws IOException { assertEquals("region", credentials.getRegion()); assertEquals("https://test.com", credentials.getRegionalCredentialVerificationUrlOverride()); - assertEquals(testSupplier, credentials.getAwsSecurityCredentialsSupplier()); assertEquals("audience", credentials.getAudience()); assertEquals("subjectTokenType", credentials.getSubjectTokenType()); assertEquals(credentials.getTokenUrl(), STS_URL); @@ -1173,7 +1171,6 @@ public void builder_defaultRegionalCredentialVerificationUrlOverride() throws IO .setSubjectTokenType("subjectTokenType") .setTokenUrl(STS_URL) .setTokenInfoUrl("tokenInfoUrl") - .setCredentialSource(AWS_CREDENTIAL_SOURCE) .setTokenInfoUrl("tokenInfoUrl") .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) .setQuotaProjectId("quotaProjectId") @@ -1203,7 +1200,6 @@ public void builder_regionRequiredWithSupplier() throws IOException { .setSubjectTokenType("subjectTokenType") .setTokenUrl(STS_URL) .setTokenInfoUrl("tokenInfoUrl") - .setCredentialSource(AWS_CREDENTIAL_SOURCE) .setTokenInfoUrl("tokenInfoUrl") .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) .setQuotaProjectId("quotaProjectId") @@ -1219,6 +1215,66 @@ public void builder_regionRequiredWithSupplier() throws IOException { } } + @Test + public void builder_supplierAndCredSourceThrows() throws IOException { + List scopes = Arrays.asList("scope1", "scope2"); + + Supplier testSupplier = () -> null; + + try { + AwsCredentials credentials = + AwsCredentials.newBuilder() + .setAwsSecurityCredentialsSupplier(testSupplier) + .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) + .setAudience("audience") + .setSubjectTokenType("subjectTokenType") + .setTokenUrl(STS_URL) + .setTokenInfoUrl("tokenInfoUrl") + .setTokenInfoUrl("tokenInfoUrl") + .setCredentialSource(AWS_CREDENTIAL_SOURCE) + .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) + .setQuotaProjectId("quotaProjectId") + .setClientId("clientId") + .setClientSecret("clientSecret") + .setScopes(scopes) + .build(); + fail("Should not be able to continue without exception."); + } catch (IllegalArgumentException exception) { + assertEquals( + "AwsCredentials cannot have both an awsSecurityCredentialsSupplier and a credentialSource.", + exception.getMessage()); + } + } + + @Test + public void builder_noSupplierOrCredSourceThrows() throws IOException { + List scopes = Arrays.asList("scope1", "scope2"); + + Supplier testSupplier = () -> null; + + try { + AwsCredentials credentials = + AwsCredentials.newBuilder() + .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) + .setAudience("audience") + .setSubjectTokenType("subjectTokenType") + .setTokenUrl(STS_URL) + .setTokenInfoUrl("tokenInfoUrl") + .setTokenInfoUrl("tokenInfoUrl") + .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) + .setQuotaProjectId("quotaProjectId") + .setClientId("clientId") + .setClientSecret("clientSecret") + .setScopes(scopes) + .build(); + fail("Should not be able to continue without exception."); + } catch (IllegalArgumentException exception) { + assertEquals( + "An awsSecurityCredentialsSupplier or a credentialSource must be provided.", + exception.getMessage()); + } + } + @Test public void serialize() throws IOException, ClassNotFoundException { List scopes = Arrays.asList("scope1", "scope2"); diff --git a/oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java index af4794290..2557cf6c3 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java @@ -310,6 +310,7 @@ public void retrieveSubjectToken_supplier() throws IOException { IdentityPoolCredentials credentials = IdentityPoolCredentials.newBuilder(FILE_SOURCED_CREDENTIAL) + .setCredentialSource(null) .setSubjectTokenSupplier(testSupplier) .build(); @@ -328,6 +329,7 @@ public void retrieveSubjectToken_supplierWrapsError() throws IOException { }; IdentityPoolCredentials credentials = IdentityPoolCredentials.newBuilder(FILE_SOURCED_CREDENTIAL) + .setCredentialSource(null) .setSubjectTokenSupplier(errorSupplier) .build(); @@ -861,6 +863,44 @@ public void builder_emptyWorkforceUserProjectWithWorkforceAudience() { assertTrue(credentials.isWorkforcePoolConfiguration()); } + @Test + public void builder_supplierAndCredSourceThrows() throws IOException { + try { + IdentityPoolCredentials credentials = + IdentityPoolCredentials.newBuilder() + .setSubjectTokenSupplier(testSupplier) + .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) + .setAudience("audience") + .setSubjectTokenType("subjectTokenType") + .setTokenUrl(STS_URL) + .setCredentialSource(FILE_CREDENTIAL_SOURCE) + .build(); + fail("Should not be able to continue without exception."); + } catch (IllegalArgumentException exception) { + assertEquals( + "IdentityPoolCredentials cannot have both a subjectTokenSupplier and a credentialSource.", + exception.getMessage()); + } + } + + @Test + public void builder_noSupplierOrCredSourceThrows() throws IOException { + + try { + IdentityPoolCredentials credentials = + IdentityPoolCredentials.newBuilder() + .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) + .setAudience("audience") + .setSubjectTokenType("subjectTokenType") + .setTokenUrl(STS_URL) + .build(); + fail("Should not be able to continue without exception."); + } catch (IllegalArgumentException exception) { + assertEquals( + "A subjectTokenSupplier or a credentialSource must be provided.", exception.getMessage()); + } + } + @Test public void serialize() throws IOException, ClassNotFoundException { IdentityPoolCredentials testCredentials = From bfb83fac02f644bd60b8991170c0da67f2b96cd9 Mon Sep 17 00:00:00 2001 From: aeitzman Date: Tue, 5 Dec 2023 13:56:31 -0800 Subject: [PATCH 12/32] Added header value constants --- .../java/com/google/auth/oauth2/AwsCredentials.java | 7 +++++-- .../google/auth/oauth2/ExternalAccountCredentials.java | 1 + .../com/google/auth/oauth2/IdentityPoolCredentials.java | 8 +++++--- .../com/google/auth/oauth2/PluggableAuthCredentials.java | 5 ++++- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java b/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java index c226a0a30..4c9d44989 100644 --- a/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java @@ -75,6 +75,9 @@ public class AwsCredentials extends ExternalAccountCredentials { static final String AWS_IMDSV2_SESSION_TOKEN_HEADER = "x-aws-ec2-metadata-token"; static final String AWS_IMDSV2_SESSION_TOKEN_TTL_HEADER = "x-aws-ec2-metadata-token-ttl-seconds"; static final String AWS_IMDSV2_SESSION_TOKEN_TTL = "300"; + + static final String AWS_METRICS_HEADER_VALUE = "aws"; + private static final long serialVersionUID = -3670131891574618105L; @Nullable private final AwsCredentialSource awsCredentialSource; @@ -166,9 +169,9 @@ public GoogleCredentials createScoped(Collection newScopes) { @Override String getCredentialSourceType() { if (this.awsSecurityCredentialsSupplier != null) { - return "programmatic"; + return PROGRAMMATIC_AUTH_METRICS_HEADER_VALUE; } - return "aws"; + return AWS_METRICS_HEADER_VALUE; } private String retrieveResource(String url, String resourceName, Map headers) diff --git a/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java b/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java index a83914079..f8d0b2d12 100644 --- a/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java @@ -99,6 +99,7 @@ abstract static class CredentialSource implements java.io.Serializable { static final String EXECUTABLE_SOURCE_KEY = "executable"; static final String DEFAULT_TOKEN_URL = "https://sts.googleapis.com/v1/token"; + static final String PROGRAMMATIC_AUTH_METRICS_HEADER_VALUE = "programmatic"; private final String transportFactoryClassName; private final String audience; diff --git a/oauth2_http/java/com/google/auth/oauth2/IdentityPoolCredentials.java b/oauth2_http/java/com/google/auth/oauth2/IdentityPoolCredentials.java index 58daf4bf4..dcfddd9c7 100644 --- a/oauth2_http/java/com/google/auth/oauth2/IdentityPoolCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/IdentityPoolCredentials.java @@ -65,6 +65,8 @@ */ public class IdentityPoolCredentials extends ExternalAccountCredentials { + static final String FILE_METRICS_HEADER_VALUE = "file"; + static final String URL_METRICS_HEADER_VALUE = "url"; private static final long serialVersionUID = 2471046175477275881L; @Nullable private final IdentityPoolCredentialSource identityPoolCredentialSource; @@ -126,13 +128,13 @@ public String retrieveSubjectToken() throws IOException { @Override String getCredentialSourceType() { if (this.subjectTokenSupplier != null) { - return "programmatic"; + return PROGRAMMATIC_AUTH_METRICS_HEADER_VALUE; } if (((IdentityPoolCredentialSource) this.getCredentialSource()).credentialSourceType == IdentityPoolCredentialSourceType.FILE) { - return "file"; + return FILE_METRICS_HEADER_VALUE; } else { - return "url"; + return URL_METRICS_HEADER_VALUE; } } diff --git a/oauth2_http/java/com/google/auth/oauth2/PluggableAuthCredentials.java b/oauth2_http/java/com/google/auth/oauth2/PluggableAuthCredentials.java index 55e31f7d0..e984c8173 100644 --- a/oauth2_http/java/com/google/auth/oauth2/PluggableAuthCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/PluggableAuthCredentials.java @@ -98,6 +98,9 @@ *

Please see this repositories README for a complete executable request/response specification. */ public class PluggableAuthCredentials extends ExternalAccountCredentials { + + static final String PLUGGABLE_AUTH_METRICS_HEADER_VALUE = "executable"; + private final PluggableAuthCredentialSource config; private final ExecutableHandler handler; @@ -189,7 +192,7 @@ public PluggableAuthCredentials createScoped(Collection newScopes) { @Override String getCredentialSourceType() { - return "executable"; + return PLUGGABLE_AUTH_METRICS_HEADER_VALUE; } public static Builder newBuilder() { From 164ac251687bc5630362eb0639fbb47ff783f4f6 Mon Sep 17 00:00:00 2001 From: aeitzman Date: Thu, 7 Dec 2023 10:35:23 -0800 Subject: [PATCH 13/32] updating java doc --- oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java | 2 ++ .../java/com/google/auth/oauth2/IdentityPoolCredentials.java | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java b/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java index 4c9d44989..499b2998f 100644 --- a/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java @@ -57,6 +57,8 @@ /** * AWS credentials representing a third-party identity for calling Google APIs. + * AWS Security credentials are either sourced by calling EC2 metadata endpoints, environment + * variables, or a user provided supplier method. * *

By default, attempts to exchange the external credential for a GCP access token. */ diff --git a/oauth2_http/java/com/google/auth/oauth2/IdentityPoolCredentials.java b/oauth2_http/java/com/google/auth/oauth2/IdentityPoolCredentials.java index dcfddd9c7..ea0a2ca16 100644 --- a/oauth2_http/java/com/google/auth/oauth2/IdentityPoolCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/IdentityPoolCredentials.java @@ -59,7 +59,7 @@ import javax.annotation.Nullable; /** - * Url-sourced and file-sourced external account credentials. + * Url-sourced, file-sourced, or user provided supplier method-sourced external account credentials. * *

By default, attempts to exchange the external credential for a GCP access token. */ From a257e554d8889a6f2d53206e1182b060569683d7 Mon Sep 17 00:00:00 2001 From: aeitzman Date: Thu, 7 Dec 2023 10:56:43 -0800 Subject: [PATCH 14/32] adding integration tests --- .../google/auth/oauth2/AwsCredentials.java | 6 +- .../ITWorkloadIdentityFederationTest.java | 73 ++++++++++++++++++- 2 files changed, 72 insertions(+), 7 deletions(-) diff --git a/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java b/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java index 499b2998f..c5ff93678 100644 --- a/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java @@ -56,9 +56,9 @@ import javax.annotation.Nullable; /** - * AWS credentials representing a third-party identity for calling Google APIs. - * AWS Security credentials are either sourced by calling EC2 metadata endpoints, environment - * variables, or a user provided supplier method. + * AWS credentials representing a third-party identity for calling Google APIs. AWS Security + * credentials are either sourced by calling EC2 metadata endpoints, environment variables, or a + * user provided supplier method. * *

By default, attempts to exchange the external credential for a GCP access token. */ diff --git a/oauth2_http/javatests/com/google/auth/oauth2/ITWorkloadIdentityFederationTest.java b/oauth2_http/javatests/com/google/auth/oauth2/ITWorkloadIdentityFederationTest.java index 9b4e9760b..6bbe68b16 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/ITWorkloadIdentityFederationTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/ITWorkloadIdentityFederationTest.java @@ -46,6 +46,7 @@ import com.google.api.client.json.gson.GsonFactory; import com.google.api.client.util.GenericData; import com.google.auth.http.HttpCredentialsAdapter; +import com.google.auth.oauth2.ExternalAccountCredentials.SubjectTokenTypes; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; @@ -54,6 +55,7 @@ import java.time.Instant; import java.util.HashMap; import java.util.Map; +import java.util.function.Supplier; import org.junit.Before; import org.junit.Test; @@ -145,10 +147,50 @@ public void awsCredentials() throws Exception { .setEnv("AWS_REGION", "us-east-2"); AwsCredentials awsCredential = - (AwsCredentials) - AwsCredentials.newBuilder(awsCredentialWithoutEnvProvider) - .setEnvironmentProvider(testEnvironmentProvider) - .build(); + AwsCredentials.newBuilder(awsCredentialWithoutEnvProvider) + .setEnvironmentProvider(testEnvironmentProvider) + .build(); + + callGcs(awsCredential); + } + + @Test + public void awsCredentials_withSupplier() throws Exception { + String idToken = generateGoogleIdToken(AWS_AUDIENCE); + + String url = + String.format( + "https://sts.amazonaws.com/?Action=AssumeRoleWithWebIdentity" + + "&Version=2011-06-15&DurationSeconds=3600&RoleSessionName=%s" + + "&RoleArn=%s&WebIdentityToken=%s", + AWS_ROLE_NAME, AWS_ROLE_ARN, idToken); + + HttpRequestFactory requestFactory = new NetHttpTransport().createRequestFactory(); + HttpRequest request = requestFactory.buildGetRequest(new GenericUrl(url)); + + JsonObjectParser parser = new JsonObjectParser(GsonFactory.getDefaultInstance()); + request.setParser(parser); + + HttpResponse response = request.execute(); + String rawXml = response.parseAsString(); + + String awsAccessKeyId = getXmlValueByTagName(rawXml, "AccessKeyId"); + String awsSecretAccessKey = getXmlValueByTagName(rawXml, "SecretAccessKey"); + String awsSessionToken = getXmlValueByTagName(rawXml, "SessionToken"); + + AwsSecurityCredentials credentials = + new AwsSecurityCredentials(awsAccessKeyId, awsSecretAccessKey, awsSessionToken); + + Supplier credSupplier = () -> credentials; + + AwsCredentials awsCredential = + AwsCredentials.newBuilder() + .setAwsSecurityCredentialsSupplier(credSupplier) + .setRegion("us-east-2") + .setSubjectTokenType(SubjectTokenTypes.AWS4) + .setAudience(OIDC_AUDIENCE) + .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) + .build(); callGcs(awsCredential); } @@ -193,6 +235,29 @@ public void identityPoolCredentials_withServiceAccountImpersonationOptions() thr assertTrue(minExpirationtime <= tokenExpiry && tokenExpiry <= maxExpirationTime); } + @Test + public void identityPoolCredentials_withProgrammaticAuth() throws IOException { + + Supplier tokenSupplier = + () -> { + try { + return generateGoogleIdToken(OIDC_AUDIENCE); + } catch (IOException e) { + throw new RuntimeException(e); + } + }; + + IdentityPoolCredentials identityPoolCredentials = + IdentityPoolCredentials.newBuilder() + .setSubjectTokenSupplier(tokenSupplier) + .setAudience(OIDC_AUDIENCE) + .setSubjectTokenType(SubjectTokenTypes.JWT) + .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) + .build(); + + callGcs(identityPoolCredentials); + } + private GenericJson buildIdentityPoolCredentialConfig() throws IOException { String idToken = generateGoogleIdToken(OIDC_AUDIENCE); From f09adfa411d611c1bd8856911ba39f1d470ab9f8 Mon Sep 17 00:00:00 2001 From: aeitzman Date: Thu, 7 Dec 2023 11:07:57 -0800 Subject: [PATCH 15/32] fix tests --- .../auth/oauth2/ITWorkloadIdentityFederationTest.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/oauth2_http/javatests/com/google/auth/oauth2/ITWorkloadIdentityFederationTest.java b/oauth2_http/javatests/com/google/auth/oauth2/ITWorkloadIdentityFederationTest.java index 6bbe68b16..518c1e596 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/ITWorkloadIdentityFederationTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/ITWorkloadIdentityFederationTest.java @@ -155,7 +155,7 @@ public void awsCredentials() throws Exception { } @Test - public void awsCredentials_withSupplier() throws Exception { + public void awsCredentials_withProgrammaticAuth() throws Exception { String idToken = generateGoogleIdToken(AWS_AUDIENCE); String url = @@ -188,7 +188,7 @@ public void awsCredentials_withSupplier() throws Exception { .setAwsSecurityCredentialsSupplier(credSupplier) .setRegion("us-east-2") .setSubjectTokenType(SubjectTokenTypes.AWS4) - .setAudience(OIDC_AUDIENCE) + .setAudience(AWS_AUDIENCE) .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) .build(); @@ -252,6 +252,9 @@ public void identityPoolCredentials_withProgrammaticAuth() throws IOException { .setSubjectTokenSupplier(tokenSupplier) .setAudience(OIDC_AUDIENCE) .setSubjectTokenType(SubjectTokenTypes.JWT) + .setServiceAccountImpersonationUrl(String.format( + "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/%s:generateAccessToken", + clientEmail)) .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) .build(); From 97946b3568646836595945ca53f83d3d2dd45646 Mon Sep 17 00:00:00 2001 From: aeitzman Date: Thu, 7 Dec 2023 11:18:15 -0800 Subject: [PATCH 16/32] fix tests, add javadoc, and format --- .../ITWorkloadIdentityFederationTest.java | 28 +++++++++++++++++-- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/oauth2_http/javatests/com/google/auth/oauth2/ITWorkloadIdentityFederationTest.java b/oauth2_http/javatests/com/google/auth/oauth2/ITWorkloadIdentityFederationTest.java index 518c1e596..631c38e40 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/ITWorkloadIdentityFederationTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/ITWorkloadIdentityFederationTest.java @@ -154,6 +154,15 @@ public void awsCredentials() throws Exception { callGcs(awsCredential); } + /** + * AwsCredentials (AWS Provider): Uses the service account keys to generate a Google ID token + * using the iamcredentials generateIdToken API. Exchanges the OIDC ID token for AWS security keys + * using AWS STS AssumeRoleWithWebIdentity API. These values will be returned as a + * AwsSecurityCredentials object and returned by a Supplier. The Auth library can now call get() + * from the supplier and create a signed request to AWS GetCallerIdentity. This will be used as + * the external subject token to be exchanged for a GCP access token via GCP STS endpoint and then + * to impersonate the original service account key. + */ @Test public void awsCredentials_withProgrammaticAuth() throws Exception { String idToken = generateGoogleIdToken(AWS_AUDIENCE); @@ -189,6 +198,10 @@ public void awsCredentials_withProgrammaticAuth() throws Exception { .setRegion("us-east-2") .setSubjectTokenType(SubjectTokenTypes.AWS4) .setAudience(AWS_AUDIENCE) + .setServiceAccountImpersonationUrl( + String.format( + "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/%s:generateAccessToken", + clientEmail)) .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) .build(); @@ -235,6 +248,14 @@ public void identityPoolCredentials_withServiceAccountImpersonationOptions() thr assertTrue(minExpirationtime <= tokenExpiry && tokenExpiry <= maxExpirationTime); } + /** + * IdentityPoolCredentials (OIDC provider): Uses the service account to generate a Google ID token + * using the iamcredentials generateIdToken API. This will use the service account client ID as + * the sub field of the token. This OIDC token will be used as the external subject token to be + * exchanged for a GCP access token via GCP STS endpoint and then to impersonate the original + * service account key. Retrieves the OIDC token from a Supplier that returns the subject token + * when get() is called. + */ @Test public void identityPoolCredentials_withProgrammaticAuth() throws IOException { @@ -252,9 +273,10 @@ public void identityPoolCredentials_withProgrammaticAuth() throws IOException { .setSubjectTokenSupplier(tokenSupplier) .setAudience(OIDC_AUDIENCE) .setSubjectTokenType(SubjectTokenTypes.JWT) - .setServiceAccountImpersonationUrl(String.format( - "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/%s:generateAccessToken", - clientEmail)) + .setServiceAccountImpersonationUrl( + String.format( + "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/%s:generateAccessToken", + clientEmail)) .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) .build(); From eb08391644a371e98d7af0be4b189362d2f4780e Mon Sep 17 00:00:00 2001 From: aeitzman Date: Mon, 11 Dec 2023 15:08:15 -0800 Subject: [PATCH 17/32] PR review comments --- .../oauth2/ExternalAccountCredentials.java | 58 +++++++++---------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java b/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java index f8d0b2d12..1bb10cd68 100644 --- a/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java @@ -64,34 +64,8 @@ */ public abstract class ExternalAccountCredentials extends GoogleCredentials { - /** - * Enum to specify values for the subjectTokenType field in {@code ExternalAccountCredentials}. - */ - public enum SubjectTokenTypes { - AWS4("urn:ietf:params:aws:token-type:aws4_request"), - JWT("urn:ietf:params:oauth:token-type:jwt"), - SAML2("urn:ietf:params:oauth:token-type:saml2"), - ID_TOKEN("urn:ietf:params:oauth:token-type:id_token"); - - public final String value; - - private SubjectTokenTypes(String value) { - this.value = value; - } - } - private static final long serialVersionUID = 8049126194174465023L; - /** Base credential source class. Dictates the retrieval method of the external credential. */ - abstract static class CredentialSource implements java.io.Serializable { - - private static final long serialVersionUID = 8204657811562399944L; - - CredentialSource(Map credentialSourceMap) { - checkNotNull(credentialSourceMap); - } - } - private static final String CLOUD_PLATFORM_SCOPE = "https://www.googleapis.com/auth/cloud-platform"; @@ -489,8 +463,8 @@ private static boolean isAwsCredential(Map credentialSource) { && ((String) credentialSource.get("environment_id")).startsWith("aws"); } - private boolean isImpersonationEnabled() { - return this.serviceAccountImpersonationUrl != null; + private boolean shouldBuildImpersonatedCredential() { + return this.serviceAccountImpersonationUrl != null && this.impersonatedCredentials == null; } /** @@ -503,7 +477,7 @@ private boolean isImpersonationEnabled() { protected AccessToken exchangeExternalCredentialForAccessToken( StsTokenExchangeRequest stsTokenExchangeRequest) throws IOException { // Handle service account impersonation if necessary. - if (this.isImpersonationEnabled() && this.impersonatedCredentials == null) { + if (this.shouldBuildImpersonatedCredential()) { this.impersonatedCredentials = this.buildImpersonatedCredentials(); } if (impersonatedCredentials != null) { @@ -971,4 +945,30 @@ Builder setEnvironmentProvider(EnvironmentProvider environmentProvider) { public abstract ExternalAccountCredentials build(); } + + /** + * Enum to specify values for the subjectTokenType field in {@code ExternalAccountCredentials}. + */ + public enum SubjectTokenTypes { + AWS4("urn:ietf:params:aws:token-type:aws4_request"), + JWT("urn:ietf:params:oauth:token-type:jwt"), + SAML2("urn:ietf:params:oauth:token-type:saml2"), + ID_TOKEN("urn:ietf:params:oauth:token-type:id_token"); + + public final String value; + + private SubjectTokenTypes(String value) { + this.value = value; + } + } + + /** Base credential source class. Dictates the retrieval method of the external credential. */ + abstract static class CredentialSource implements java.io.Serializable { + + private static final long serialVersionUID = 8204657811562399944L; + + CredentialSource(Map credentialSourceMap) { + checkNotNull(credentialSourceMap); + } + } } From 5ae2645e0694fbb93a51189fd577831c3d1eb07d Mon Sep 17 00:00:00 2001 From: aeitzman <12433791+aeitzman@users.noreply.github.com> Date: Tue, 12 Dec 2023 10:25:00 -0800 Subject: [PATCH 18/32] Update oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java Co-authored-by: Leo <39062083+lsirac@users.noreply.github.com> --- oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java b/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java index c5ff93678..5b954dd21 100644 --- a/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java @@ -56,7 +56,7 @@ import javax.annotation.Nullable; /** - * AWS credentials representing a third-party identity for calling Google APIs. AWS Security + * Credentials representing an AWS third-party identity for calling Google APIs. * credentials are either sourced by calling EC2 metadata endpoints, environment variables, or a * user provided supplier method. * From 61f6ae57513433a49b0b6cea443b920c3673526e Mon Sep 17 00:00:00 2001 From: aeitzman Date: Tue, 12 Dec 2023 10:36:32 -0800 Subject: [PATCH 19/32] PR comments --- oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java | 2 +- .../java/com/google/auth/oauth2/ExternalAccountCredentials.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java b/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java index 5b954dd21..408cfc494 100644 --- a/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java @@ -56,7 +56,7 @@ import javax.annotation.Nullable; /** - * Credentials representing an AWS third-party identity for calling Google APIs. + * Credentials representing an AWS third-party identity for calling Google APIs. AWS security * credentials are either sourced by calling EC2 metadata endpoints, environment variables, or a * user provided supplier method. * diff --git a/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java b/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java index 1bb10cd68..7b6fa609b 100644 --- a/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java @@ -947,7 +947,7 @@ Builder setEnvironmentProvider(EnvironmentProvider environmentProvider) { } /** - * Enum to specify values for the subjectTokenType field in {@code ExternalAccountCredentials}. + * Enum specifying values for the subjectTokenType field in {@code ExternalAccountCredentials}. */ public enum SubjectTokenTypes { AWS4("urn:ietf:params:aws:token-type:aws4_request"), From e43d708f885ef42df41683b697231c1caba30314 Mon Sep 17 00:00:00 2001 From: aeitzman Date: Wed, 13 Dec 2023 13:27:31 -0800 Subject: [PATCH 20/32] changing to aws_region instead of region to clarify usage and keep region available for future use. --- .../google/auth/oauth2/AwsCredentials.java | 27 ++++++++----------- .../auth/oauth2/AwsCredentialsTest.java | 18 ++++++------- .../ITWorkloadIdentityFederationTest.java | 2 +- 3 files changed, 21 insertions(+), 26 deletions(-) diff --git a/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java b/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java index 408cfc494..2298dfabe 100644 --- a/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java @@ -85,7 +85,7 @@ public class AwsCredentials extends ExternalAccountCredentials { @Nullable private final AwsCredentialSource awsCredentialSource; @Nullable private final Supplier awsSecurityCredentialsSupplier; @Nullable private final String regionalCredentialVerificationUrlOverride; - @Nullable private final String region; + @Nullable private final String awsRegion; /** Internal constructor. See {@link AwsCredentials.Builder}. */ AwsCredentials(Builder builder) { @@ -103,16 +103,16 @@ public class AwsCredentials extends ExternalAccountCredentials { // credentials. if (builder.awsSecurityCredentialsSupplier != null) { this.awsSecurityCredentialsSupplier = builder.awsSecurityCredentialsSupplier; - if (builder.region == null) { + if (builder.awsRegion == null) { throw new IllegalArgumentException( - "A region must be specified when using an aws security credential supplier."); + "An AWS region must be specified when using an aws security credential supplier."); } this.awsCredentialSource = null; } else { this.awsCredentialSource = (AwsCredentialSource) builder.credentialSource; this.awsSecurityCredentialsSupplier = null; } - this.region = builder.region; + this.awsRegion = builder.awsRegion; this.regionalCredentialVerificationUrlOverride = builder.regionalCredentialVerificationUrlOverride; } @@ -302,8 +302,8 @@ Map createMetadataRequestHeaders(AwsCredentialSource awsCredenti String getAwsRegion(Map metadataRequestHeaders) throws IOException { // If user has provided a region string, return that instead of checking environment or metadata // server. - if (this.region != null) { - return this.region; + if (this.awsRegion != null) { + return this.awsRegion; } String region; if (canRetrieveRegionFromEnvironment()) { @@ -395,11 +395,6 @@ String getEnv(String name) { return System.getenv(name); } - @Nullable - public String getRegion() { - return this.region; - } - @Nullable public String getRegionalCredentialVerificationUrlOverride() { return this.regionalCredentialVerificationUrlOverride; @@ -436,7 +431,7 @@ public static class Builder extends ExternalAccountCredentials.Builder { private Supplier awsSecurityCredentialsSupplier; - private String region; + private String awsRegion; private String regionalCredentialVerificationUrlOverride; @@ -444,7 +439,7 @@ public static class Builder extends ExternalAccountCredentials.Builder { Builder(AwsCredentials credentials) { super(credentials); - this.region = credentials.region; + this.awsRegion = credentials.awsRegion; this.awsSecurityCredentialsSupplier = credentials.awsSecurityCredentialsSupplier; this.regionalCredentialVerificationUrlOverride = credentials.regionalCredentialVerificationUrlOverride; @@ -468,12 +463,12 @@ public Builder setAwsSecurityCredentialsSupplier( * Sets the AWS region. Required when using an AWS Security Credentials Supplier. If set, will * override any region obtained via environment variables or the metadata endpoint. * - * @param region the aws region to set. + * @param awsRegion the aws region to set. * @return this {@code Builder} object */ @CanIgnoreReturnValue - public Builder setRegion(String region) { - this.region = region; + public Builder setAwsRegion(String awsRegion) { + this.awsRegion = awsRegion; return this; } diff --git a/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java index 212d6c600..4253ae503 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java @@ -223,7 +223,7 @@ public void refreshAccessTokenProgrammaticRefresh_withoutServiceAccountImpersona AwsCredentials awsCredential = AwsCredentials.newBuilder() - .setRegion("test") + .setAwsRegion("test") .setAwsSecurityCredentialsSupplier(CredentialSupplier) .setHttpTransportFactory(transportFactory) .setAudience("audience") @@ -251,7 +251,7 @@ public void refreshAccessTokenProgrammaticRefresh_withServiceAccountImpersonatio AwsCredentials awsCredential = AwsCredentials.newBuilder() - .setRegion("test") + .setAwsRegion("test") .setAwsSecurityCredentialsSupplier(CredentialSupplier) .setHttpTransportFactory(transportFactory) .setAudience("audience") @@ -603,7 +603,7 @@ public void retrieveSubjectToken_withProgrammaticRefresh() throws IOException { AwsCredentials awsCredential = AwsCredentials.newBuilder() - .setRegion("test") + .setAwsRegion("test") .setAwsSecurityCredentialsSupplier(CredentialSupplier) .setHttpTransportFactory(transportFactory) .setAudience("audience") @@ -646,7 +646,7 @@ public void retrieveSubjectToken_withProgrammaticRefreshSessionToken() throws IO AwsCredentials awsCredential = AwsCredentials.newBuilder() - .setRegion("test") + .setAwsRegion("test") .setAwsSecurityCredentialsSupplier(awsSecurityCredentialsSupplierWithToken) .setHttpTransportFactory(transportFactory) .setAudience("audience") @@ -691,7 +691,7 @@ public void retrieveSubjectToken_withProgrammaticRefreshWrapsError() throws IOEx AwsCredentials awsCredential = AwsCredentials.newBuilder() - .setRegion("test") + .setAwsRegion("test") .setAwsSecurityCredentialsSupplier(errorSupplier) .setHttpTransportFactory(transportFactory) .setAudience("audience") @@ -1125,7 +1125,7 @@ public void builder() throws IOException { AwsCredentials credentials = AwsCredentials.newBuilder() .setRegionalCredentialVerificationUrlOverride("https://test.com") - .setRegion("region") + .setAwsRegion("region") .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) .setAudience("audience") .setSubjectTokenType("subjectTokenType") @@ -1140,7 +1140,7 @@ public void builder() throws IOException { .setScopes(scopes) .build(); - assertEquals("region", credentials.getRegion()); + assertEquals("region", credentials.getAwsRegion(null)); assertEquals("https://test.com", credentials.getRegionalCredentialVerificationUrlOverride()); assertEquals("audience", credentials.getAudience()); assertEquals("subjectTokenType", credentials.getSubjectTokenType()); @@ -1164,7 +1164,7 @@ public void builder_defaultRegionalCredentialVerificationUrlOverride() throws IO AwsCredentials credentials = AwsCredentials.newBuilder() - .setRegion("region") + .setAwsRegion("region") .setAwsSecurityCredentialsSupplier(testSupplier) .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) .setAudience("audience") @@ -1210,7 +1210,7 @@ public void builder_regionRequiredWithSupplier() throws IOException { fail("Should not be able to continue without exception."); } catch (IllegalArgumentException exception) { assertEquals( - "A region must be specified when using an aws security credential supplier.", + "An AWS region must be specified when using an aws security credential supplier.", exception.getMessage()); } } diff --git a/oauth2_http/javatests/com/google/auth/oauth2/ITWorkloadIdentityFederationTest.java b/oauth2_http/javatests/com/google/auth/oauth2/ITWorkloadIdentityFederationTest.java index 631c38e40..c27e7a5d4 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/ITWorkloadIdentityFederationTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/ITWorkloadIdentityFederationTest.java @@ -195,7 +195,7 @@ public void awsCredentials_withProgrammaticAuth() throws Exception { AwsCredentials awsCredential = AwsCredentials.newBuilder() .setAwsSecurityCredentialsSupplier(credSupplier) - .setRegion("us-east-2") + .setAwsRegion("us-east-2") .setSubjectTokenType(SubjectTokenTypes.AWS4) .setAudience(AWS_AUDIENCE) .setServiceAccountImpersonationUrl( From 4a22e0861e0d8f685232dd500e9f7f02d844b58b Mon Sep 17 00:00:00 2001 From: aeitzman Date: Mon, 8 Jan 2024 09:46:33 -0800 Subject: [PATCH 21/32] Adding Aws Security Credential Providers --- .../google/auth/oauth2/AwsCredentials.java | 279 +++--------------- .../AwsSecurityCredentialsProvider.java | 47 +++ ...nternalAwsSecurityCredentialsProvider.java | 262 ++++++++++++++++ .../UserAwsSecurityCredentialsProvider.java | 74 +++++ .../auth/oauth2/AwsCredentialsTest.java | 157 ++-------- ...nalAwsSecurityCredentialsProviderTest.java | 140 +++++++++ 6 files changed, 585 insertions(+), 374 deletions(-) create mode 100644 oauth2_http/java/com/google/auth/oauth2/AwsSecurityCredentialsProvider.java create mode 100644 oauth2_http/java/com/google/auth/oauth2/InternalAwsSecurityCredentialsProvider.java create mode 100644 oauth2_http/java/com/google/auth/oauth2/UserAwsSecurityCredentialsProvider.java create mode 100644 oauth2_http/javatests/com/google/auth/oauth2/InternalAwsSecurityCredentialsProviderTest.java diff --git a/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java b/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java index 2298dfabe..dd16cd205 100644 --- a/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java @@ -31,18 +31,9 @@ package com.google.auth.oauth2; -import com.google.api.client.http.GenericUrl; -import com.google.api.client.http.HttpContent; -import com.google.api.client.http.HttpHeaders; -import com.google.api.client.http.HttpMethods; -import com.google.api.client.http.HttpRequest; -import com.google.api.client.http.HttpRequestFactory; -import com.google.api.client.http.HttpResponse; import com.google.api.client.json.GenericJson; -import com.google.api.client.json.JsonParser; import com.google.auth.http.HttpTransportFactory; import com.google.common.annotations.VisibleForTesting; -import com.google.common.collect.ImmutableList; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.io.IOException; import java.io.UnsupportedEncodingException; @@ -64,27 +55,18 @@ */ public class AwsCredentials extends ExternalAccountCredentials { - // Supported environment variables. - static final String AWS_REGION = "AWS_REGION"; - static final String AWS_DEFAULT_REGION = "AWS_DEFAULT_REGION"; - static final String AWS_ACCESS_KEY_ID = "AWS_ACCESS_KEY_ID"; - static final String AWS_SECRET_ACCESS_KEY = "AWS_SECRET_ACCESS_KEY"; - static final String AWS_SESSION_TOKEN = "AWS_SESSION_TOKEN"; - static final String DEFAULT_REGIONAL_CREDENTIAL_VERIFICATION_URL = "https://sts.{region}.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15"; - static final String AWS_IMDSV2_SESSION_TOKEN_HEADER = "x-aws-ec2-metadata-token"; - static final String AWS_IMDSV2_SESSION_TOKEN_TTL_HEADER = "x-aws-ec2-metadata-token-ttl-seconds"; - static final String AWS_IMDSV2_SESSION_TOKEN_TTL = "300"; - static final String AWS_METRICS_HEADER_VALUE = "aws"; private static final long serialVersionUID = -3670131891574618105L; - @Nullable private final AwsCredentialSource awsCredentialSource; - @Nullable private final Supplier awsSecurityCredentialsSupplier; + @Nullable private final AwsSecurityCredentialsProvider awsSecurityCredentialsProvider; + // Regional credential verification url override. This needs to be its own value so we can + // correctly pass it to a builder. @Nullable private final String regionalCredentialVerificationUrlOverride; + @Nullable private final String regionalCredentialVerificationUrl; @Nullable private final String awsRegion; /** Internal constructor. See {@link AwsCredentials.Builder}. */ @@ -99,22 +81,36 @@ public class AwsCredentials extends ExternalAccountCredentials { throw new IllegalArgumentException( "An awsSecurityCredentialsSupplier or a credentialSource must be provided."); } + + AwsCredentialSource credentialSource = (AwsCredentialSource) builder.credentialSource; + this.awsRegion = builder.awsRegion; + // Set regional credential verification url override if provided. + this.regionalCredentialVerificationUrlOverride = + builder.regionalCredentialVerificationUrlOverride; + + // Set regional credential verification url depending on inputs. + if (this.regionalCredentialVerificationUrlOverride != null) { + this.regionalCredentialVerificationUrl = this.regionalCredentialVerificationUrlOverride; + } else if (credentialSource != null) { + this.regionalCredentialVerificationUrl = credentialSource.regionalCredentialVerificationUrl; + } else { + this.regionalCredentialVerificationUrl = DEFAULT_REGIONAL_CREDENTIAL_VERIFICATION_URL; + } + // If user has provided a security credential supplier, use that to retrieve the AWS security // credentials. if (builder.awsSecurityCredentialsSupplier != null) { - this.awsSecurityCredentialsSupplier = builder.awsSecurityCredentialsSupplier; - if (builder.awsRegion == null) { - throw new IllegalArgumentException( - "An AWS region must be specified when using an aws security credential supplier."); - } - this.awsCredentialSource = null; + this.awsSecurityCredentialsProvider = + new UserAwsSecurityCredentialsProvider( + builder.awsSecurityCredentialsSupplier, this.awsRegion); } else { - this.awsCredentialSource = (AwsCredentialSource) builder.credentialSource; - this.awsSecurityCredentialsSupplier = null; + this.awsSecurityCredentialsProvider = + new InternalAwsSecurityCredentialsProvider( + credentialSource, + this.getEnvironmentProvider(), + this.transportFactory, + builder.awsRegion); } - this.awsRegion = builder.awsRegion; - this.regionalCredentialVerificationUrlOverride = - builder.regionalCredentialVerificationUrlOverride; } @Override @@ -134,16 +130,12 @@ public AccessToken refreshAccessToken() throws IOException { @Override public String retrieveSubjectToken() throws IOException { - Map metadataRequestHeaders = new HashMap<>(); - if (shouldUseMetadataServer()) { - metadataRequestHeaders = createMetadataRequestHeaders(awsCredentialSource); - } // The targeted region is required to generate the signed request. The regional // endpoint must also be used. - String region = getAwsRegion(metadataRequestHeaders); + String region = awsSecurityCredentialsProvider.getRegion(); - AwsSecurityCredentials credentials = getAwsSecurityCredentials(metadataRequestHeaders); + AwsSecurityCredentials credentials = awsSecurityCredentialsProvider.getCredentials(); // Generate the signed request to the AWS STS GetCallerIdentity API. Map headers = new HashMap<>(); @@ -153,7 +145,7 @@ public String retrieveSubjectToken() throws IOException { AwsRequestSigner.newBuilder( credentials, "POST", - this.getRegionalCredentialVerificationUrl().replace("{region}", region), + this.regionalCredentialVerificationUrl.replace("{region}", region), region) .setAdditionalHeaders(headers) .build(); @@ -170,41 +162,12 @@ public GoogleCredentials createScoped(Collection newScopes) { @Override String getCredentialSourceType() { - if (this.awsSecurityCredentialsSupplier != null) { + if (this.awsSecurityCredentialsProvider.isUserSupplied()) { return PROGRAMMATIC_AUTH_METRICS_HEADER_VALUE; } return AWS_METRICS_HEADER_VALUE; } - private String retrieveResource(String url, String resourceName, Map headers) - throws IOException { - return retrieveResource(url, resourceName, HttpMethods.GET, headers, /* content= */ null); - } - - private String retrieveResource( - String url, - String resourceName, - String requestMethod, - Map headers, - @Nullable HttpContent content) - throws IOException { - try { - HttpRequestFactory requestFactory = transportFactory.create().createRequestFactory(); - HttpRequest request = - requestFactory.buildRequest(requestMethod, new GenericUrl(url), content); - - HttpHeaders requestHeaders = request.getHeaders(); - for (Map.Entry header : headers.entrySet()) { - requestHeaders.set(header.getKey(), header.getValue()); - } - - HttpResponse response = request.execute(); - return response.parseAsString(); - } catch (IOException e) { - throw new IOException(String.format("Failed to retrieve AWS %s.", resourceName), e); - } - } - private String buildSubjectToken(AwsRequestSignature signature) throws UnsupportedEncodingException { Map canonicalHeaders = signature.getCanonicalHeaders(); @@ -224,170 +187,13 @@ private String buildSubjectToken(AwsRequestSignature signature) token.put("headers", headerList); token.put("method", signature.getHttpMethod()); token.put( - "url", - this.getRegionalCredentialVerificationUrl().replace("{region}", signature.getRegion())); + "url", this.regionalCredentialVerificationUrl.replace("{region}", signature.getRegion())); return URLEncoder.encode(token.toString(), "UTF-8"); } - private boolean canRetrieveRegionFromEnvironment() { - // The AWS region can be provided through AWS_REGION or AWS_DEFAULT_REGION. Only one is - // required. - List keys = ImmutableList.of(AWS_REGION, AWS_DEFAULT_REGION); - for (String env : keys) { - String value = getEnvironmentProvider().getEnv(env); - if (value != null && value.trim().length() > 0) { - // Region available. - return true; - } - } - return false; - } - - private boolean canRetrieveSecurityCredentialsFromEnvironment() { - // Check if both AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY are available. - List keys = ImmutableList.of(AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY); - for (String env : keys) { - String value = getEnvironmentProvider().getEnv(env); - if (value == null || value.trim().length() == 0) { - // Return false if one of them are missing. - return false; - } - } - return true; - } - - @VisibleForTesting - boolean shouldUseMetadataServer() { - return this.awsSecurityCredentialsSupplier == null - && (!canRetrieveRegionFromEnvironment() - || !canRetrieveSecurityCredentialsFromEnvironment()); - } - - @VisibleForTesting - Map createMetadataRequestHeaders(AwsCredentialSource awsCredentialSource) - throws IOException { - Map metadataRequestHeaders = new HashMap<>(); - - // AWS IDMSv2 introduced a requirement for a session token to be present - // with the requests made to metadata endpoints. This requirement is to help - // prevent SSRF attacks. - // Presence of "imdsv2_session_token_url" in Credential Source of config file - // will trigger a flow with session token, else there will not be a session - // token with the metadata requests. - // Both flows work for IDMS v1 and v2. But if IDMSv2 is enabled, then if - // session token is not present, Unauthorized exception will be thrown. - if (awsCredentialSource.imdsv2SessionTokenUrl != null) { - Map tokenRequestHeaders = - new HashMap() { - { - put(AWS_IMDSV2_SESSION_TOKEN_TTL_HEADER, AWS_IMDSV2_SESSION_TOKEN_TTL); - } - }; - - String imdsv2SessionToken = - retrieveResource( - awsCredentialSource.imdsv2SessionTokenUrl, - "Session Token", - HttpMethods.PUT, - tokenRequestHeaders, - /* content= */ null); - - metadataRequestHeaders.put(AWS_IMDSV2_SESSION_TOKEN_HEADER, imdsv2SessionToken); - } - - return metadataRequestHeaders; - } - - @VisibleForTesting - String getAwsRegion(Map metadataRequestHeaders) throws IOException { - // If user has provided a region string, return that instead of checking environment or metadata - // server. - if (this.awsRegion != null) { - return this.awsRegion; - } - String region; - if (canRetrieveRegionFromEnvironment()) { - // For AWS Lambda, the region is retrieved through the AWS_REGION environment variable. - region = getEnvironmentProvider().getEnv(AWS_REGION); - if (region != null && region.trim().length() > 0) { - return region; - } - return getEnvironmentProvider().getEnv(AWS_DEFAULT_REGION); - } - - if (awsCredentialSource.regionUrl == null || awsCredentialSource.regionUrl.isEmpty()) { - throw new IOException( - "Unable to determine the AWS region. The credential source does not contain the region URL."); - } - - region = retrieveResource(awsCredentialSource.regionUrl, "region", metadataRequestHeaders); - - // There is an extra appended character that must be removed. If `us-east-1b` is returned, - // we want `us-east-1`. - return region.substring(0, region.length() - 1); - } - - @VisibleForTesting - AwsSecurityCredentials getAwsSecurityCredentials(Map metadataRequestHeaders) - throws IOException { - // If this credential is using programmatic auth, call the user provided supplier. - if (this.awsSecurityCredentialsSupplier != null) { - try { - return this.awsSecurityCredentialsSupplier.get(); - } catch (Throwable e) { - throw new GoogleAuthException( - /* isRetryable= */ false, - /* retryCount= */ 0, - "Error retrieving token from AWS security credentials supplier.", - e); - } - } - - // Check environment variables for credentials first. - if (canRetrieveSecurityCredentialsFromEnvironment()) { - String accessKeyId = getEnvironmentProvider().getEnv(AWS_ACCESS_KEY_ID); - String secretAccessKey = getEnvironmentProvider().getEnv(AWS_SECRET_ACCESS_KEY); - String token = getEnvironmentProvider().getEnv(AWS_SESSION_TOKEN); - return new AwsSecurityCredentials(accessKeyId, secretAccessKey, token); - } - - // Credentials not retrievable from environment variables - call metadata server. - // Retrieve the IAM role that is attached to the VM. This is required to retrieve the AWS - // security credentials. - if (awsCredentialSource.url == null || awsCredentialSource.url.isEmpty()) { - throw new IOException( - "Unable to determine the AWS IAM role name. The credential source does not contain the" - + " url field."); - } - String roleName = retrieveResource(awsCredentialSource.url, "IAM role", metadataRequestHeaders); - - // Retrieve the AWS security credentials by calling the endpoint specified by the credential - // source. - String awsCredentials = - retrieveResource( - awsCredentialSource.url + "/" + roleName, "credentials", metadataRequestHeaders); - - JsonParser parser = OAuth2Utils.JSON_FACTORY.createJsonParser(awsCredentials); - GenericJson genericJson = parser.parseAndClose(GenericJson.class); - - String accessKeyId = (String) genericJson.get("AccessKeyId"); - String secretAccessKey = (String) genericJson.get("SecretAccessKey"); - String token = (String) genericJson.get("Token"); - - // These credentials last for a few hours - we may consider caching these in the - // future. - return new AwsSecurityCredentials(accessKeyId, secretAccessKey, token); - } - @VisibleForTesting String getRegionalCredentialVerificationUrl() { - if (this.regionalCredentialVerificationUrlOverride != null) { - return this.regionalCredentialVerificationUrlOverride; - } else if (this.awsCredentialSource != null) { - return this.awsCredentialSource.regionalCredentialVerificationUrl; - } else { - return DEFAULT_REGIONAL_CREDENTIAL_VERIFICATION_URL; - } + return this.regionalCredentialVerificationUrl; } @VisibleForTesting @@ -395,14 +201,14 @@ String getEnv(String name) { return System.getenv(name); } - @Nullable - public String getRegionalCredentialVerificationUrlOverride() { - return this.regionalCredentialVerificationUrlOverride; + @VisibleForTesting + AwsSecurityCredentialsProvider getAwsSecurityCredentialsProvider() { + return this.awsSecurityCredentialsProvider; } @Nullable - public Supplier getAwsSecurityCredentialsSupplier() { - return this.awsSecurityCredentialsSupplier; + public String getRegionalCredentialVerificationUrlOverride() { + return this.regionalCredentialVerificationUrlOverride; } private static GenericJson formatTokenHeaderForSts(String key, String value) { @@ -440,7 +246,10 @@ public static class Builder extends ExternalAccountCredentials.Builder { Builder(AwsCredentials credentials) { super(credentials); this.awsRegion = credentials.awsRegion; - this.awsSecurityCredentialsSupplier = credentials.awsSecurityCredentialsSupplier; + if (credentials.awsSecurityCredentialsProvider.isUserSupplied()) { + this.awsSecurityCredentialsSupplier = + credentials.awsSecurityCredentialsProvider.getSupplier(); + } this.regionalCredentialVerificationUrlOverride = credentials.regionalCredentialVerificationUrlOverride; } diff --git a/oauth2_http/java/com/google/auth/oauth2/AwsSecurityCredentialsProvider.java b/oauth2_http/java/com/google/auth/oauth2/AwsSecurityCredentialsProvider.java new file mode 100644 index 000000000..45e4ca4eb --- /dev/null +++ b/oauth2_http/java/com/google/auth/oauth2/AwsSecurityCredentialsProvider.java @@ -0,0 +1,47 @@ +/* + * Copyright 2024 Google LLC + * + * 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 LLC 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.oauth2; + +import java.io.IOException; +import java.io.Serializable; +import java.util.function.Supplier; + +interface AwsSecurityCredentialsProvider extends Serializable { + + String getRegion() throws IOException; + + AwsSecurityCredentials getCredentials() throws IOException; + + boolean isUserSupplied(); + + Supplier getSupplier(); +} diff --git a/oauth2_http/java/com/google/auth/oauth2/InternalAwsSecurityCredentialsProvider.java b/oauth2_http/java/com/google/auth/oauth2/InternalAwsSecurityCredentialsProvider.java new file mode 100644 index 000000000..944028cb9 --- /dev/null +++ b/oauth2_http/java/com/google/auth/oauth2/InternalAwsSecurityCredentialsProvider.java @@ -0,0 +1,262 @@ +/* + * Copyright 2024 Google LLC + * + * 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 LLC 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.oauth2; + +import com.google.api.client.http.GenericUrl; +import com.google.api.client.http.HttpContent; +import com.google.api.client.http.HttpHeaders; +import com.google.api.client.http.HttpMethods; +import com.google.api.client.http.HttpRequest; +import com.google.api.client.http.HttpRequestFactory; +import com.google.api.client.http.HttpResponse; +import com.google.api.client.json.GenericJson; +import com.google.api.client.json.JsonParser; +import com.google.appengine.repackaged.com.google.common.collect.ImmutableList; +import com.google.auth.http.HttpTransportFactory; +import com.google.common.annotations.VisibleForTesting; +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; +import javax.annotation.Nullable; + +class InternalAwsSecurityCredentialsProvider implements AwsSecurityCredentialsProvider { + private static final long serialVersionUID = 4438370785261365013L; + + // Supported environment variables. + static final String AWS_REGION = "AWS_REGION"; + static final String AWS_DEFAULT_REGION = "AWS_DEFAULT_REGION"; + static final String AWS_ACCESS_KEY_ID = "AWS_ACCESS_KEY_ID"; + static final String AWS_SECRET_ACCESS_KEY = "AWS_SECRET_ACCESS_KEY"; + static final String AWS_SESSION_TOKEN = "AWS_SESSION_TOKEN"; + + static final String AWS_IMDSV2_SESSION_TOKEN_HEADER = "x-aws-ec2-metadata-token"; + static final String AWS_IMDSV2_SESSION_TOKEN_TTL_HEADER = "x-aws-ec2-metadata-token-ttl-seconds"; + static final String AWS_IMDSV2_SESSION_TOKEN_TTL = "300"; + + private final AwsCredentialSource awsCredentialSource; + private EnvironmentProvider environmentProvider; + private transient HttpTransportFactory transportFactory; + private final String region; + + InternalAwsSecurityCredentialsProvider( + AwsCredentialSource awsCredentialSource, + EnvironmentProvider environmentProvider, + HttpTransportFactory transportFactory, + String region) { + this.environmentProvider = environmentProvider; + this.awsCredentialSource = awsCredentialSource; + this.transportFactory = transportFactory; + this.region = region; + } + + public AwsSecurityCredentials getCredentials() throws IOException { + // Check environment variables for credentials first. + if (canRetrieveSecurityCredentialsFromEnvironment()) { + String accessKeyId = environmentProvider.getEnv(AWS_ACCESS_KEY_ID); + String secretAccessKey = environmentProvider.getEnv(AWS_SECRET_ACCESS_KEY); + String token = environmentProvider.getEnv(AWS_SESSION_TOKEN); + return new AwsSecurityCredentials(accessKeyId, secretAccessKey, token); + } + + Map metadataRequestHeaders = createMetadataRequestHeaders(awsCredentialSource); + + // Credentials not retrievable from environment variables - call metadata server. + // Retrieve the IAM role that is attached to the VM. This is required to retrieve the AWS + // security credentials. + if (awsCredentialSource.url == null || awsCredentialSource.url.isEmpty()) { + throw new IOException( + "Unable to determine the AWS IAM role name. The credential source does not contain the" + + " url field."); + } + String roleName = retrieveResource(awsCredentialSource.url, "IAM role", metadataRequestHeaders); + + // Retrieve the AWS security credentials by calling the endpoint specified by the credential + // source. + String awsCredentials = + retrieveResource( + awsCredentialSource.url + "/" + roleName, "credentials", metadataRequestHeaders); + + JsonParser parser = OAuth2Utils.JSON_FACTORY.createJsonParser(awsCredentials); + GenericJson genericJson = parser.parseAndClose(GenericJson.class); + + String accessKeyId = (String) genericJson.get("AccessKeyId"); + String secretAccessKey = (String) genericJson.get("SecretAccessKey"); + String token = (String) genericJson.get("Token"); + + // These credentials last for a few hours - we may consider caching these in the + // future. + return new AwsSecurityCredentials(accessKeyId, secretAccessKey, token); + } + + public String getRegion() throws IOException { + // If user has provided a region string, return that instead of checking environment or metadata + // server. + if (this.region != null) { + return this.region; + } + String region; + if (canRetrieveRegionFromEnvironment()) { + // For AWS Lambda, the region is retrieved through the AWS_REGION environment variable. + region = environmentProvider.getEnv(AWS_REGION); + if (region != null && region.trim().length() > 0) { + return region; + } + return environmentProvider.getEnv(AWS_DEFAULT_REGION); + } + + Map metadataRequestHeaders = createMetadataRequestHeaders(awsCredentialSource); + + if (awsCredentialSource.regionUrl == null || awsCredentialSource.regionUrl.isEmpty()) { + throw new IOException( + "Unable to determine the AWS region. The credential source does not contain the region URL."); + } + + region = retrieveResource(awsCredentialSource.regionUrl, "region", metadataRequestHeaders); + + // There is an extra appended character that must be removed. If `us-east-1b` is returned, + // we want `us-east-1`. + return region.substring(0, region.length() - 1); + } + + private boolean canRetrieveRegionFromEnvironment() { + // The AWS region can be provided through AWS_REGION or AWS_DEFAULT_REGION. Only one is + // required. + List keys = ImmutableList.of(AWS_REGION, AWS_DEFAULT_REGION); + for (String env : keys) { + String value = environmentProvider.getEnv(env); + if (value != null && value.trim().length() > 0) { + // Region available. + return true; + } + } + return false; + } + + public boolean isUserSupplied() { + return false; + } + + public Supplier getSupplier() { + return () -> { + try { + return this.getCredentials(); + } catch (IOException e) { + throw new RuntimeException(e); + } + }; + } + + private boolean canRetrieveSecurityCredentialsFromEnvironment() { + // Check if both AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY are available. + List keys = ImmutableList.of(AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY); + for (String env : keys) { + String value = environmentProvider.getEnv(env); + if (value == null || value.trim().length() == 0) { + // Return false if one of them are missing. + return false; + } + } + return true; + } + + @VisibleForTesting + boolean shouldUseMetadataServer() { + return (!canRetrieveRegionFromEnvironment() + || !canRetrieveSecurityCredentialsFromEnvironment()); + } + + private String retrieveResource(String url, String resourceName, Map headers) + throws IOException { + return retrieveResource(url, resourceName, HttpMethods.GET, headers, /* content= */ null); + } + + private String retrieveResource( + String url, + String resourceName, + String requestMethod, + Map headers, + @Nullable HttpContent content) + throws IOException { + try { + HttpRequestFactory requestFactory = transportFactory.create().createRequestFactory(); + HttpRequest request = + requestFactory.buildRequest(requestMethod, new GenericUrl(url), content); + + HttpHeaders requestHeaders = request.getHeaders(); + for (Map.Entry header : headers.entrySet()) { + requestHeaders.set(header.getKey(), header.getValue()); + } + + HttpResponse response = request.execute(); + return response.parseAsString(); + } catch (IOException e) { + throw new IOException(String.format("Failed to retrieve AWS %s.", resourceName), e); + } + } + + @VisibleForTesting + Map createMetadataRequestHeaders(AwsCredentialSource awsCredentialSource) + throws IOException { + Map metadataRequestHeaders = new HashMap<>(); + + // AWS IDMSv2 introduced a requirement for a session token to be present + // with the requests made to metadata endpoints. This requirement is to help + // prevent SSRF attacks. + // Presence of "imdsv2_session_token_url" in Credential Source of config file + // will trigger a flow with session token, else there will not be a session + // token with the metadata requests. + // Both flows work for IDMS v1 and v2. But if IDMSv2 is enabled, then if + // session token is not present, Unauthorized exception will be thrown. + if (awsCredentialSource.imdsv2SessionTokenUrl != null) { + Map tokenRequestHeaders = + new HashMap() { + { + put(AWS_IMDSV2_SESSION_TOKEN_TTL_HEADER, AWS_IMDSV2_SESSION_TOKEN_TTL); + } + }; + + String imdsv2SessionToken = + retrieveResource( + awsCredentialSource.imdsv2SessionTokenUrl, + "Session Token", + HttpMethods.PUT, + tokenRequestHeaders, + /* content= */ null); + + metadataRequestHeaders.put(AWS_IMDSV2_SESSION_TOKEN_HEADER, imdsv2SessionToken); + } + + return metadataRequestHeaders; + } +} diff --git a/oauth2_http/java/com/google/auth/oauth2/UserAwsSecurityCredentialsProvider.java b/oauth2_http/java/com/google/auth/oauth2/UserAwsSecurityCredentialsProvider.java new file mode 100644 index 000000000..63c84089c --- /dev/null +++ b/oauth2_http/java/com/google/auth/oauth2/UserAwsSecurityCredentialsProvider.java @@ -0,0 +1,74 @@ +/* + * Copyright 2024 Google LLC + * + * 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 LLC 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.oauth2; + +import java.util.function.Supplier; + +class UserAwsSecurityCredentialsProvider implements AwsSecurityCredentialsProvider { + private static final long serialVersionUID = 6699948149655089007L; + + private final String region; + private final transient Supplier awsSecurityCredentialsSupplier; + + UserAwsSecurityCredentialsProvider(Supplier supplier, String region) { + if (region == null || region.trim().isEmpty()) { + throw new IllegalArgumentException( + "An AWS region must be specified when using an aws security credential supplier."); + } + this.region = region; + this.awsSecurityCredentialsSupplier = supplier; + } + + public String getRegion() { + return this.region; + } + + public AwsSecurityCredentials getCredentials() throws GoogleAuthException { + try { + return this.awsSecurityCredentialsSupplier.get(); + } catch (RuntimeException e) { + throw new GoogleAuthException( + /* isRetryable= */ false, + /* retryCount= */ 0, + "Error retrieving token from AWS security credentials supplier.", + e); + } + } + + public boolean isUserSupplied() { + return true; + } + + public Supplier getSupplier() { + return this.awsSecurityCredentialsSupplier; + } +} diff --git a/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java index 4253ae503..5559a306c 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java @@ -45,7 +45,6 @@ import com.google.api.client.util.Clock; import com.google.auth.TestUtils; import com.google.auth.oauth2.ExternalAccountCredentialsTest.MockExternalAccountCredentialsTransportFactory; -import com.google.common.collect.ImmutableList; import java.io.IOException; import java.io.InputStream; import java.net.URI; @@ -348,7 +347,7 @@ public void retrieveSubjectTokenWithSessionTokenUrl() throws IOException { assertNotNull(headers.get("Authorization")); List requests = transportFactory.transport.getRequests(); - assertEquals(4, requests.size()); + assertEquals(5, requests.size()); // Validate the session token request ValidateRequest( @@ -357,15 +356,17 @@ public void retrieveSubjectTokenWithSessionTokenUrl() throws IOException { new HashMap() { { put( - AwsCredentials.AWS_IMDSV2_SESSION_TOKEN_TTL_HEADER, - AwsCredentials.AWS_IMDSV2_SESSION_TOKEN_TTL); + InternalAwsSecurityCredentialsProvider.AWS_IMDSV2_SESSION_TOKEN_TTL_HEADER, + InternalAwsSecurityCredentialsProvider.AWS_IMDSV2_SESSION_TOKEN_TTL); } }); Map sessionTokenHeader = new HashMap() { { - put(AwsCredentials.AWS_IMDSV2_SESSION_TOKEN_HEADER, AWS_IMDSV2_SESSION_TOKEN); + put( + InternalAwsSecurityCredentialsProvider.AWS_IMDSV2_SESSION_TOKEN_HEADER, + AWS_IMDSV2_SESSION_TOKEN); } }; @@ -373,10 +374,10 @@ public void retrieveSubjectTokenWithSessionTokenUrl() throws IOException { ValidateRequest(requests.get(1), AWS_REGION_URL, sessionTokenHeader); // Validate role request. - ValidateRequest(requests.get(2), AWS_CREDENTIALS_URL, sessionTokenHeader); + ValidateRequest(requests.get(3), AWS_CREDENTIALS_URL, sessionTokenHeader); // Validate security credentials request. - ValidateRequest(requests.get(3), AWS_CREDENTIALS_URL_WITH_ROLE, sessionTokenHeader); + ValidateRequest(requests.get(4), AWS_CREDENTIALS_URL_WITH_ROLE, sessionTokenHeader); } @Test @@ -722,7 +723,7 @@ public void getAwsSecurityCredentials_fromEnvironmentVariablesNoToken() throws I .build(); AwsSecurityCredentials credentials = - testAwsCredentials.getAwsSecurityCredentials(EMPTY_METADATA_HEADERS); + testAwsCredentials.getAwsSecurityCredentialsProvider().getCredentials(); assertEquals("awsAccessKeyId", credentials.getAccessKeyId()); assertEquals("awsSecretAccessKey", credentials.getSecretAccessKey()); @@ -755,7 +756,7 @@ public void getAwsSecurityCredentials_fromEnvironmentVariablesWithToken() throws .build(); AwsSecurityCredentials credentials = - testAwsCredentials.getAwsSecurityCredentials(EMPTY_METADATA_HEADERS); + testAwsCredentials.getAwsSecurityCredentialsProvider().getCredentials(); assertEquals("awsAccessKeyId", credentials.getAccessKeyId()); assertEquals("awsSecretAccessKey", credentials.getSecretAccessKey()); @@ -777,7 +778,7 @@ public void getAwsSecurityCredentials_fromEnvironmentVariables_noMetadataServerC .build(); AwsSecurityCredentials credentials = - testAwsCredentials.getAwsSecurityCredentials(EMPTY_METADATA_HEADERS); + testAwsCredentials.getAwsSecurityCredentialsProvider().getCredentials(); assertEquals("awsAccessKeyId", credentials.getAccessKeyId()); assertEquals("awsSecretAccessKey", credentials.getSecretAccessKey()); @@ -796,7 +797,7 @@ public void getAwsSecurityCredentials_fromMetadataServer() throws IOException { .build(); AwsSecurityCredentials credentials = - awsCredential.getAwsSecurityCredentials(EMPTY_METADATA_HEADERS); + awsCredential.getAwsSecurityCredentialsProvider().getCredentials(); assertEquals("accessKeyId", credentials.getAccessKeyId()); assertEquals("secretAccessKey", credentials.getSecretAccessKey()); @@ -828,7 +829,7 @@ public void getAwsSecurityCredentials_fromMetadataServer_noUrlProvided() { .build(); try { - awsCredential.getAwsSecurityCredentials(EMPTY_METADATA_HEADERS); + awsCredential.getAwsSecurityCredentialsProvider().getCredentials(); fail("Should not be able to use credential without exception."); } catch (IOException exception) { assertEquals( @@ -856,7 +857,7 @@ public void getAwsRegion_awsRegionEnvironmentVariable() throws IOException { .setEnvironmentProvider(environmentProvider) .build(); - String region = awsCredentials.getAwsRegion(EMPTY_METADATA_HEADERS); + String region = awsCredentials.getAwsSecurityCredentialsProvider().getRegion(); // Should attempt to retrieve the region from AWS_REGION env var first. // Metadata server would return us-east-1b. @@ -881,7 +882,7 @@ public void getAwsRegion_awsDefaultRegionEnvironmentVariable() throws IOExceptio .setEnvironmentProvider(environmentProvider) .build(); - String region = awsCredentials.getAwsRegion(EMPTY_METADATA_HEADERS); + String region = awsCredentials.getAwsSecurityCredentialsProvider().getRegion(); // Should attempt to retrieve the region from DEFAULT_AWS_REGION before calling the metadata // server. Metadata server would return us-east-1b. @@ -902,7 +903,7 @@ public void getAwsRegion_metadataServer() throws IOException { .setCredentialSource(buildAwsCredentialSource(transportFactory)) .build(); - String region = awsCredentials.getAwsRegion(EMPTY_METADATA_HEADERS); + String region = awsCredentials.getAwsSecurityCredentialsProvider().getRegion(); // Should retrieve the region from the Metadata server. String expectedRegion = @@ -994,128 +995,6 @@ public void credentialSource_missingRegionalCredVerificationUrl() { } } - @Test - public void shouldUseMetadataServer_withRequiredEnvironmentVariables() { - MockExternalAccountCredentialsTransportFactory transportFactory = - new MockExternalAccountCredentialsTransportFactory(); - - // Add required environment variables. - List regionKeys = ImmutableList.of("AWS_REGION", "AWS_DEFAULT_REGION"); - for (String regionKey : regionKeys) { - TestEnvironmentProvider environmentProvider = new TestEnvironmentProvider(); - // AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY are always required. - environmentProvider - .setEnv(regionKey, "awsRegion") - .setEnv("AWS_ACCESS_KEY_ID", "awsAccessKeyId") - .setEnv("AWS_SECRET_ACCESS_KEY", "awsSecretAccessKey"); - AwsCredentials awsCredential = - AwsCredentials.newBuilder(AWS_CREDENTIAL) - .setHttpTransportFactory(transportFactory) - .setCredentialSource(buildAwsImdsv2CredentialSource(transportFactory)) - .setEnvironmentProvider(environmentProvider) - .build(); - assertFalse(awsCredential.shouldUseMetadataServer()); - } - } - - @Test - public void shouldUseMetadataServer_missingRegion() { - MockExternalAccountCredentialsTransportFactory transportFactory = - new MockExternalAccountCredentialsTransportFactory(); - - TestEnvironmentProvider environmentProvider = new TestEnvironmentProvider(); - environmentProvider - .setEnv("AWS_ACCESS_KEY_ID", "awsAccessKeyId") - .setEnv("AWS_SECRET_ACCESS_KEY", "awsSecretAccessKey"); - AwsCredentials awsCredential = - AwsCredentials.newBuilder(AWS_CREDENTIAL) - .setHttpTransportFactory(transportFactory) - .setCredentialSource(buildAwsImdsv2CredentialSource(transportFactory)) - .setEnvironmentProvider(environmentProvider) - .build(); - assertTrue(awsCredential.shouldUseMetadataServer()); - } - - @Test - public void shouldUseMetadataServer_missingAwsAccessKeyId() { - MockExternalAccountCredentialsTransportFactory transportFactory = - new MockExternalAccountCredentialsTransportFactory(); - - // Add required environment variables. - List regionKeys = ImmutableList.of("AWS_REGION", "AWS_DEFAULT_REGION"); - for (String regionKey : regionKeys) { - TestEnvironmentProvider environmentProvider = new TestEnvironmentProvider(); - // AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY are always required. - environmentProvider - .setEnv(regionKey, "awsRegion") - .setEnv("AWS_SECRET_ACCESS_KEY", "awsSecretAccessKey"); - AwsCredentials awsCredential = - AwsCredentials.newBuilder(AWS_CREDENTIAL) - .setHttpTransportFactory(transportFactory) - .setCredentialSource(buildAwsImdsv2CredentialSource(transportFactory)) - .setEnvironmentProvider(environmentProvider) - .build(); - assertTrue(awsCredential.shouldUseMetadataServer()); - } - } - - @Test - public void shouldUseMetadataServer_missingAwsSecretAccessKey() { - MockExternalAccountCredentialsTransportFactory transportFactory = - new MockExternalAccountCredentialsTransportFactory(); - - // Add required environment variables. - List regionKeys = ImmutableList.of("AWS_REGION", "AWS_DEFAULT_REGION"); - for (String regionKey : regionKeys) { - TestEnvironmentProvider environmentProvider = new TestEnvironmentProvider(); - // AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY are always required. - environmentProvider - .setEnv(regionKey, "awsRegion") - .setEnv("AWS_ACCESS_KEY_ID", "awsAccessKeyId"); - AwsCredentials awsCredential = - AwsCredentials.newBuilder(AWS_CREDENTIAL) - .setHttpTransportFactory(transportFactory) - .setCredentialSource(buildAwsImdsv2CredentialSource(transportFactory)) - .setEnvironmentProvider(environmentProvider) - .build(); - assertTrue(awsCredential.shouldUseMetadataServer()); - } - } - - @Test - public void shouldUseMetadataServer_missingAwsSecurityCreds() { - MockExternalAccountCredentialsTransportFactory transportFactory = - new MockExternalAccountCredentialsTransportFactory(); - - // Add required environment variables. - List regionKeys = ImmutableList.of("AWS_REGION", "AWS_DEFAULT_REGION"); - for (String regionKey : regionKeys) { - TestEnvironmentProvider environmentProvider = new TestEnvironmentProvider(); - // AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY are always required. - // Not set here. - environmentProvider.setEnv(regionKey, "awsRegion"); - AwsCredentials awsCredential = - AwsCredentials.newBuilder(AWS_CREDENTIAL) - .setHttpTransportFactory(transportFactory) - .setCredentialSource(buildAwsImdsv2CredentialSource(transportFactory)) - .setEnvironmentProvider(environmentProvider) - .build(); - assertTrue(awsCredential.shouldUseMetadataServer()); - } - } - - @Test - public void shouldUseMetadataServer_noEnvironmentVars() { - MockExternalAccountCredentialsTransportFactory transportFactory = - new MockExternalAccountCredentialsTransportFactory(); - AwsCredentials awsCredential = - AwsCredentials.newBuilder(AWS_CREDENTIAL) - .setHttpTransportFactory(transportFactory) - .setCredentialSource(buildAwsImdsv2CredentialSource(transportFactory)) - .build(); - assertTrue(awsCredential.shouldUseMetadataServer()); - } - @Test public void builder() throws IOException { List scopes = Arrays.asList("scope1", "scope2"); @@ -1140,7 +1019,7 @@ public void builder() throws IOException { .setScopes(scopes) .build(); - assertEquals("region", credentials.getAwsRegion(null)); + // assertEquals("region", credentials.getAwsRegion(null)); assertEquals("https://test.com", credentials.getRegionalCredentialVerificationUrlOverride()); assertEquals("audience", credentials.getAudience()); assertEquals("subjectTokenType", credentials.getSubjectTokenType()); @@ -1331,7 +1210,7 @@ private static AwsCredentialSource buildAwsCredentialSource( return new AwsCredentialSource(buildAwsCredentialSourceMap(transportFactory)); } - private static AwsCredentialSource buildAwsImdsv2CredentialSource( + static AwsCredentialSource buildAwsImdsv2CredentialSource( MockExternalAccountCredentialsTransportFactory transportFactory) { Map credentialSourceMap = buildAwsCredentialSourceMap(transportFactory); credentialSourceMap.put( diff --git a/oauth2_http/javatests/com/google/auth/oauth2/InternalAwsSecurityCredentialsProviderTest.java b/oauth2_http/javatests/com/google/auth/oauth2/InternalAwsSecurityCredentialsProviderTest.java new file mode 100644 index 000000000..ec4665e53 --- /dev/null +++ b/oauth2_http/javatests/com/google/auth/oauth2/InternalAwsSecurityCredentialsProviderTest.java @@ -0,0 +1,140 @@ +package com.google.auth.oauth2; + +import static com.google.auth.oauth2.AwsCredentialsTest.buildAwsImdsv2CredentialSource; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import com.google.appengine.repackaged.com.google.common.collect.ImmutableList; +import com.google.auth.oauth2.ExternalAccountCredentialsTest.MockExternalAccountCredentialsTransportFactory; +import java.util.List; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Tests for {@link InternalAwsSecurityCredentialsProvider}. */ +@RunWith(JUnit4.class) +public class InternalAwsSecurityCredentialsProviderTest { + @Test + public void shouldUseMetadataServer_withRequiredEnvironmentVariables() { + MockExternalAccountCredentialsTransportFactory transportFactory = + new MockExternalAccountCredentialsTransportFactory(); + + // Add required environment variables. + List regionKeys = ImmutableList.of("AWS_REGION", "AWS_DEFAULT_REGION"); + for (String regionKey : regionKeys) { + TestEnvironmentProvider environmentProvider = new TestEnvironmentProvider(); + // AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY are always required. + environmentProvider + .setEnv(regionKey, "awsRegion") + .setEnv("AWS_ACCESS_KEY_ID", "awsAccessKeyId") + .setEnv("AWS_SECRET_ACCESS_KEY", "awsSecretAccessKey"); + InternalAwsSecurityCredentialsProvider provider = + new InternalAwsSecurityCredentialsProvider( + buildAwsImdsv2CredentialSource(transportFactory), + environmentProvider, + transportFactory, + null); + assertFalse(provider.shouldUseMetadataServer()); + } + } + + @Test + public void shouldUseMetadataServer_missingRegion() { + MockExternalAccountCredentialsTransportFactory transportFactory = + new MockExternalAccountCredentialsTransportFactory(); + + TestEnvironmentProvider environmentProvider = new TestEnvironmentProvider(); + environmentProvider + .setEnv("AWS_ACCESS_KEY_ID", "awsAccessKeyId") + .setEnv("AWS_SECRET_ACCESS_KEY", "awsSecretAccessKey"); + InternalAwsSecurityCredentialsProvider provider = + new InternalAwsSecurityCredentialsProvider( + buildAwsImdsv2CredentialSource(transportFactory), + environmentProvider, + transportFactory, + null); + assertTrue(provider.shouldUseMetadataServer()); + } + + @Test + public void shouldUseMetadataServer_missingAwsAccessKeyId() { + MockExternalAccountCredentialsTransportFactory transportFactory = + new MockExternalAccountCredentialsTransportFactory(); + + // Add required environment variables. + List regionKeys = ImmutableList.of("AWS_REGION", "AWS_DEFAULT_REGION"); + for (String regionKey : regionKeys) { + TestEnvironmentProvider environmentProvider = new TestEnvironmentProvider(); + // AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY are always required. + environmentProvider + .setEnv(regionKey, "awsRegion") + .setEnv("AWS_SECRET_ACCESS_KEY", "awsSecretAccessKey"); + InternalAwsSecurityCredentialsProvider provider = + new InternalAwsSecurityCredentialsProvider( + buildAwsImdsv2CredentialSource(transportFactory), + environmentProvider, + transportFactory, + null); + assertTrue(provider.shouldUseMetadataServer()); + } + } + + @Test + public void shouldUseMetadataServer_missingAwsSecretAccessKey() { + MockExternalAccountCredentialsTransportFactory transportFactory = + new MockExternalAccountCredentialsTransportFactory(); + + // Add required environment variables. + List regionKeys = ImmutableList.of("AWS_REGION", "AWS_DEFAULT_REGION"); + for (String regionKey : regionKeys) { + TestEnvironmentProvider environmentProvider = new TestEnvironmentProvider(); + // AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY are always required. + environmentProvider + .setEnv(regionKey, "awsRegion") + .setEnv("AWS_ACCESS_KEY_ID", "awsAccessKeyId"); + InternalAwsSecurityCredentialsProvider provider = + new InternalAwsSecurityCredentialsProvider( + buildAwsImdsv2CredentialSource(transportFactory), + environmentProvider, + transportFactory, + null); + assertTrue(provider.shouldUseMetadataServer()); + } + } + + @Test + public void shouldUseMetadataServer_missingAwsSecurityCreds() { + MockExternalAccountCredentialsTransportFactory transportFactory = + new MockExternalAccountCredentialsTransportFactory(); + + // Add required environment variables. + List regionKeys = ImmutableList.of("AWS_REGION", "AWS_DEFAULT_REGION"); + for (String regionKey : regionKeys) { + TestEnvironmentProvider environmentProvider = new TestEnvironmentProvider(); + // AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY are always required. + // Not set here. + environmentProvider.setEnv(regionKey, "awsRegion"); + InternalAwsSecurityCredentialsProvider provider = + new InternalAwsSecurityCredentialsProvider( + buildAwsImdsv2CredentialSource(transportFactory), + environmentProvider, + transportFactory, + null); + assertTrue(provider.shouldUseMetadataServer()); + } + } + + @Test + public void shouldUseMetadataServer_noEnvironmentVars() { + TestEnvironmentProvider environmentProvider = new TestEnvironmentProvider(); + MockExternalAccountCredentialsTransportFactory transportFactory = + new MockExternalAccountCredentialsTransportFactory(); + InternalAwsSecurityCredentialsProvider provider = + new InternalAwsSecurityCredentialsProvider( + buildAwsImdsv2CredentialSource(transportFactory), + environmentProvider, + transportFactory, + null); + assertTrue(provider.shouldUseMetadataServer()); + } +} From f480b844a923e22aa354ca8ce2a3019cf21dd44e Mon Sep 17 00:00:00 2001 From: aeitzman Date: Mon, 8 Jan 2024 13:06:16 -0800 Subject: [PATCH 22/32] Adding identity pool providers --- .../google/auth/oauth2/AwsCredentials.java | 2 +- .../FileIdentityPoolSubjectTokenProvider.java | 85 +++++++++++++ .../auth/oauth2/IdentityPoolCredentials.java | 118 +++--------------- .../IdentityPoolSubjectTokenProvider.java | 70 +++++++++++ ...nternalAwsSecurityCredentialsProvider.java | 2 +- ...mmaticAwsSecurityCredentialsProvider.java} | 5 +- ...maticIdentityPoolSubjectTokenProvider.java | 67 ++++++++++ .../UrlIdentityPoolSubjectTokenProvider.java | 97 ++++++++++++++ .../oauth2/IdentityPoolCredentialsTest.java | 2 +- ...nalAwsSecurityCredentialsProviderTest.java | 2 +- 10 files changed, 344 insertions(+), 106 deletions(-) create mode 100644 oauth2_http/java/com/google/auth/oauth2/FileIdentityPoolSubjectTokenProvider.java create mode 100644 oauth2_http/java/com/google/auth/oauth2/IdentityPoolSubjectTokenProvider.java rename oauth2_http/java/com/google/auth/oauth2/{UserAwsSecurityCredentialsProvider.java => ProgrammaticAwsSecurityCredentialsProvider.java} (92%) create mode 100644 oauth2_http/java/com/google/auth/oauth2/ProgrammaticIdentityPoolSubjectTokenProvider.java create mode 100644 oauth2_http/java/com/google/auth/oauth2/UrlIdentityPoolSubjectTokenProvider.java diff --git a/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java b/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java index dd16cd205..80f027f89 100644 --- a/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java @@ -101,7 +101,7 @@ public class AwsCredentials extends ExternalAccountCredentials { // credentials. if (builder.awsSecurityCredentialsSupplier != null) { this.awsSecurityCredentialsProvider = - new UserAwsSecurityCredentialsProvider( + new ProgrammaticAwsSecurityCredentialsProvider( builder.awsSecurityCredentialsSupplier, this.awsRegion); } else { this.awsSecurityCredentialsProvider = diff --git a/oauth2_http/java/com/google/auth/oauth2/FileIdentityPoolSubjectTokenProvider.java b/oauth2_http/java/com/google/auth/oauth2/FileIdentityPoolSubjectTokenProvider.java new file mode 100644 index 000000000..fed068d39 --- /dev/null +++ b/oauth2_http/java/com/google/auth/oauth2/FileIdentityPoolSubjectTokenProvider.java @@ -0,0 +1,85 @@ +/* + * Copyright 2024 Google LLC + * + * 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 LLC 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.oauth2; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.LinkOption; +import java.nio.file.Paths; +import java.util.function.Supplier; + +class FileIdentityPoolSubjectTokenProvider extends IdentityPoolSubjectTokenProvider { + + private final IdentityPoolCredentialSource credentialSource; + + FileIdentityPoolSubjectTokenProvider(IdentityPoolCredentialSource credentialSource) { + this.credentialSource = credentialSource; + } + + @Override + public String getSubjectToken() throws IOException { + return this.retrieveSubjectTokenFromCredentialFile(); + } + + @Override + public boolean isUserSupplied() { + return false; + } + + @Override + public Supplier getSupplier() { + return () -> { + try { + return this.getSubjectToken(); + } catch (IOException e) { + throw new RuntimeException(e); + } + }; + } + + private String retrieveSubjectTokenFromCredentialFile() throws IOException { + String credentialFilePath = this.credentialSource.credentialLocation; + if (!Files.exists(Paths.get(credentialFilePath), LinkOption.NOFOLLOW_LINKS)) { + throw new IOException( + String.format( + "Invalid credential location. The file at %s does not exist.", credentialFilePath)); + } + try { + return parseToken(new FileInputStream(new File(credentialFilePath)), this.credentialSource); + } catch (IOException e) { + throw new IOException( + "Error when attempting to read the subject token from the credential file.", e); + } + } +} diff --git a/oauth2_http/java/com/google/auth/oauth2/IdentityPoolCredentials.java b/oauth2_http/java/com/google/auth/oauth2/IdentityPoolCredentials.java index ea0a2ca16..05a33f869 100644 --- a/oauth2_http/java/com/google/auth/oauth2/IdentityPoolCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/IdentityPoolCredentials.java @@ -31,32 +31,14 @@ package com.google.auth.oauth2; -import com.google.api.client.http.GenericUrl; -import com.google.api.client.http.HttpHeaders; -import com.google.api.client.http.HttpRequest; -import com.google.api.client.http.HttpResponse; -import com.google.api.client.json.GenericJson; -import com.google.api.client.json.JsonObjectParser; import com.google.auth.http.HttpTransportFactory; -import com.google.auth.oauth2.IdentityPoolCredentialSource.CredentialFormatType; import com.google.auth.oauth2.IdentityPoolCredentialSource.IdentityPoolCredentialSourceType; -import com.google.common.io.CharStreams; import com.google.errorprone.annotations.CanIgnoreReturnValue; -import java.io.BufferedReader; -import java.io.File; -import java.io.FileInputStream; import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.LinkOption; -import java.nio.file.Paths; import java.util.ArrayList; import java.util.Collection; import java.util.Map; import java.util.function.Supplier; -import javax.annotation.Nullable; /** * Url-sourced, file-sourced, or user provided supplier method-sourced external account credentials. @@ -69,28 +51,32 @@ public class IdentityPoolCredentials extends ExternalAccountCredentials { static final String URL_METRICS_HEADER_VALUE = "url"; private static final long serialVersionUID = 2471046175477275881L; - @Nullable private final IdentityPoolCredentialSource identityPoolCredentialSource; - - @Nullable private final Supplier subjectTokenSupplier; + private final IdentityPoolSubjectTokenProvider subjectTokenProvider; /** Internal constructor. See {@link Builder}. */ IdentityPoolCredentials(Builder builder) { super(builder); + IdentityPoolCredentialSource credentialSource = + (IdentityPoolCredentialSource) builder.credentialSource; + // Check that one and only one of supplier or credential source are provided. - if (builder.subjectTokenSupplier != null && builder.credentialSource != null) { + if (builder.subjectTokenSupplier != null && credentialSource != null) { throw new IllegalArgumentException( "IdentityPoolCredentials cannot have both a subjectTokenSupplier and a credentialSource."); } - if (builder.subjectTokenSupplier == null && builder.credentialSource == null) { + if (builder.subjectTokenSupplier == null && credentialSource == null) { throw new IllegalArgumentException( "A subjectTokenSupplier or a credentialSource must be provided."); } if (builder.subjectTokenSupplier != null) { - this.subjectTokenSupplier = builder.subjectTokenSupplier; - this.identityPoolCredentialSource = null; + this.subjectTokenProvider = + new ProgrammaticIdentityPoolSubjectTokenProvider(builder.subjectTokenSupplier); + } else if (credentialSource.credentialSourceType + == IdentityPoolCredentialSource.IdentityPoolCredentialSourceType.FILE) { + this.subjectTokenProvider = new FileIdentityPoolSubjectTokenProvider(credentialSource); } else { - this.identityPoolCredentialSource = (IdentityPoolCredentialSource) builder.credentialSource; - this.subjectTokenSupplier = null; + this.subjectTokenProvider = + new UrlIdentityPoolSubjectTokenProvider(credentialSource, this.transportFactory); } } @@ -111,23 +97,12 @@ public AccessToken refreshAccessToken() throws IOException { @Override public String retrieveSubjectToken() throws IOException { - if (this.subjectTokenSupplier != null) { - try { - return this.subjectTokenSupplier.get(); - } catch (Throwable e) { - throw new GoogleAuthException( - false, 0, "Error retrieving token from subject token supplier.", e); - } - } else if (identityPoolCredentialSource.credentialSourceType - == IdentityPoolCredentialSource.IdentityPoolCredentialSourceType.FILE) { - return retrieveSubjectTokenFromCredentialFile(); - } - return getSubjectTokenFromMetadataServer(); + return this.subjectTokenProvider.getSubjectToken(); } @Override String getCredentialSourceType() { - if (this.subjectTokenSupplier != null) { + if (this.subjectTokenProvider.isUserSupplied()) { return PROGRAMMATIC_AUTH_METRICS_HEADER_VALUE; } if (((IdentityPoolCredentialSource) this.getCredentialSource()).credentialSourceType @@ -138,65 +113,6 @@ String getCredentialSourceType() { } } - public Supplier getSubjectTokenSupplier() { - return this.subjectTokenSupplier; - } - - private String retrieveSubjectTokenFromCredentialFile() throws IOException { - String credentialFilePath = identityPoolCredentialSource.credentialLocation; - if (!Files.exists(Paths.get(credentialFilePath), LinkOption.NOFOLLOW_LINKS)) { - throw new IOException( - String.format( - "Invalid credential location. The file at %s does not exist.", credentialFilePath)); - } - try { - return parseToken(new FileInputStream(new File(credentialFilePath))); - } catch (IOException e) { - throw new IOException( - "Error when attempting to read the subject token from the credential file.", e); - } - } - - private String parseToken(InputStream inputStream) throws IOException { - if (identityPoolCredentialSource.credentialFormatType == CredentialFormatType.TEXT) { - BufferedReader reader = - new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); - return CharStreams.toString(reader); - } - - JsonObjectParser parser = new JsonObjectParser(OAuth2Utils.JSON_FACTORY); - GenericJson fileContents = - parser.parseAndClose(inputStream, StandardCharsets.UTF_8, GenericJson.class); - - if (!fileContents.containsKey(identityPoolCredentialSource.subjectTokenFieldName)) { - throw new IOException("Invalid subject token field name. No subject token was found."); - } - return (String) fileContents.get(identityPoolCredentialSource.subjectTokenFieldName); - } - - private String getSubjectTokenFromMetadataServer() throws IOException { - HttpRequest request = - transportFactory - .create() - .createRequestFactory() - .buildGetRequest(new GenericUrl(identityPoolCredentialSource.credentialLocation)); - request.setParser(new JsonObjectParser(OAuth2Utils.JSON_FACTORY)); - - if (identityPoolCredentialSource.hasHeaders()) { - HttpHeaders headers = new HttpHeaders(); - headers.putAll(identityPoolCredentialSource.headers); - request.setHeaders(headers); - } - - try { - HttpResponse response = request.execute(); - return parseToken(response.getContent()); - } catch (IOException e) { - throw new IOException( - String.format("Error getting subject token from metadata server: %s", e.getMessage()), e); - } - } - /** Clones the IdentityPoolCredentials with the specified scopes. */ @Override public IdentityPoolCredentials createScoped(Collection newScopes) { @@ -220,7 +136,9 @@ public static class Builder extends ExternalAccountCredentials.Builder { Builder(IdentityPoolCredentials credentials) { super(credentials); - this.setSubjectTokenSupplier(credentials.subjectTokenSupplier); + if (credentials.subjectTokenProvider.isUserSupplied()) { + this.setSubjectTokenSupplier(credentials.subjectTokenProvider.getSupplier()); + } } /** diff --git a/oauth2_http/java/com/google/auth/oauth2/IdentityPoolSubjectTokenProvider.java b/oauth2_http/java/com/google/auth/oauth2/IdentityPoolSubjectTokenProvider.java new file mode 100644 index 000000000..c745a55ac --- /dev/null +++ b/oauth2_http/java/com/google/auth/oauth2/IdentityPoolSubjectTokenProvider.java @@ -0,0 +1,70 @@ +/* + * Copyright 2024 Google LLC + * + * 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 LLC 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.oauth2; + +import com.google.api.client.json.GenericJson; +import com.google.api.client.json.JsonObjectParser; +import com.google.auth.oauth2.IdentityPoolCredentialSource.CredentialFormatType; +import com.google.common.io.CharStreams; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Serializable; +import java.nio.charset.StandardCharsets; +import java.util.function.Supplier; + +abstract class IdentityPoolSubjectTokenProvider implements Serializable { + abstract String getSubjectToken() throws IOException; + + abstract boolean isUserSupplied(); + + abstract Supplier getSupplier(); + + protected static String parseToken( + InputStream inputStream, IdentityPoolCredentialSource credentialSource) throws IOException { + if (credentialSource.credentialFormatType == CredentialFormatType.TEXT) { + BufferedReader reader = + new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); + return CharStreams.toString(reader); + } + + JsonObjectParser parser = new JsonObjectParser(OAuth2Utils.JSON_FACTORY); + GenericJson fileContents = + parser.parseAndClose(inputStream, StandardCharsets.UTF_8, GenericJson.class); + + if (!fileContents.containsKey(credentialSource.subjectTokenFieldName)) { + throw new IOException("Invalid subject token field name. No subject token was found."); + } + return (String) fileContents.get(credentialSource.subjectTokenFieldName); + } +} diff --git a/oauth2_http/java/com/google/auth/oauth2/InternalAwsSecurityCredentialsProvider.java b/oauth2_http/java/com/google/auth/oauth2/InternalAwsSecurityCredentialsProvider.java index 944028cb9..e626d9188 100644 --- a/oauth2_http/java/com/google/auth/oauth2/InternalAwsSecurityCredentialsProvider.java +++ b/oauth2_http/java/com/google/auth/oauth2/InternalAwsSecurityCredentialsProvider.java @@ -40,9 +40,9 @@ import com.google.api.client.http.HttpResponse; import com.google.api.client.json.GenericJson; import com.google.api.client.json.JsonParser; -import com.google.appengine.repackaged.com.google.common.collect.ImmutableList; import com.google.auth.http.HttpTransportFactory; import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.ImmutableList; import java.io.IOException; import java.util.HashMap; import java.util.List; diff --git a/oauth2_http/java/com/google/auth/oauth2/UserAwsSecurityCredentialsProvider.java b/oauth2_http/java/com/google/auth/oauth2/ProgrammaticAwsSecurityCredentialsProvider.java similarity index 92% rename from oauth2_http/java/com/google/auth/oauth2/UserAwsSecurityCredentialsProvider.java rename to oauth2_http/java/com/google/auth/oauth2/ProgrammaticAwsSecurityCredentialsProvider.java index 63c84089c..e4bd63e2e 100644 --- a/oauth2_http/java/com/google/auth/oauth2/UserAwsSecurityCredentialsProvider.java +++ b/oauth2_http/java/com/google/auth/oauth2/ProgrammaticAwsSecurityCredentialsProvider.java @@ -33,13 +33,14 @@ import java.util.function.Supplier; -class UserAwsSecurityCredentialsProvider implements AwsSecurityCredentialsProvider { +class ProgrammaticAwsSecurityCredentialsProvider implements AwsSecurityCredentialsProvider { private static final long serialVersionUID = 6699948149655089007L; private final String region; private final transient Supplier awsSecurityCredentialsSupplier; - UserAwsSecurityCredentialsProvider(Supplier supplier, String region) { + ProgrammaticAwsSecurityCredentialsProvider( + Supplier supplier, String region) { if (region == null || region.trim().isEmpty()) { throw new IllegalArgumentException( "An AWS region must be specified when using an aws security credential supplier."); diff --git a/oauth2_http/java/com/google/auth/oauth2/ProgrammaticIdentityPoolSubjectTokenProvider.java b/oauth2_http/java/com/google/auth/oauth2/ProgrammaticIdentityPoolSubjectTokenProvider.java new file mode 100644 index 000000000..2a330aa1e --- /dev/null +++ b/oauth2_http/java/com/google/auth/oauth2/ProgrammaticIdentityPoolSubjectTokenProvider.java @@ -0,0 +1,67 @@ +/* + * Copyright 2024 Google LLC + * + * 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 LLC 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.oauth2; + +import java.io.IOException; +import java.util.function.Supplier; + +class ProgrammaticIdentityPoolSubjectTokenProvider extends IdentityPoolSubjectTokenProvider { + + private final Supplier subjectTokenSupplier; + + ProgrammaticIdentityPoolSubjectTokenProvider(Supplier subjectTokenSupplier) { + this.subjectTokenSupplier = subjectTokenSupplier; + } + + @Override + String getSubjectToken() throws IOException { + try { + return this.subjectTokenSupplier.get(); + } catch (RuntimeException e) { + throw new GoogleAuthException( + /* isRetryable= */ false, + /* retryCount= */ 0, + "Error retrieving token from subject token supplier.", + e); + } + } + + @Override + boolean isUserSupplied() { + return true; + } + + @Override + Supplier getSupplier() { + return this.subjectTokenSupplier; + } +} diff --git a/oauth2_http/java/com/google/auth/oauth2/UrlIdentityPoolSubjectTokenProvider.java b/oauth2_http/java/com/google/auth/oauth2/UrlIdentityPoolSubjectTokenProvider.java new file mode 100644 index 000000000..e1585664a --- /dev/null +++ b/oauth2_http/java/com/google/auth/oauth2/UrlIdentityPoolSubjectTokenProvider.java @@ -0,0 +1,97 @@ +/* + * Copyright 2024 Google LLC + * + * 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 LLC 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.oauth2; + +import com.google.api.client.http.GenericUrl; +import com.google.api.client.http.HttpHeaders; +import com.google.api.client.http.HttpRequest; +import com.google.api.client.http.HttpResponse; +import com.google.api.client.json.JsonObjectParser; +import com.google.auth.http.HttpTransportFactory; +import java.io.IOException; +import java.util.function.Supplier; + +class UrlIdentityPoolSubjectTokenProvider extends IdentityPoolSubjectTokenProvider { + + private final IdentityPoolCredentialSource credentialSource; + private final transient HttpTransportFactory transportFactory; + + UrlIdentityPoolSubjectTokenProvider( + IdentityPoolCredentialSource credentialSource, HttpTransportFactory transportFactory) { + this.credentialSource = credentialSource; + this.transportFactory = transportFactory; + } + + @Override + public String getSubjectToken() throws IOException { + return this.getSubjectTokenFromMetadataServer(); + } + + @Override + public boolean isUserSupplied() { + return false; + } + + @Override + public Supplier getSupplier() { + return () -> { + try { + return this.getSubjectToken(); + } catch (IOException e) { + throw new RuntimeException(e); + } + }; + } + + private String getSubjectTokenFromMetadataServer() throws IOException { + HttpRequest request = + transportFactory + .create() + .createRequestFactory() + .buildGetRequest(new GenericUrl(credentialSource.credentialLocation)); + request.setParser(new JsonObjectParser(OAuth2Utils.JSON_FACTORY)); + + if (credentialSource.hasHeaders()) { + HttpHeaders headers = new HttpHeaders(); + headers.putAll(credentialSource.headers); + request.setHeaders(headers); + } + + try { + HttpResponse response = request.execute(); + return parseToken(response.getContent(), this.credentialSource); + } catch (IOException e) { + throw new IOException( + String.format("Error getting subject token from metadata server: %s", e.getMessage()), e); + } + } +} diff --git a/oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java index e9d7d3baf..5dae58712 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java @@ -806,7 +806,7 @@ public void builder_subjectTokenSupplier() { .setScopes(scopes) .build(); - assertEquals(testSupplier, credentials.getSubjectTokenSupplier()); + // assertEquals(testSupplier, credentials.getSubjectTokenSupplier()); } @Test diff --git a/oauth2_http/javatests/com/google/auth/oauth2/InternalAwsSecurityCredentialsProviderTest.java b/oauth2_http/javatests/com/google/auth/oauth2/InternalAwsSecurityCredentialsProviderTest.java index ec4665e53..3032530a6 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/InternalAwsSecurityCredentialsProviderTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/InternalAwsSecurityCredentialsProviderTest.java @@ -4,8 +4,8 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import com.google.appengine.repackaged.com.google.common.collect.ImmutableList; import com.google.auth.oauth2.ExternalAccountCredentialsTest.MockExternalAccountCredentialsTransportFactory; +import com.google.common.collect.ImmutableList; import java.util.List; import org.junit.Test; import org.junit.runner.RunWith; From 188b803f992ecacf9708388fcd3f2361701f498b Mon Sep 17 00:00:00 2001 From: aeitzman Date: Tue, 9 Jan 2024 11:31:13 -0800 Subject: [PATCH 23/32] PR comments --- .../google/auth/oauth2/AwsCredentials.java | 13 ++++--- .../AwsSecurityCredentialsProvider.java | 36 ++++++++++++++++--- .../oauth2/ExternalAccountCredentials.java | 2 +- .../FileIdentityPoolSubjectTokenProvider.java | 21 ++++++++--- .../auth/oauth2/IdentityPoolCredentials.java | 25 +++++++------ .../IdentityPoolSubjectTokenProvider.java | 23 +++++++++++- ...nternalAwsSecurityCredentialsProvider.java | 32 +++++++++++++---- ...ammaticAwsSecurityCredentialsProvider.java | 26 +++++++++++--- ...maticIdentityPoolSubjectTokenProvider.java | 17 +++++++-- .../UrlIdentityPoolSubjectTokenProvider.java | 22 +++++++++--- ...nalAwsSecurityCredentialsProviderTest.java | 31 ++++++++++++++++ 11 files changed, 204 insertions(+), 44 deletions(-) diff --git a/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java b/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java index 80f027f89..ce8ece687 100644 --- a/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java @@ -69,6 +69,10 @@ public class AwsCredentials extends ExternalAccountCredentials { @Nullable private final String regionalCredentialVerificationUrl; @Nullable private final String awsRegion; + // Boolean to tell if a Supplier was provided to build the credential. This is needed to tell + // whether the supplier should be passed when creating a new builder from this credential. + private final boolean builtWithSupplier; + /** Internal constructor. See {@link AwsCredentials.Builder}. */ AwsCredentials(Builder builder) { super(builder); @@ -103,6 +107,7 @@ public class AwsCredentials extends ExternalAccountCredentials { this.awsSecurityCredentialsProvider = new ProgrammaticAwsSecurityCredentialsProvider( builder.awsSecurityCredentialsSupplier, this.awsRegion); + this.builtWithSupplier = true; } else { this.awsSecurityCredentialsProvider = new InternalAwsSecurityCredentialsProvider( @@ -110,6 +115,7 @@ public class AwsCredentials extends ExternalAccountCredentials { this.getEnvironmentProvider(), this.transportFactory, builder.awsRegion); + this.builtWithSupplier = false; } } @@ -162,10 +168,7 @@ public GoogleCredentials createScoped(Collection newScopes) { @Override String getCredentialSourceType() { - if (this.awsSecurityCredentialsProvider.isUserSupplied()) { - return PROGRAMMATIC_AUTH_METRICS_HEADER_VALUE; - } - return AWS_METRICS_HEADER_VALUE; + return this.awsSecurityCredentialsProvider.getMetricsHeaderValue(); } private String buildSubjectToken(AwsRequestSignature signature) @@ -246,7 +249,7 @@ public static class Builder extends ExternalAccountCredentials.Builder { Builder(AwsCredentials credentials) { super(credentials); this.awsRegion = credentials.awsRegion; - if (credentials.awsSecurityCredentialsProvider.isUserSupplied()) { + if (credentials.builtWithSupplier) { this.awsSecurityCredentialsSupplier = credentials.awsSecurityCredentialsProvider.getSupplier(); } diff --git a/oauth2_http/java/com/google/auth/oauth2/AwsSecurityCredentialsProvider.java b/oauth2_http/java/com/google/auth/oauth2/AwsSecurityCredentialsProvider.java index 45e4ca4eb..b6610bed4 100644 --- a/oauth2_http/java/com/google/auth/oauth2/AwsSecurityCredentialsProvider.java +++ b/oauth2_http/java/com/google/auth/oauth2/AwsSecurityCredentialsProvider.java @@ -35,13 +35,39 @@ import java.io.Serializable; import java.util.function.Supplier; -interface AwsSecurityCredentialsProvider extends Serializable { +/** + * Provider for retrieving AWS Security credentials for {@Link AwsCredentials} to exchange for GCP + * access tokens. + */ +abstract class AwsSecurityCredentialsProvider implements Serializable { - String getRegion() throws IOException; + /** + * Gets the AWS region to use. + * + * @return the AWS region that should be used for the credential. + * @throws IOException + */ + abstract String getRegion() throws IOException; - AwsSecurityCredentials getCredentials() throws IOException; + /** + * Gets AWS security credentials. + * + * @return valid AWS security credentials that can be exchanged for a GCP access token. + * @throws IOException + */ + abstract AwsSecurityCredentials getCredentials() throws IOException; - boolean isUserSupplied(); + /** + * Gets the metrics header value that should be used for the sts request. + * + * @return the metrics header value. + */ + abstract String getMetricsHeaderValue(); - Supplier getSupplier(); + /** + * Gets the Aws security credential supplier. + * + * @return the Supplier used to retrieve the AWS security credentials. + */ + abstract Supplier getSupplier(); } diff --git a/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java b/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java index 8454f0b9a..228502325 100644 --- a/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java @@ -73,7 +73,7 @@ public abstract class ExternalAccountCredentials extends GoogleCredentials { static final String EXECUTABLE_SOURCE_KEY = "executable"; static final String DEFAULT_TOKEN_URL = "https://sts.googleapis.com/v1/token"; - static final String PROGRAMMATIC_AUTH_METRICS_HEADER_VALUE = "programmatic"; + static final String PROGRAMMATIC_METRICS_HEADER_VALUE = "programmatic"; private final String transportFactoryClassName; private final String audience; diff --git a/oauth2_http/java/com/google/auth/oauth2/FileIdentityPoolSubjectTokenProvider.java b/oauth2_http/java/com/google/auth/oauth2/FileIdentityPoolSubjectTokenProvider.java index fed068d39..b8fb1ce08 100644 --- a/oauth2_http/java/com/google/auth/oauth2/FileIdentityPoolSubjectTokenProvider.java +++ b/oauth2_http/java/com/google/auth/oauth2/FileIdentityPoolSubjectTokenProvider.java @@ -31,6 +31,8 @@ package com.google.auth.oauth2; +import static com.google.auth.oauth2.IdentityPoolCredentials.FILE_METRICS_HEADER_VALUE; + import java.io.File; import java.io.FileInputStream; import java.io.IOException; @@ -39,26 +41,37 @@ import java.nio.file.Paths; import java.util.function.Supplier; +/** + * Internal provider for retrieving subject tokens for {@Link IdentityPoolCredentials} to exchange + * for GCP access tokens via a local file. + */ class FileIdentityPoolSubjectTokenProvider extends IdentityPoolSubjectTokenProvider { + private final long serialVersionUID = 2475549052347431992L; + private final IdentityPoolCredentialSource credentialSource; + /** + * Constructor for FileIdentitySubjectTokenProvider + * + * @param credentialSource the credential source to use. + */ FileIdentityPoolSubjectTokenProvider(IdentityPoolCredentialSource credentialSource) { this.credentialSource = credentialSource; } @Override - public String getSubjectToken() throws IOException { + String getSubjectToken() throws IOException { return this.retrieveSubjectTokenFromCredentialFile(); } @Override - public boolean isUserSupplied() { - return false; + String getMetricsHeaderValue() { + return FILE_METRICS_HEADER_VALUE; } @Override - public Supplier getSupplier() { + Supplier getSupplier() { return () -> { try { return this.getSubjectToken(); diff --git a/oauth2_http/java/com/google/auth/oauth2/IdentityPoolCredentials.java b/oauth2_http/java/com/google/auth/oauth2/IdentityPoolCredentials.java index 05a33f869..4ea349b21 100644 --- a/oauth2_http/java/com/google/auth/oauth2/IdentityPoolCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/IdentityPoolCredentials.java @@ -32,7 +32,7 @@ package com.google.auth.oauth2; import com.google.auth.http.HttpTransportFactory; -import com.google.auth.oauth2.IdentityPoolCredentialSource.IdentityPoolCredentialSourceType; +import com.google.common.annotations.VisibleForTesting; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.io.IOException; import java.util.ArrayList; @@ -52,6 +52,9 @@ public class IdentityPoolCredentials extends ExternalAccountCredentials { private static final long serialVersionUID = 2471046175477275881L; private final IdentityPoolSubjectTokenProvider subjectTokenProvider; + // Boolean to tell if a Supplier was provided to build the credential. This is needed to tell + // whether the supplier should be passed when creating a new builder from this credential. + private final boolean builtWithSupplier; /** Internal constructor. See {@link Builder}. */ IdentityPoolCredentials(Builder builder) { @@ -71,12 +74,15 @@ public class IdentityPoolCredentials extends ExternalAccountCredentials { if (builder.subjectTokenSupplier != null) { this.subjectTokenProvider = new ProgrammaticIdentityPoolSubjectTokenProvider(builder.subjectTokenSupplier); + this.builtWithSupplier = true; } else if (credentialSource.credentialSourceType == IdentityPoolCredentialSource.IdentityPoolCredentialSourceType.FILE) { this.subjectTokenProvider = new FileIdentityPoolSubjectTokenProvider(credentialSource); + this.builtWithSupplier = false; } else { this.subjectTokenProvider = new UrlIdentityPoolSubjectTokenProvider(credentialSource, this.transportFactory); + this.builtWithSupplier = false; } } @@ -102,15 +108,12 @@ public String retrieveSubjectToken() throws IOException { @Override String getCredentialSourceType() { - if (this.subjectTokenProvider.isUserSupplied()) { - return PROGRAMMATIC_AUTH_METRICS_HEADER_VALUE; - } - if (((IdentityPoolCredentialSource) this.getCredentialSource()).credentialSourceType - == IdentityPoolCredentialSourceType.FILE) { - return FILE_METRICS_HEADER_VALUE; - } else { - return URL_METRICS_HEADER_VALUE; - } + return this.subjectTokenProvider.getMetricsHeaderValue(); + } + + @VisibleForTesting + IdentityPoolSubjectTokenProvider getIdentityPoolSubjectTokenProvider() { + return this.subjectTokenProvider; } /** Clones the IdentityPoolCredentials with the specified scopes. */ @@ -136,7 +139,7 @@ public static class Builder extends ExternalAccountCredentials.Builder { Builder(IdentityPoolCredentials credentials) { super(credentials); - if (credentials.subjectTokenProvider.isUserSupplied()) { + if (credentials.builtWithSupplier) { this.setSubjectTokenSupplier(credentials.subjectTokenProvider.getSupplier()); } } diff --git a/oauth2_http/java/com/google/auth/oauth2/IdentityPoolSubjectTokenProvider.java b/oauth2_http/java/com/google/auth/oauth2/IdentityPoolSubjectTokenProvider.java index c745a55ac..a127d7de2 100644 --- a/oauth2_http/java/com/google/auth/oauth2/IdentityPoolSubjectTokenProvider.java +++ b/oauth2_http/java/com/google/auth/oauth2/IdentityPoolSubjectTokenProvider.java @@ -43,11 +43,32 @@ import java.nio.charset.StandardCharsets; import java.util.function.Supplier; +/** + * Provider for retrieving subject tokens for {@Link IdentityPoolCredentials} to exchange for GCP + * access tokens. + */ abstract class IdentityPoolSubjectTokenProvider implements Serializable { + + /** + * Gets a subject token that can be exchanged for a GCP access token. + * + * @return a valid subject token. + * @throws IOException + */ abstract String getSubjectToken() throws IOException; - abstract boolean isUserSupplied(); + /** + * Gets the metrics header value that should be used for the sts request. + * + * @return the metrics header value. + */ + abstract String getMetricsHeaderValue(); + /** + * Gets the supplier used to retrieve the subject token. + * + * @return the supplier used for getSubjectToken. + */ abstract Supplier getSupplier(); protected static String parseToken( diff --git a/oauth2_http/java/com/google/auth/oauth2/InternalAwsSecurityCredentialsProvider.java b/oauth2_http/java/com/google/auth/oauth2/InternalAwsSecurityCredentialsProvider.java index e626d9188..f94e2a11a 100644 --- a/oauth2_http/java/com/google/auth/oauth2/InternalAwsSecurityCredentialsProvider.java +++ b/oauth2_http/java/com/google/auth/oauth2/InternalAwsSecurityCredentialsProvider.java @@ -31,6 +31,8 @@ package com.google.auth.oauth2; +import static com.google.auth.oauth2.AwsCredentials.AWS_METRICS_HEADER_VALUE; + import com.google.api.client.http.GenericUrl; import com.google.api.client.http.HttpContent; import com.google.api.client.http.HttpHeaders; @@ -50,7 +52,12 @@ import java.util.function.Supplier; import javax.annotation.Nullable; -class InternalAwsSecurityCredentialsProvider implements AwsSecurityCredentialsProvider { +/** + * Internal provider for retrieving AWS security credentials for {@Link AwsCredentials} to exchange + * for GCP access tokens. The credentials are retrieved either via environment variables or metadata + * endpoints. + */ +class InternalAwsSecurityCredentialsProvider extends AwsSecurityCredentialsProvider { private static final long serialVersionUID = 4438370785261365013L; // Supported environment variables. @@ -69,6 +76,15 @@ class InternalAwsSecurityCredentialsProvider implements AwsSecurityCredentialsPr private transient HttpTransportFactory transportFactory; private final String region; + /** + * Constructor for InternalAwsSecurityCredentialsProvider + * + * @param awsCredentialSource the credential source to use. + * @param environmentProvider the environment provider to use for environment variables. + * @param transportFactory the transport factory to use for metadata requests. + * @param region an AWS region to use. If provided, getRegion will return this value instead of + * retrieving it via environment variables or metadata endpoints. + */ InternalAwsSecurityCredentialsProvider( AwsCredentialSource awsCredentialSource, EnvironmentProvider environmentProvider, @@ -80,7 +96,8 @@ class InternalAwsSecurityCredentialsProvider implements AwsSecurityCredentialsPr this.region = region; } - public AwsSecurityCredentials getCredentials() throws IOException { + @Override + AwsSecurityCredentials getCredentials() throws IOException { // Check environment variables for credentials first. if (canRetrieveSecurityCredentialsFromEnvironment()) { String accessKeyId = environmentProvider.getEnv(AWS_ACCESS_KEY_ID); @@ -119,7 +136,8 @@ public AwsSecurityCredentials getCredentials() throws IOException { return new AwsSecurityCredentials(accessKeyId, secretAccessKey, token); } - public String getRegion() throws IOException { + @Override + String getRegion() throws IOException { // If user has provided a region string, return that instead of checking environment or metadata // server. if (this.region != null) { @@ -163,11 +181,13 @@ private boolean canRetrieveRegionFromEnvironment() { return false; } - public boolean isUserSupplied() { - return false; + @Override + String getMetricsHeaderValue() { + return AWS_METRICS_HEADER_VALUE; } - public Supplier getSupplier() { + @Override + Supplier getSupplier() { return () -> { try { return this.getCredentials(); diff --git a/oauth2_http/java/com/google/auth/oauth2/ProgrammaticAwsSecurityCredentialsProvider.java b/oauth2_http/java/com/google/auth/oauth2/ProgrammaticAwsSecurityCredentialsProvider.java index e4bd63e2e..55970f66f 100644 --- a/oauth2_http/java/com/google/auth/oauth2/ProgrammaticAwsSecurityCredentialsProvider.java +++ b/oauth2_http/java/com/google/auth/oauth2/ProgrammaticAwsSecurityCredentialsProvider.java @@ -31,14 +31,27 @@ package com.google.auth.oauth2; +import static com.google.auth.oauth2.ExternalAccountCredentials.PROGRAMMATIC_METRICS_HEADER_VALUE; + import java.util.function.Supplier; -class ProgrammaticAwsSecurityCredentialsProvider implements AwsSecurityCredentialsProvider { +/** + * Provider for retrieving AWS security credentials for {@Link AwsCredentials} to exchange for GCP + * access tokens. The credentials are retrieved by calling a Supplier that has been defined by users + * when creating the AwsCredential. + */ +class ProgrammaticAwsSecurityCredentialsProvider extends AwsSecurityCredentialsProvider { private static final long serialVersionUID = 6699948149655089007L; private final String region; private final transient Supplier awsSecurityCredentialsSupplier; + /** + * Constructor for ProgrammaticAwsSecurityCredentialsProvider + * + * @param supplier the user defined supplier that returns AWS security credentials. + * @param region the AWS region that should be returned by getRegion(). + */ ProgrammaticAwsSecurityCredentialsProvider( Supplier supplier, String region) { if (region == null || region.trim().isEmpty()) { @@ -49,11 +62,13 @@ class ProgrammaticAwsSecurityCredentialsProvider implements AwsSecurityCredentia this.awsSecurityCredentialsSupplier = supplier; } - public String getRegion() { + @Override + String getRegion() { return this.region; } - public AwsSecurityCredentials getCredentials() throws GoogleAuthException { + @Override + AwsSecurityCredentials getCredentials() throws GoogleAuthException { try { return this.awsSecurityCredentialsSupplier.get(); } catch (RuntimeException e) { @@ -65,8 +80,9 @@ public AwsSecurityCredentials getCredentials() throws GoogleAuthException { } } - public boolean isUserSupplied() { - return true; + @Override + String getMetricsHeaderValue() { + return PROGRAMMATIC_METRICS_HEADER_VALUE; } public Supplier getSupplier() { diff --git a/oauth2_http/java/com/google/auth/oauth2/ProgrammaticIdentityPoolSubjectTokenProvider.java b/oauth2_http/java/com/google/auth/oauth2/ProgrammaticIdentityPoolSubjectTokenProvider.java index 2a330aa1e..56e297086 100644 --- a/oauth2_http/java/com/google/auth/oauth2/ProgrammaticIdentityPoolSubjectTokenProvider.java +++ b/oauth2_http/java/com/google/auth/oauth2/ProgrammaticIdentityPoolSubjectTokenProvider.java @@ -31,13 +31,26 @@ package com.google.auth.oauth2; +import static com.google.auth.oauth2.ExternalAccountCredentials.PROGRAMMATIC_METRICS_HEADER_VALUE; + import java.io.IOException; import java.util.function.Supplier; +/** + * Provider for retrieving subject tokens for {@Link IdentityPoolCredentials} to exchange for GCP + * access tokens. The subject token is retrieved by calling a user provided Supplier. + */ class ProgrammaticIdentityPoolSubjectTokenProvider extends IdentityPoolSubjectTokenProvider { + private final long serialVersionUID = 9009530138782505571L; + private final Supplier subjectTokenSupplier; + /** + * Constructor for ProgrammaticIdentityPoolSubjectTokenProvider. + * + * @param subjectTokenSupplier the user defined supplier that returns a subject token. + */ ProgrammaticIdentityPoolSubjectTokenProvider(Supplier subjectTokenSupplier) { this.subjectTokenSupplier = subjectTokenSupplier; } @@ -56,8 +69,8 @@ String getSubjectToken() throws IOException { } @Override - boolean isUserSupplied() { - return true; + String getMetricsHeaderValue() { + return PROGRAMMATIC_METRICS_HEADER_VALUE; } @Override diff --git a/oauth2_http/java/com/google/auth/oauth2/UrlIdentityPoolSubjectTokenProvider.java b/oauth2_http/java/com/google/auth/oauth2/UrlIdentityPoolSubjectTokenProvider.java index e1585664a..c85d68ffe 100644 --- a/oauth2_http/java/com/google/auth/oauth2/UrlIdentityPoolSubjectTokenProvider.java +++ b/oauth2_http/java/com/google/auth/oauth2/UrlIdentityPoolSubjectTokenProvider.java @@ -31,6 +31,8 @@ package com.google.auth.oauth2; +import static com.google.auth.oauth2.IdentityPoolCredentials.URL_METRICS_HEADER_VALUE; + import com.google.api.client.http.GenericUrl; import com.google.api.client.http.HttpHeaders; import com.google.api.client.http.HttpRequest; @@ -40,11 +42,23 @@ import java.io.IOException; import java.util.function.Supplier; +/** + * Provider for retrieving subject tokens for {@Link IdentityPoolCredentials} to exchange for GCP + * access tokens. The subject token is retrieved by calling a URL that returns the token. + */ class UrlIdentityPoolSubjectTokenProvider extends IdentityPoolSubjectTokenProvider { + private static final long serialVersionUID = 4964578313468011844L; + private final IdentityPoolCredentialSource credentialSource; private final transient HttpTransportFactory transportFactory; + /** + * Constructor for UrlIdentityPoolSubjectTokenProvider. + * + * @param credentialSource the credential source to use. + * @param transportFactory the transport factory to use for calling the URL. + */ UrlIdentityPoolSubjectTokenProvider( IdentityPoolCredentialSource credentialSource, HttpTransportFactory transportFactory) { this.credentialSource = credentialSource; @@ -52,17 +66,17 @@ class UrlIdentityPoolSubjectTokenProvider extends IdentityPoolSubjectTokenProvid } @Override - public String getSubjectToken() throws IOException { + String getSubjectToken() throws IOException { return this.getSubjectTokenFromMetadataServer(); } @Override - public boolean isUserSupplied() { - return false; + String getMetricsHeaderValue() { + return URL_METRICS_HEADER_VALUE; } @Override - public Supplier getSupplier() { + Supplier getSupplier() { return () -> { try { return this.getSubjectToken(); diff --git a/oauth2_http/javatests/com/google/auth/oauth2/InternalAwsSecurityCredentialsProviderTest.java b/oauth2_http/javatests/com/google/auth/oauth2/InternalAwsSecurityCredentialsProviderTest.java index 3032530a6..7552c88d1 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/InternalAwsSecurityCredentialsProviderTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/InternalAwsSecurityCredentialsProviderTest.java @@ -1,3 +1,34 @@ +/* + * Copyright 2024 Google LLC + * + * 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 LLC 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.oauth2; import static com.google.auth.oauth2.AwsCredentialsTest.buildAwsImdsv2CredentialSource; From e69108e5bc79df21bb0fac3dc60db4f4a6358ccd Mon Sep 17 00:00:00 2001 From: aeitzman Date: Tue, 9 Jan 2024 11:38:50 -0800 Subject: [PATCH 24/32] fix test --- .../com/google/auth/oauth2/IdentityPoolCredentialsTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java index 5dae58712..d9471adb6 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java @@ -806,7 +806,7 @@ public void builder_subjectTokenSupplier() { .setScopes(scopes) .build(); - // assertEquals(testSupplier, credentials.getSubjectTokenSupplier()); + assertEquals(testSupplier, credentials.getIdentityPoolSubjectTokenProvider().getSupplier()); } @Test From 4f3e2537db5f5b376b4e4a4a027251c6f0fc3b1c Mon Sep 17 00:00:00 2001 From: aeitzman Date: Wed, 10 Jan 2024 12:56:32 -0800 Subject: [PATCH 25/32] refactoring to expose rename provider to supplier and expose it publicly --- .../google/auth/oauth2/AwsCredentials.java | 67 +++------ ...va => AwsSecurityCredentialsSupplier.java} | 23 +-- .../oauth2/ExternalAccountCredentials.java | 4 +- ...FileIdentityPoolSubjectTokenSupplier.java} | 55 +++---- .../auth/oauth2/IdentityPoolCredentials.java | 40 +++-- .../IdentityPoolSubjectTokenProvider.java | 91 ------------ ... => IdentityPoolSubjectTokenSupplier.java} | 45 +----- ...ternalAwsSecurityCredentialsSupplier.java} | 39 +---- ...ammaticAwsSecurityCredentialsProvider.java | 91 ------------ ... UrlIdentityPoolSubjectTokenSupplier.java} | 29 +--- .../auth/oauth2/AwsCredentialsTest.java | 139 ++++++++---------- .../ITWorkloadIdentityFederationTest.java | 29 +++- .../oauth2/IdentityPoolCredentialsTest.java | 36 +++-- ...alAwsSecurityCredentialsSupplierTest.java} | 58 ++++---- 14 files changed, 218 insertions(+), 528 deletions(-) rename oauth2_http/java/com/google/auth/oauth2/{AwsSecurityCredentialsProvider.java => AwsSecurityCredentialsSupplier.java} (75%) rename oauth2_http/java/com/google/auth/oauth2/{FileIdentityPoolSubjectTokenProvider.java => FileIdentityPoolSubjectTokenSupplier.java} (67%) delete mode 100644 oauth2_http/java/com/google/auth/oauth2/IdentityPoolSubjectTokenProvider.java rename oauth2_http/java/com/google/auth/oauth2/{ProgrammaticIdentityPoolSubjectTokenProvider.java => IdentityPoolSubjectTokenSupplier.java} (57%) rename oauth2_http/java/com/google/auth/oauth2/{InternalAwsSecurityCredentialsProvider.java => InternalAwsSecurityCredentialsSupplier.java} (90%) delete mode 100644 oauth2_http/java/com/google/auth/oauth2/ProgrammaticAwsSecurityCredentialsProvider.java rename oauth2_http/java/com/google/auth/oauth2/{UrlIdentityPoolSubjectTokenProvider.java => UrlIdentityPoolSubjectTokenSupplier.java} (82%) rename oauth2_http/javatests/com/google/auth/oauth2/{InternalAwsSecurityCredentialsProviderTest.java => InternalAwsSecurityCredentialsSupplierTest.java} (81%) diff --git a/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java b/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java index ce8ece687..6fa60c652 100644 --- a/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java @@ -43,7 +43,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.function.Supplier; import javax.annotation.Nullable; /** @@ -62,16 +61,12 @@ public class AwsCredentials extends ExternalAccountCredentials { private static final long serialVersionUID = -3670131891574618105L; - @Nullable private final AwsSecurityCredentialsProvider awsSecurityCredentialsProvider; + @Nullable private final AwsSecurityCredentialsSupplier awsSecurityCredentialsSupplier; // Regional credential verification url override. This needs to be its own value so we can // correctly pass it to a builder. @Nullable private final String regionalCredentialVerificationUrlOverride; @Nullable private final String regionalCredentialVerificationUrl; - @Nullable private final String awsRegion; - - // Boolean to tell if a Supplier was provided to build the credential. This is needed to tell - // whether the supplier should be passed when creating a new builder from this credential. - private final boolean builtWithSupplier; + private final String metricsHeaderValue; /** Internal constructor. See {@link AwsCredentials.Builder}. */ AwsCredentials(Builder builder) { @@ -87,7 +82,6 @@ public class AwsCredentials extends ExternalAccountCredentials { } AwsCredentialSource credentialSource = (AwsCredentialSource) builder.credentialSource; - this.awsRegion = builder.awsRegion; // Set regional credential verification url override if provided. this.regionalCredentialVerificationUrlOverride = builder.regionalCredentialVerificationUrlOverride; @@ -104,18 +98,15 @@ public class AwsCredentials extends ExternalAccountCredentials { // If user has provided a security credential supplier, use that to retrieve the AWS security // credentials. if (builder.awsSecurityCredentialsSupplier != null) { - this.awsSecurityCredentialsProvider = - new ProgrammaticAwsSecurityCredentialsProvider( - builder.awsSecurityCredentialsSupplier, this.awsRegion); - this.builtWithSupplier = true; + this.awsSecurityCredentialsSupplier = builder.awsSecurityCredentialsSupplier; + this.metricsHeaderValue = PROGRAMMATIC_METRICS_HEADER_VALUE; } else { - this.awsSecurityCredentialsProvider = - new InternalAwsSecurityCredentialsProvider( + this.awsSecurityCredentialsSupplier = + new InternalAwsSecurityCredentialsSupplier( credentialSource, this.getEnvironmentProvider(), - this.transportFactory, - builder.awsRegion); - this.builtWithSupplier = false; + this.transportFactory); + this.metricsHeaderValue = AWS_METRICS_HEADER_VALUE; } } @@ -139,9 +130,9 @@ public String retrieveSubjectToken() throws IOException { // The targeted region is required to generate the signed request. The regional // endpoint must also be used. - String region = awsSecurityCredentialsProvider.getRegion(); + String region = awsSecurityCredentialsSupplier.getRegion(); - AwsSecurityCredentials credentials = awsSecurityCredentialsProvider.getCredentials(); + AwsSecurityCredentials credentials = awsSecurityCredentialsSupplier.getCredentials(); // Generate the signed request to the AWS STS GetCallerIdentity API. Map headers = new HashMap<>(); @@ -168,7 +159,7 @@ public GoogleCredentials createScoped(Collection newScopes) { @Override String getCredentialSourceType() { - return this.awsSecurityCredentialsProvider.getMetricsHeaderValue(); + return this.metricsHeaderValue; } private String buildSubjectToken(AwsRequestSignature signature) @@ -205,8 +196,8 @@ String getEnv(String name) { } @VisibleForTesting - AwsSecurityCredentialsProvider getAwsSecurityCredentialsProvider() { - return this.awsSecurityCredentialsProvider; + AwsSecurityCredentialsSupplier getAwsSecurityCredentialsSupplier() { + return this.awsSecurityCredentialsSupplier; } @Nullable @@ -238,9 +229,7 @@ public static AwsCredentials.Builder newBuilder(AwsCredentials awsCredentials) { public static class Builder extends ExternalAccountCredentials.Builder { - private Supplier awsSecurityCredentialsSupplier; - - private String awsRegion; + private AwsSecurityCredentialsSupplier awsSecurityCredentialsSupplier; private String regionalCredentialVerificationUrlOverride; @@ -248,10 +237,8 @@ public static class Builder extends ExternalAccountCredentials.Builder { Builder(AwsCredentials credentials) { super(credentials); - this.awsRegion = credentials.awsRegion; - if (credentials.builtWithSupplier) { - this.awsSecurityCredentialsSupplier = - credentials.awsSecurityCredentialsProvider.getSupplier(); + if (this.credentialSource == null) { + this.awsSecurityCredentialsSupplier = credentials.awsSecurityCredentialsSupplier; } this.regionalCredentialVerificationUrlOverride = credentials.regionalCredentialVerificationUrlOverride; @@ -259,31 +246,19 @@ public static class Builder extends ExternalAccountCredentials.Builder { /** * Sets the AWS security credentials supplier. The supplier should return a valid {@code - * AwsSecurityCredentials} object. An AWS region also is required when using a supplier. + * AwsSecurityCredentials} object and a valid AWS region. * - * @param awsSecurityCredentialsSupplier the supplier method to be called. + * @param awsSecurityCredentialsSupplier the supplier to use. * @return this {@code Builder} object */ @CanIgnoreReturnValue - public Builder setAwsSecurityCredentialsSupplier( - Supplier awsSecurityCredentialsSupplier) { + public Builder setAwsSecurityCredentialsSupplier + ( + AwsSecurityCredentialsSupplier awsSecurityCredentialsSupplier) { this.awsSecurityCredentialsSupplier = awsSecurityCredentialsSupplier; return this; } - /** - * Sets the AWS region. Required when using an AWS Security Credentials Supplier. If set, will - * override any region obtained via environment variables or the metadata endpoint. - * - * @param awsRegion the aws region to set. - * @return this {@code Builder} object - */ - @CanIgnoreReturnValue - public Builder setAwsRegion(String awsRegion) { - this.awsRegion = awsRegion; - return this; - } - /** * Sets the AWS regional credential verification URL. If set, will override any credential * verification URL provided in the credential source. If not set, the credential verification diff --git a/oauth2_http/java/com/google/auth/oauth2/AwsSecurityCredentialsProvider.java b/oauth2_http/java/com/google/auth/oauth2/AwsSecurityCredentialsSupplier.java similarity index 75% rename from oauth2_http/java/com/google/auth/oauth2/AwsSecurityCredentialsProvider.java rename to oauth2_http/java/com/google/auth/oauth2/AwsSecurityCredentialsSupplier.java index b6610bed4..b28dd858d 100644 --- a/oauth2_http/java/com/google/auth/oauth2/AwsSecurityCredentialsProvider.java +++ b/oauth2_http/java/com/google/auth/oauth2/AwsSecurityCredentialsSupplier.java @@ -33,13 +33,12 @@ import java.io.IOException; import java.io.Serializable; -import java.util.function.Supplier; /** - * Provider for retrieving AWS Security credentials for {@Link AwsCredentials} to exchange for GCP + * Supplier for retrieving AWS Security credentials for {@Link AwsCredentials} to exchange for GCP * access tokens. */ -abstract class AwsSecurityCredentialsProvider implements Serializable { +public interface AwsSecurityCredentialsSupplier extends Serializable { /** * Gets the AWS region to use. @@ -47,7 +46,7 @@ abstract class AwsSecurityCredentialsProvider implements Serializable { * @return the AWS region that should be used for the credential. * @throws IOException */ - abstract String getRegion() throws IOException; + String getRegion() throws IOException; /** * Gets AWS security credentials. @@ -55,19 +54,5 @@ abstract class AwsSecurityCredentialsProvider implements Serializable { * @return valid AWS security credentials that can be exchanged for a GCP access token. * @throws IOException */ - abstract AwsSecurityCredentials getCredentials() throws IOException; - - /** - * Gets the metrics header value that should be used for the sts request. - * - * @return the metrics header value. - */ - abstract String getMetricsHeaderValue(); - - /** - * Gets the Aws security credential supplier. - * - * @return the Supplier used to retrieve the AWS security credentials. - */ - abstract Supplier getSupplier(); + AwsSecurityCredentials getCredentials() throws IOException; } diff --git a/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java b/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java index 228502325..3f989b48f 100644 --- a/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java @@ -476,8 +476,8 @@ protected AccessToken exchangeExternalCredentialForAccessToken( if (this.shouldBuildImpersonatedCredential()) { this.impersonatedCredentials = this.buildImpersonatedCredentials(); } - if (impersonatedCredentials != null) { - return impersonatedCredentials.refreshAccessToken(); + if (this.impersonatedCredentials != null) { + return this.impersonatedCredentials.refreshAccessToken(); } StsRequestHandler.Builder requestHandler = diff --git a/oauth2_http/java/com/google/auth/oauth2/FileIdentityPoolSubjectTokenProvider.java b/oauth2_http/java/com/google/auth/oauth2/FileIdentityPoolSubjectTokenSupplier.java similarity index 67% rename from oauth2_http/java/com/google/auth/oauth2/FileIdentityPoolSubjectTokenProvider.java rename to oauth2_http/java/com/google/auth/oauth2/FileIdentityPoolSubjectTokenSupplier.java index b8fb1ce08..22edd6df4 100644 --- a/oauth2_http/java/com/google/auth/oauth2/FileIdentityPoolSubjectTokenProvider.java +++ b/oauth2_http/java/com/google/auth/oauth2/FileIdentityPoolSubjectTokenSupplier.java @@ -31,21 +31,26 @@ package com.google.auth.oauth2; -import static com.google.auth.oauth2.IdentityPoolCredentials.FILE_METRICS_HEADER_VALUE; - +import com.google.api.client.json.GenericJson; +import com.google.api.client.json.JsonObjectParser; +import com.google.auth.oauth2.IdentityPoolCredentialSource.CredentialFormatType; +import com.google.common.io.CharStreams; +import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.LinkOption; import java.nio.file.Paths; -import java.util.function.Supplier; /** * Internal provider for retrieving subject tokens for {@Link IdentityPoolCredentials} to exchange * for GCP access tokens via a local file. */ -class FileIdentityPoolSubjectTokenProvider extends IdentityPoolSubjectTokenProvider { +class FileIdentityPoolSubjectTokenSupplier implements IdentityPoolSubjectTokenSupplier { private final long serialVersionUID = 2475549052347431992L; @@ -56,32 +61,12 @@ class FileIdentityPoolSubjectTokenProvider extends IdentityPoolSubjectTokenProvi * * @param credentialSource the credential source to use. */ - FileIdentityPoolSubjectTokenProvider(IdentityPoolCredentialSource credentialSource) { + FileIdentityPoolSubjectTokenSupplier(IdentityPoolCredentialSource credentialSource) { this.credentialSource = credentialSource; } @Override - String getSubjectToken() throws IOException { - return this.retrieveSubjectTokenFromCredentialFile(); - } - - @Override - String getMetricsHeaderValue() { - return FILE_METRICS_HEADER_VALUE; - } - - @Override - Supplier getSupplier() { - return () -> { - try { - return this.getSubjectToken(); - } catch (IOException e) { - throw new RuntimeException(e); - } - }; - } - - private String retrieveSubjectTokenFromCredentialFile() throws IOException { + public String getSubjectToken() throws IOException { String credentialFilePath = this.credentialSource.credentialLocation; if (!Files.exists(Paths.get(credentialFilePath), LinkOption.NOFOLLOW_LINKS)) { throw new IOException( @@ -95,4 +80,22 @@ private String retrieveSubjectTokenFromCredentialFile() throws IOException { "Error when attempting to read the subject token from the credential file.", e); } } + + static String parseToken( + InputStream inputStream, IdentityPoolCredentialSource credentialSource) throws IOException { + if (credentialSource.credentialFormatType == CredentialFormatType.TEXT) { + BufferedReader reader = + new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); + return CharStreams.toString(reader); + } + + JsonObjectParser parser = new JsonObjectParser(OAuth2Utils.JSON_FACTORY); + GenericJson fileContents = + parser.parseAndClose(inputStream, StandardCharsets.UTF_8, GenericJson.class); + + if (!fileContents.containsKey(credentialSource.subjectTokenFieldName)) { + throw new IOException("Invalid subject token field name. No subject token was found."); + } + return (String) fileContents.get(credentialSource.subjectTokenFieldName); + } } diff --git a/oauth2_http/java/com/google/auth/oauth2/IdentityPoolCredentials.java b/oauth2_http/java/com/google/auth/oauth2/IdentityPoolCredentials.java index 4ea349b21..a61390f0c 100644 --- a/oauth2_http/java/com/google/auth/oauth2/IdentityPoolCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/IdentityPoolCredentials.java @@ -38,7 +38,6 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Map; -import java.util.function.Supplier; /** * Url-sourced, file-sourced, or user provided supplier method-sourced external account credentials. @@ -51,10 +50,8 @@ public class IdentityPoolCredentials extends ExternalAccountCredentials { static final String URL_METRICS_HEADER_VALUE = "url"; private static final long serialVersionUID = 2471046175477275881L; - private final IdentityPoolSubjectTokenProvider subjectTokenProvider; - // Boolean to tell if a Supplier was provided to build the credential. This is needed to tell - // whether the supplier should be passed when creating a new builder from this credential. - private final boolean builtWithSupplier; + private final IdentityPoolSubjectTokenSupplier subjectTokenSupplier; + private final String metricsHeaderValue; /** Internal constructor. See {@link Builder}. */ IdentityPoolCredentials(Builder builder) { @@ -72,17 +69,16 @@ public class IdentityPoolCredentials extends ExternalAccountCredentials { "A subjectTokenSupplier or a credentialSource must be provided."); } if (builder.subjectTokenSupplier != null) { - this.subjectTokenProvider = - new ProgrammaticIdentityPoolSubjectTokenProvider(builder.subjectTokenSupplier); - this.builtWithSupplier = true; + this.subjectTokenSupplier = builder.subjectTokenSupplier; + this.metricsHeaderValue = PROGRAMMATIC_METRICS_HEADER_VALUE; } else if (credentialSource.credentialSourceType == IdentityPoolCredentialSource.IdentityPoolCredentialSourceType.FILE) { - this.subjectTokenProvider = new FileIdentityPoolSubjectTokenProvider(credentialSource); - this.builtWithSupplier = false; + this.subjectTokenSupplier = new FileIdentityPoolSubjectTokenSupplier(credentialSource); + this.metricsHeaderValue = FILE_METRICS_HEADER_VALUE; } else { - this.subjectTokenProvider = - new UrlIdentityPoolSubjectTokenProvider(credentialSource, this.transportFactory); - this.builtWithSupplier = false; + this.subjectTokenSupplier = + new UrlIdentityPoolSubjectTokenSupplier(credentialSource, this.transportFactory); + this.metricsHeaderValue = URL_METRICS_HEADER_VALUE; } } @@ -103,17 +99,17 @@ public AccessToken refreshAccessToken() throws IOException { @Override public String retrieveSubjectToken() throws IOException { - return this.subjectTokenProvider.getSubjectToken(); + return this.subjectTokenSupplier.getSubjectToken(); } @Override String getCredentialSourceType() { - return this.subjectTokenProvider.getMetricsHeaderValue(); + return this.metricsHeaderValue; } @VisibleForTesting - IdentityPoolSubjectTokenProvider getIdentityPoolSubjectTokenProvider() { - return this.subjectTokenProvider; + IdentityPoolSubjectTokenSupplier getIdentityPoolSubjectTokenSupplier() { + return this.subjectTokenSupplier; } /** Clones the IdentityPoolCredentials with the specified scopes. */ @@ -133,25 +129,25 @@ public static Builder newBuilder(IdentityPoolCredentials identityPoolCredentials public static class Builder extends ExternalAccountCredentials.Builder { - private Supplier subjectTokenSupplier; + private IdentityPoolSubjectTokenSupplier subjectTokenSupplier; Builder() {} Builder(IdentityPoolCredentials credentials) { super(credentials); - if (credentials.builtWithSupplier) { - this.setSubjectTokenSupplier(credentials.subjectTokenProvider.getSupplier()); + if (this.credentialSource == null) { + this.subjectTokenSupplier = credentials.subjectTokenSupplier; } } /** * Sets the subject token supplier. The supplier should return a valid subject token string. * - * @param subjectTokenSupplier the supplier method to be called. + * @param subjectTokenSupplier the supplier to use. * @return this {@code Builder} object */ @CanIgnoreReturnValue - public Builder setSubjectTokenSupplier(Supplier subjectTokenSupplier) { + public Builder setSubjectTokenSupplier(IdentityPoolSubjectTokenSupplier subjectTokenSupplier) { this.subjectTokenSupplier = subjectTokenSupplier; return this; } diff --git a/oauth2_http/java/com/google/auth/oauth2/IdentityPoolSubjectTokenProvider.java b/oauth2_http/java/com/google/auth/oauth2/IdentityPoolSubjectTokenProvider.java deleted file mode 100644 index a127d7de2..000000000 --- a/oauth2_http/java/com/google/auth/oauth2/IdentityPoolSubjectTokenProvider.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * 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 LLC 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.oauth2; - -import com.google.api.client.json.GenericJson; -import com.google.api.client.json.JsonObjectParser; -import com.google.auth.oauth2.IdentityPoolCredentialSource.CredentialFormatType; -import com.google.common.io.CharStreams; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Serializable; -import java.nio.charset.StandardCharsets; -import java.util.function.Supplier; - -/** - * Provider for retrieving subject tokens for {@Link IdentityPoolCredentials} to exchange for GCP - * access tokens. - */ -abstract class IdentityPoolSubjectTokenProvider implements Serializable { - - /** - * Gets a subject token that can be exchanged for a GCP access token. - * - * @return a valid subject token. - * @throws IOException - */ - abstract String getSubjectToken() throws IOException; - - /** - * Gets the metrics header value that should be used for the sts request. - * - * @return the metrics header value. - */ - abstract String getMetricsHeaderValue(); - - /** - * Gets the supplier used to retrieve the subject token. - * - * @return the supplier used for getSubjectToken. - */ - abstract Supplier getSupplier(); - - protected static String parseToken( - InputStream inputStream, IdentityPoolCredentialSource credentialSource) throws IOException { - if (credentialSource.credentialFormatType == CredentialFormatType.TEXT) { - BufferedReader reader = - new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); - return CharStreams.toString(reader); - } - - JsonObjectParser parser = new JsonObjectParser(OAuth2Utils.JSON_FACTORY); - GenericJson fileContents = - parser.parseAndClose(inputStream, StandardCharsets.UTF_8, GenericJson.class); - - if (!fileContents.containsKey(credentialSource.subjectTokenFieldName)) { - throw new IOException("Invalid subject token field name. No subject token was found."); - } - return (String) fileContents.get(credentialSource.subjectTokenFieldName); - } -} diff --git a/oauth2_http/java/com/google/auth/oauth2/ProgrammaticIdentityPoolSubjectTokenProvider.java b/oauth2_http/java/com/google/auth/oauth2/IdentityPoolSubjectTokenSupplier.java similarity index 57% rename from oauth2_http/java/com/google/auth/oauth2/ProgrammaticIdentityPoolSubjectTokenProvider.java rename to oauth2_http/java/com/google/auth/oauth2/IdentityPoolSubjectTokenSupplier.java index 56e297086..21521d823 100644 --- a/oauth2_http/java/com/google/auth/oauth2/ProgrammaticIdentityPoolSubjectTokenProvider.java +++ b/oauth2_http/java/com/google/auth/oauth2/IdentityPoolSubjectTokenSupplier.java @@ -31,50 +31,21 @@ package com.google.auth.oauth2; -import static com.google.auth.oauth2.ExternalAccountCredentials.PROGRAMMATIC_METRICS_HEADER_VALUE; - import java.io.IOException; -import java.util.function.Supplier; +import java.io.Serializable; +@FunctionalInterface /** * Provider for retrieving subject tokens for {@Link IdentityPoolCredentials} to exchange for GCP - * access tokens. The subject token is retrieved by calling a user provided Supplier. + * access tokens. */ -class ProgrammaticIdentityPoolSubjectTokenProvider extends IdentityPoolSubjectTokenProvider { - - private final long serialVersionUID = 9009530138782505571L; - - private final Supplier subjectTokenSupplier; +interface IdentityPoolSubjectTokenSupplier extends Serializable { /** - * Constructor for ProgrammaticIdentityPoolSubjectTokenProvider. + * Gets a subject token that can be exchanged for a GCP access token. * - * @param subjectTokenSupplier the user defined supplier that returns a subject token. + * @return a valid subject token. + * @throws IOException */ - ProgrammaticIdentityPoolSubjectTokenProvider(Supplier subjectTokenSupplier) { - this.subjectTokenSupplier = subjectTokenSupplier; - } - - @Override - String getSubjectToken() throws IOException { - try { - return this.subjectTokenSupplier.get(); - } catch (RuntimeException e) { - throw new GoogleAuthException( - /* isRetryable= */ false, - /* retryCount= */ 0, - "Error retrieving token from subject token supplier.", - e); - } - } - - @Override - String getMetricsHeaderValue() { - return PROGRAMMATIC_METRICS_HEADER_VALUE; - } - - @Override - Supplier getSupplier() { - return this.subjectTokenSupplier; - } + String getSubjectToken() throws IOException; } diff --git a/oauth2_http/java/com/google/auth/oauth2/InternalAwsSecurityCredentialsProvider.java b/oauth2_http/java/com/google/auth/oauth2/InternalAwsSecurityCredentialsSupplier.java similarity index 90% rename from oauth2_http/java/com/google/auth/oauth2/InternalAwsSecurityCredentialsProvider.java rename to oauth2_http/java/com/google/auth/oauth2/InternalAwsSecurityCredentialsSupplier.java index f94e2a11a..308717470 100644 --- a/oauth2_http/java/com/google/auth/oauth2/InternalAwsSecurityCredentialsProvider.java +++ b/oauth2_http/java/com/google/auth/oauth2/InternalAwsSecurityCredentialsSupplier.java @@ -31,8 +31,6 @@ package com.google.auth.oauth2; -import static com.google.auth.oauth2.AwsCredentials.AWS_METRICS_HEADER_VALUE; - import com.google.api.client.http.GenericUrl; import com.google.api.client.http.HttpContent; import com.google.api.client.http.HttpHeaders; @@ -49,7 +47,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.function.Supplier; import javax.annotation.Nullable; /** @@ -57,7 +54,7 @@ * for GCP access tokens. The credentials are retrieved either via environment variables or metadata * endpoints. */ -class InternalAwsSecurityCredentialsProvider extends AwsSecurityCredentialsProvider { +class InternalAwsSecurityCredentialsSupplier implements AwsSecurityCredentialsSupplier { private static final long serialVersionUID = 4438370785261365013L; // Supported environment variables. @@ -74,7 +71,6 @@ class InternalAwsSecurityCredentialsProvider extends AwsSecurityCredentialsProvi private final AwsCredentialSource awsCredentialSource; private EnvironmentProvider environmentProvider; private transient HttpTransportFactory transportFactory; - private final String region; /** * Constructor for InternalAwsSecurityCredentialsProvider @@ -82,22 +78,18 @@ class InternalAwsSecurityCredentialsProvider extends AwsSecurityCredentialsProvi * @param awsCredentialSource the credential source to use. * @param environmentProvider the environment provider to use for environment variables. * @param transportFactory the transport factory to use for metadata requests. - * @param region an AWS region to use. If provided, getRegion will return this value instead of - * retrieving it via environment variables or metadata endpoints. */ - InternalAwsSecurityCredentialsProvider( + InternalAwsSecurityCredentialsSupplier( AwsCredentialSource awsCredentialSource, EnvironmentProvider environmentProvider, - HttpTransportFactory transportFactory, - String region) { + HttpTransportFactory transportFactory) { this.environmentProvider = environmentProvider; this.awsCredentialSource = awsCredentialSource; this.transportFactory = transportFactory; - this.region = region; } @Override - AwsSecurityCredentials getCredentials() throws IOException { + public AwsSecurityCredentials getCredentials() throws IOException { // Check environment variables for credentials first. if (canRetrieveSecurityCredentialsFromEnvironment()) { String accessKeyId = environmentProvider.getEnv(AWS_ACCESS_KEY_ID); @@ -137,12 +129,7 @@ AwsSecurityCredentials getCredentials() throws IOException { } @Override - String getRegion() throws IOException { - // If user has provided a region string, return that instead of checking environment or metadata - // server. - if (this.region != null) { - return this.region; - } + public String getRegion() throws IOException { String region; if (canRetrieveRegionFromEnvironment()) { // For AWS Lambda, the region is retrieved through the AWS_REGION environment variable. @@ -181,22 +168,6 @@ private boolean canRetrieveRegionFromEnvironment() { return false; } - @Override - String getMetricsHeaderValue() { - return AWS_METRICS_HEADER_VALUE; - } - - @Override - Supplier getSupplier() { - return () -> { - try { - return this.getCredentials(); - } catch (IOException e) { - throw new RuntimeException(e); - } - }; - } - private boolean canRetrieveSecurityCredentialsFromEnvironment() { // Check if both AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY are available. List keys = ImmutableList.of(AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY); diff --git a/oauth2_http/java/com/google/auth/oauth2/ProgrammaticAwsSecurityCredentialsProvider.java b/oauth2_http/java/com/google/auth/oauth2/ProgrammaticAwsSecurityCredentialsProvider.java deleted file mode 100644 index 55970f66f..000000000 --- a/oauth2_http/java/com/google/auth/oauth2/ProgrammaticAwsSecurityCredentialsProvider.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * 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 LLC 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.oauth2; - -import static com.google.auth.oauth2.ExternalAccountCredentials.PROGRAMMATIC_METRICS_HEADER_VALUE; - -import java.util.function.Supplier; - -/** - * Provider for retrieving AWS security credentials for {@Link AwsCredentials} to exchange for GCP - * access tokens. The credentials are retrieved by calling a Supplier that has been defined by users - * when creating the AwsCredential. - */ -class ProgrammaticAwsSecurityCredentialsProvider extends AwsSecurityCredentialsProvider { - private static final long serialVersionUID = 6699948149655089007L; - - private final String region; - private final transient Supplier awsSecurityCredentialsSupplier; - - /** - * Constructor for ProgrammaticAwsSecurityCredentialsProvider - * - * @param supplier the user defined supplier that returns AWS security credentials. - * @param region the AWS region that should be returned by getRegion(). - */ - ProgrammaticAwsSecurityCredentialsProvider( - Supplier supplier, String region) { - if (region == null || region.trim().isEmpty()) { - throw new IllegalArgumentException( - "An AWS region must be specified when using an aws security credential supplier."); - } - this.region = region; - this.awsSecurityCredentialsSupplier = supplier; - } - - @Override - String getRegion() { - return this.region; - } - - @Override - AwsSecurityCredentials getCredentials() throws GoogleAuthException { - try { - return this.awsSecurityCredentialsSupplier.get(); - } catch (RuntimeException e) { - throw new GoogleAuthException( - /* isRetryable= */ false, - /* retryCount= */ 0, - "Error retrieving token from AWS security credentials supplier.", - e); - } - } - - @Override - String getMetricsHeaderValue() { - return PROGRAMMATIC_METRICS_HEADER_VALUE; - } - - public Supplier getSupplier() { - return this.awsSecurityCredentialsSupplier; - } -} diff --git a/oauth2_http/java/com/google/auth/oauth2/UrlIdentityPoolSubjectTokenProvider.java b/oauth2_http/java/com/google/auth/oauth2/UrlIdentityPoolSubjectTokenSupplier.java similarity index 82% rename from oauth2_http/java/com/google/auth/oauth2/UrlIdentityPoolSubjectTokenProvider.java rename to oauth2_http/java/com/google/auth/oauth2/UrlIdentityPoolSubjectTokenSupplier.java index c85d68ffe..f886bfdd0 100644 --- a/oauth2_http/java/com/google/auth/oauth2/UrlIdentityPoolSubjectTokenProvider.java +++ b/oauth2_http/java/com/google/auth/oauth2/UrlIdentityPoolSubjectTokenSupplier.java @@ -31,7 +31,7 @@ package com.google.auth.oauth2; -import static com.google.auth.oauth2.IdentityPoolCredentials.URL_METRICS_HEADER_VALUE; +import static com.google.auth.oauth2.FileIdentityPoolSubjectTokenSupplier.parseToken; import com.google.api.client.http.GenericUrl; import com.google.api.client.http.HttpHeaders; @@ -40,13 +40,12 @@ import com.google.api.client.json.JsonObjectParser; import com.google.auth.http.HttpTransportFactory; import java.io.IOException; -import java.util.function.Supplier; /** * Provider for retrieving subject tokens for {@Link IdentityPoolCredentials} to exchange for GCP * access tokens. The subject token is retrieved by calling a URL that returns the token. */ -class UrlIdentityPoolSubjectTokenProvider extends IdentityPoolSubjectTokenProvider { +class UrlIdentityPoolSubjectTokenSupplier implements IdentityPoolSubjectTokenSupplier { private static final long serialVersionUID = 4964578313468011844L; @@ -59,34 +58,14 @@ class UrlIdentityPoolSubjectTokenProvider extends IdentityPoolSubjectTokenProvid * @param credentialSource the credential source to use. * @param transportFactory the transport factory to use for calling the URL. */ - UrlIdentityPoolSubjectTokenProvider( + UrlIdentityPoolSubjectTokenSupplier( IdentityPoolCredentialSource credentialSource, HttpTransportFactory transportFactory) { this.credentialSource = credentialSource; this.transportFactory = transportFactory; } @Override - String getSubjectToken() throws IOException { - return this.getSubjectTokenFromMetadataServer(); - } - - @Override - String getMetricsHeaderValue() { - return URL_METRICS_HEADER_VALUE; - } - - @Override - Supplier getSupplier() { - return () -> { - try { - return this.getSubjectToken(); - } catch (IOException e) { - throw new RuntimeException(e); - } - }; - } - - private String getSubjectTokenFromMetadataServer() throws IOException { + public String getSubjectToken() throws IOException { HttpRequest request = transportFactory .create() diff --git a/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java index 7008ce910..7b5f5e011 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java @@ -31,7 +31,6 @@ package com.google.auth.oauth2; -import static com.google.auth.oauth2.MockExternalAccountCredentialsTransport.SERVICE_ACCOUNT_IMPERSONATION_URL; import static org.junit.Assert.*; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -104,12 +103,9 @@ public class AwsCredentialsTest extends BaseSerializationTest { .setCredentialSource(AWS_CREDENTIAL_SOURCE) .build(); - private static final AwsSecurityCredentials ProgrammaticAwsCreds = + private static final AwsSecurityCredentials programmaticAwsCreds = new AwsSecurityCredentials("testAccessKey", "testSecretAccessKey", null); - private static final Supplier CredentialSupplier = - () -> ProgrammaticAwsCreds; - @Test public void test_awsCredentialSource() { String keys[] = {"region_url", "url", "imdsv2_session_token_url"}; @@ -220,10 +216,11 @@ public void refreshAccessTokenProgrammaticRefresh_withoutServiceAccountImpersona MockExternalAccountCredentialsTransportFactory transportFactory = new MockExternalAccountCredentialsTransportFactory(); + AwsSecurityCredentialsSupplier supplier = new TestAwsSecurityCredentialsSupplier("test", programmaticAwsCreds, null); + AwsCredentials awsCredential = AwsCredentials.newBuilder() - .setAwsRegion("test") - .setAwsSecurityCredentialsSupplier(CredentialSupplier) + .setAwsSecurityCredentialsSupplier(supplier) .setHttpTransportFactory(transportFactory) .setAudience("audience") .setTokenUrl(STS_URL) @@ -248,10 +245,11 @@ public void refreshAccessTokenProgrammaticRefresh_withServiceAccountImpersonatio transportFactory.transport.setExpireTime(TestUtils.getDefaultExpireTime()); + AwsSecurityCredentialsSupplier supplier = new TestAwsSecurityCredentialsSupplier("test", programmaticAwsCreds, null); + AwsCredentials awsCredential = AwsCredentials.newBuilder() - .setAwsRegion("test") - .setAwsSecurityCredentialsSupplier(CredentialSupplier) + .setAwsSecurityCredentialsSupplier(supplier) .setHttpTransportFactory(transportFactory) .setAudience("audience") .setTokenUrl(STS_URL) @@ -356,8 +354,8 @@ public void retrieveSubjectTokenWithSessionTokenUrl() throws IOException { new HashMap() { { put( - InternalAwsSecurityCredentialsProvider.AWS_IMDSV2_SESSION_TOKEN_TTL_HEADER, - InternalAwsSecurityCredentialsProvider.AWS_IMDSV2_SESSION_TOKEN_TTL); + InternalAwsSecurityCredentialsSupplier.AWS_IMDSV2_SESSION_TOKEN_TTL_HEADER, + InternalAwsSecurityCredentialsSupplier.AWS_IMDSV2_SESSION_TOKEN_TTL); } }); @@ -365,7 +363,7 @@ public void retrieveSubjectTokenWithSessionTokenUrl() throws IOException { new HashMap() { { put( - InternalAwsSecurityCredentialsProvider.AWS_IMDSV2_SESSION_TOKEN_HEADER, + InternalAwsSecurityCredentialsSupplier.AWS_IMDSV2_SESSION_TOKEN_HEADER, AWS_IMDSV2_SESSION_TOKEN); } }; @@ -602,10 +600,11 @@ public void retrieveSubjectToken_withProgrammaticRefresh() throws IOException { MockExternalAccountCredentialsTransportFactory transportFactory = new MockExternalAccountCredentialsTransportFactory(); + AwsSecurityCredentialsSupplier supplier = new TestAwsSecurityCredentialsSupplier("test", programmaticAwsCreds, null); + AwsCredentials awsCredential = AwsCredentials.newBuilder() - .setAwsRegion("test") - .setAwsSecurityCredentialsSupplier(CredentialSupplier) + .setAwsSecurityCredentialsSupplier(supplier) .setHttpTransportFactory(transportFactory) .setAudience("audience") .setTokenUrl(STS_URL) @@ -642,13 +641,11 @@ public void retrieveSubjectToken_withProgrammaticRefreshSessionToken() throws IO AwsSecurityCredentials securityCredentialsWithToken = new AwsSecurityCredentials("accessToken", "secretAccessKey", "token"); - Supplier awsSecurityCredentialsSupplierWithToken = - () -> securityCredentialsWithToken; + AwsSecurityCredentialsSupplier supplier = new TestAwsSecurityCredentialsSupplier("test", securityCredentialsWithToken, null); AwsCredentials awsCredential = AwsCredentials.newBuilder() - .setAwsRegion("test") - .setAwsSecurityCredentialsSupplier(awsSecurityCredentialsSupplierWithToken) + .setAwsSecurityCredentialsSupplier(supplier) .setHttpTransportFactory(transportFactory) .setAudience("audience") .setTokenUrl(STS_URL) @@ -679,21 +676,17 @@ public void retrieveSubjectToken_withProgrammaticRefreshSessionToken() throws IO } @Test - public void retrieveSubjectToken_withProgrammaticRefreshWrapsError() throws IOException { + public void retrieveSubjectToken_withProgrammaticRefreshThrowsError() throws IOException { MockExternalAccountCredentialsTransportFactory transportFactory = new MockExternalAccountCredentialsTransportFactory(); - RuntimeException testException = new RuntimeException("test"); + IOException testException = new IOException("test"); - Supplier errorSupplier = - () -> { - throw testException; - }; + AwsSecurityCredentialsSupplier supplier = new TestAwsSecurityCredentialsSupplier("test", null, testException); AwsCredentials awsCredential = AwsCredentials.newBuilder() - .setAwsRegion("test") - .setAwsSecurityCredentialsSupplier(errorSupplier) + .setAwsSecurityCredentialsSupplier(supplier) .setHttpTransportFactory(transportFactory) .setAudience("audience") .setTokenUrl(STS_URL) @@ -703,10 +696,9 @@ public void retrieveSubjectToken_withProgrammaticRefreshWrapsError() throws IOEx try { String subjectToken = URLDecoder.decode(awsCredential.retrieveSubjectToken(), "UTF-8"); fail("retrieveSubjectToken should not succeed"); - } catch (GoogleAuthException e) { + } catch (IOException e) { assertEquals( - "Error retrieving token from AWS security credentials supplier.", e.getMessage()); - assertEquals(testException, e.getCause()); + "test", e.getMessage()); } } @@ -723,7 +715,7 @@ public void getAwsSecurityCredentials_fromEnvironmentVariablesNoToken() throws I .build(); AwsSecurityCredentials credentials = - testAwsCredentials.getAwsSecurityCredentialsProvider().getCredentials(); + testAwsCredentials.getAwsSecurityCredentialsSupplier().getCredentials(); assertEquals("awsAccessKeyId", credentials.getAccessKeyId()); assertEquals("awsSecretAccessKey", credentials.getSecretAccessKey()); @@ -756,7 +748,7 @@ public void getAwsSecurityCredentials_fromEnvironmentVariablesWithToken() throws .build(); AwsSecurityCredentials credentials = - testAwsCredentials.getAwsSecurityCredentialsProvider().getCredentials(); + testAwsCredentials.getAwsSecurityCredentialsSupplier().getCredentials(); assertEquals("awsAccessKeyId", credentials.getAccessKeyId()); assertEquals("awsSecretAccessKey", credentials.getSecretAccessKey()); @@ -778,7 +770,7 @@ public void getAwsSecurityCredentials_fromEnvironmentVariables_noMetadataServerC .build(); AwsSecurityCredentials credentials = - testAwsCredentials.getAwsSecurityCredentialsProvider().getCredentials(); + testAwsCredentials.getAwsSecurityCredentialsSupplier().getCredentials(); assertEquals("awsAccessKeyId", credentials.getAccessKeyId()); assertEquals("awsSecretAccessKey", credentials.getSecretAccessKey()); @@ -797,7 +789,7 @@ public void getAwsSecurityCredentials_fromMetadataServer() throws IOException { .build(); AwsSecurityCredentials credentials = - awsCredential.getAwsSecurityCredentialsProvider().getCredentials(); + awsCredential.getAwsSecurityCredentialsSupplier().getCredentials(); assertEquals("accessKeyId", credentials.getAccessKeyId()); assertEquals("secretAccessKey", credentials.getSecretAccessKey()); @@ -829,7 +821,7 @@ public void getAwsSecurityCredentials_fromMetadataServer_noUrlProvided() { .build(); try { - awsCredential.getAwsSecurityCredentialsProvider().getCredentials(); + awsCredential.getAwsSecurityCredentialsSupplier().getCredentials(); fail("Should not be able to use credential without exception."); } catch (IOException exception) { assertEquals( @@ -857,7 +849,7 @@ public void getAwsRegion_awsRegionEnvironmentVariable() throws IOException { .setEnvironmentProvider(environmentProvider) .build(); - String region = awsCredentials.getAwsSecurityCredentialsProvider().getRegion(); + String region = awsCredentials.getAwsSecurityCredentialsSupplier().getRegion(); // Should attempt to retrieve the region from AWS_REGION env var first. // Metadata server would return us-east-1b. @@ -882,7 +874,7 @@ public void getAwsRegion_awsDefaultRegionEnvironmentVariable() throws IOExceptio .setEnvironmentProvider(environmentProvider) .build(); - String region = awsCredentials.getAwsSecurityCredentialsProvider().getRegion(); + String region = awsCredentials.getAwsSecurityCredentialsSupplier().getRegion(); // Should attempt to retrieve the region from DEFAULT_AWS_REGION before calling the metadata // server. Metadata server would return us-east-1b. @@ -903,7 +895,7 @@ public void getAwsRegion_metadataServer() throws IOException { .setCredentialSource(buildAwsCredentialSource(transportFactory)) .build(); - String region = awsCredentials.getAwsSecurityCredentialsProvider().getRegion(); + String region = awsCredentials.getAwsSecurityCredentialsSupplier().getRegion(); // Should retrieve the region from the Metadata server. String expectedRegion = @@ -999,12 +991,9 @@ public void credentialSource_missingRegionalCredVerificationUrl() { public void builder() throws IOException { List scopes = Arrays.asList("scope1", "scope2"); - Supplier testSupplier = () -> null; - AwsCredentials credentials = AwsCredentials.newBuilder() .setRegionalCredentialVerificationUrlOverride("https://test.com") - .setAwsRegion("region") .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) .setAudience("audience") .setSubjectTokenType("subjectTokenType") @@ -1019,7 +1008,6 @@ public void builder() throws IOException { .setScopes(scopes) .build(); - // assertEquals("region", credentials.getAwsRegion(null)); assertEquals("https://test.com", credentials.getRegionalCredentialVerificationUrlOverride()); assertEquals("audience", credentials.getAudience()); assertEquals("subjectTokenType", credentials.getSubjectTokenType()); @@ -1039,18 +1027,16 @@ public void builder() throws IOException { public void builder_defaultRegionalCredentialVerificationUrlOverride() throws IOException { List scopes = Arrays.asList("scope1", "scope2"); - Supplier testSupplier = () -> null; + AwsSecurityCredentialsSupplier supplier = new TestAwsSecurityCredentialsSupplier("region", null, null); AwsCredentials credentials = AwsCredentials.newBuilder() - .setAwsRegion("region") - .setAwsSecurityCredentialsSupplier(testSupplier) + .setAwsSecurityCredentialsSupplier(supplier) .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) .setAudience("audience") .setSubjectTokenType("subjectTokenType") .setTokenUrl(STS_URL) .setTokenInfoUrl("tokenInfoUrl") - .setTokenInfoUrl("tokenInfoUrl") .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) .setQuotaProjectId("quotaProjectId") .setClientId("clientId") @@ -1064,52 +1050,21 @@ public void builder_defaultRegionalCredentialVerificationUrlOverride() throws IO credentials.getRegionalCredentialVerificationUrl()); } - @Test - public void builder_regionRequiredWithSupplier() throws IOException { - List scopes = Arrays.asList("scope1", "scope2"); - - Supplier testSupplier = () -> null; - - try { - AwsCredentials credentials = - AwsCredentials.newBuilder() - .setAwsSecurityCredentialsSupplier(testSupplier) - .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) - .setAudience("audience") - .setSubjectTokenType("subjectTokenType") - .setTokenUrl(STS_URL) - .setTokenInfoUrl("tokenInfoUrl") - .setTokenInfoUrl("tokenInfoUrl") - .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) - .setQuotaProjectId("quotaProjectId") - .setClientId("clientId") - .setClientSecret("clientSecret") - .setScopes(scopes) - .build(); - fail("Should not be able to continue without exception."); - } catch (IllegalArgumentException exception) { - assertEquals( - "An AWS region must be specified when using an aws security credential supplier.", - exception.getMessage()); - } - } - @Test public void builder_supplierAndCredSourceThrows() throws IOException { List scopes = Arrays.asList("scope1", "scope2"); - Supplier testSupplier = () -> null; + AwsSecurityCredentialsSupplier supplier = new TestAwsSecurityCredentialsSupplier("region", null, null); try { AwsCredentials credentials = AwsCredentials.newBuilder() - .setAwsSecurityCredentialsSupplier(testSupplier) + .setAwsSecurityCredentialsSupplier(supplier) .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) .setAudience("audience") .setSubjectTokenType("subjectTokenType") .setTokenUrl(STS_URL) .setTokenInfoUrl("tokenInfoUrl") - .setTokenInfoUrl("tokenInfoUrl") .setCredentialSource(AWS_CREDENTIAL_SOURCE) .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) .setQuotaProjectId("quotaProjectId") @@ -1126,7 +1081,7 @@ public void builder_supplierAndCredSourceThrows() throws IOException { } @Test - public void builder_noSupplierOrCredSourceThrows() throws IOException { + public void builder_noSupplieOrCredSourceThrows() throws IOException { List scopes = Arrays.asList("scope1", "scope2"); Supplier testSupplier = () -> null; @@ -1250,4 +1205,30 @@ static InputStream writeAwsCredentialsStream(String stsUrl, String regionUrl, St return TestUtils.jsonToInputStream(json); } + + class TestAwsSecurityCredentialsSupplier implements AwsSecurityCredentialsSupplier { + + private String region; + private AwsSecurityCredentials credentials; + private IOException credentialException; + + TestAwsSecurityCredentialsSupplier(String region, AwsSecurityCredentials credentials, IOException credentialException){ + this.region = region; + this.credentials = credentials; + this.credentialException = credentialException; + } + + @Override + public String getRegion() throws IOException { + return this.region; + } + + @Override + public AwsSecurityCredentials getCredentials() throws IOException { + if (this.credentialException != null){ + throw this.credentialException; + } + return this.credentials; + } + } } diff --git a/oauth2_http/javatests/com/google/auth/oauth2/ITWorkloadIdentityFederationTest.java b/oauth2_http/javatests/com/google/auth/oauth2/ITWorkloadIdentityFederationTest.java index c27e7a5d4..a80acda29 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/ITWorkloadIdentityFederationTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/ITWorkloadIdentityFederationTest.java @@ -190,12 +190,10 @@ public void awsCredentials_withProgrammaticAuth() throws Exception { AwsSecurityCredentials credentials = new AwsSecurityCredentials(awsAccessKeyId, awsSecretAccessKey, awsSessionToken); - Supplier credSupplier = () -> credentials; - + AwsSecurityCredentialsSupplier provider = new ITAwsSecurityCredentialsProvider("us-east-2", credentials); AwsCredentials awsCredential = AwsCredentials.newBuilder() - .setAwsSecurityCredentialsSupplier(credSupplier) - .setAwsRegion("us-east-2") + .setAwsSecurityCredentialsSupplier(provider) .setSubjectTokenType(SubjectTokenTypes.AWS4) .setAudience(AWS_AUDIENCE) .setServiceAccountImpersonationUrl( @@ -259,7 +257,7 @@ public void identityPoolCredentials_withServiceAccountImpersonationOptions() thr @Test public void identityPoolCredentials_withProgrammaticAuth() throws IOException { - Supplier tokenSupplier = + IdentityPoolSubjectTokenSupplier tokenSupplier = () -> { try { return generateGoogleIdToken(OIDC_AUDIENCE); @@ -453,4 +451,25 @@ private String getXmlValueByTagName(String rawXml, String tagName) { } return null; } + + private class ITAwsSecurityCredentialsProvider implements AwsSecurityCredentialsSupplier { + + private String region; + private AwsSecurityCredentials credentials; + + ITAwsSecurityCredentialsProvider(String region, AwsSecurityCredentials credentials){ + this.region = region; + this.credentials = credentials; + } + + @Override + public String getRegion(){ + return this.region; + } + + @Override + public AwsSecurityCredentials getCredentials(){ + return this.credentials; + } + } } diff --git a/oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java index d9471adb6..79316662e 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java @@ -49,7 +49,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.function.Supplier; import javax.annotation.Nullable; import org.junit.Test; import org.junit.runner.RunWith; @@ -82,7 +81,7 @@ public class IdentityPoolCredentialsTest extends BaseSerializationTest { .setCredentialSource(FILE_CREDENTIAL_SOURCE) .build(); - private static final Supplier testSupplier = () -> "testSubjectToken"; + private static final IdentityPoolSubjectTokenSupplier testProvider = () -> "testSubjectToken"; static class MockExternalAccountCredentialsTransportFactory implements HttpTransportFactory { @@ -306,39 +305,38 @@ public void retrieveSubjectToken_urlSourcedCredential_throws() { } @Test - public void retrieveSubjectToken_supplier() throws IOException { + public void retrieveSubjectToken_provider() throws IOException { IdentityPoolCredentials credentials = IdentityPoolCredentials.newBuilder(FILE_SOURCED_CREDENTIAL) .setCredentialSource(null) - .setSubjectTokenSupplier(testSupplier) + .setSubjectTokenSupplier(testProvider) .build(); String subjectToken = credentials.retrieveSubjectToken(); - assertEquals(testSupplier.get(), subjectToken); + assertEquals(testProvider.getSubjectToken(), subjectToken); } @Test - public void retrieveSubjectToken_supplierWrapsError() throws IOException { - RuntimeException testException = new RuntimeException("test"); + public void retrieveSubjectToken_providerThrowsError() throws IOException { + IOException testException = new IOException("test"); - Supplier errorSupplier = + IdentityPoolSubjectTokenSupplier errorProvider = () -> { throw testException; }; IdentityPoolCredentials credentials = IdentityPoolCredentials.newBuilder(FILE_SOURCED_CREDENTIAL) .setCredentialSource(null) - .setSubjectTokenSupplier(errorSupplier) + .setSubjectTokenSupplier(errorProvider) .build(); try { String subjectToken = credentials.retrieveSubjectToken(); fail("retrieveSubjectToken should fail."); - } catch (GoogleAuthException e) { - assertEquals("Error retrieving token from subject token supplier.", e.getMessage()); - assertEquals(testException, e.getCause()); + } catch (IOException e) { + assertEquals("test", e.getMessage()); } } @@ -476,14 +474,14 @@ public void refreshAccessToken_withServiceAccountImpersonationOptions() throws I } @Test - public void refreshAccessToken_supplier() throws IOException { + public void refreshAccessToken_Provider() throws IOException { MockExternalAccountCredentialsTransportFactory transportFactory = new MockExternalAccountCredentialsTransportFactory(); transportFactory.transport.setExpireTime(TestUtils.getDefaultExpireTime()); IdentityPoolCredentials credential = IdentityPoolCredentials.newBuilder() - .setSubjectTokenSupplier(testSupplier) + .setSubjectTokenSupplier(testProvider) .setAudience( "//iam.googleapis.com/projects/123/locations/global/workloadIdentityPools/pool/providers/provider") .setSubjectTokenType("subjectTokenType") @@ -503,14 +501,14 @@ public void refreshAccessToken_supplier() throws IOException { } @Test - public void refreshAccessToken_supplierWithServiceAccountImpersonation() throws IOException { + public void refreshAccessToken_providerWithServiceAccountImpersonation() throws IOException { MockExternalAccountCredentialsTransportFactory transportFactory = new MockExternalAccountCredentialsTransportFactory(); transportFactory.transport.setExpireTime(TestUtils.getDefaultExpireTime()); IdentityPoolCredentials credential = IdentityPoolCredentials.newBuilder() - .setSubjectTokenSupplier(testSupplier) + .setSubjectTokenSupplier(testProvider) .setAudience( "//iam.googleapis.com/projects/123/locations/global/workloadIdentityPools/pool/providers/provider") .setSubjectTokenType("subjectTokenType") @@ -793,7 +791,7 @@ public void builder_subjectTokenSupplier() { IdentityPoolCredentials credentials = IdentityPoolCredentials.newBuilder() - .setSubjectTokenSupplier(testSupplier) + .setSubjectTokenSupplier(testProvider) .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) .setAudience("audience") .setSubjectTokenType("subjectTokenType") @@ -806,7 +804,7 @@ public void builder_subjectTokenSupplier() { .setScopes(scopes) .build(); - assertEquals(testSupplier, credentials.getIdentityPoolSubjectTokenProvider().getSupplier()); + assertEquals(testProvider, credentials.getIdentityPoolSubjectTokenSupplier()); } @Test @@ -868,7 +866,7 @@ public void builder_supplierAndCredSourceThrows() throws IOException { try { IdentityPoolCredentials credentials = IdentityPoolCredentials.newBuilder() - .setSubjectTokenSupplier(testSupplier) + .setSubjectTokenSupplier(testProvider) .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) .setAudience("audience") .setSubjectTokenType("subjectTokenType") diff --git a/oauth2_http/javatests/com/google/auth/oauth2/InternalAwsSecurityCredentialsProviderTest.java b/oauth2_http/javatests/com/google/auth/oauth2/InternalAwsSecurityCredentialsSupplierTest.java similarity index 81% rename from oauth2_http/javatests/com/google/auth/oauth2/InternalAwsSecurityCredentialsProviderTest.java rename to oauth2_http/javatests/com/google/auth/oauth2/InternalAwsSecurityCredentialsSupplierTest.java index 7552c88d1..f95bd4cab 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/InternalAwsSecurityCredentialsProviderTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/InternalAwsSecurityCredentialsSupplierTest.java @@ -42,9 +42,9 @@ import org.junit.runner.RunWith; import org.junit.runners.JUnit4; -/** Tests for {@link InternalAwsSecurityCredentialsProvider}. */ +/** Tests for {@link InternalAwsSecurityCredentialsSupplier}. */ @RunWith(JUnit4.class) -public class InternalAwsSecurityCredentialsProviderTest { +public class InternalAwsSecurityCredentialsSupplierTest { @Test public void shouldUseMetadataServer_withRequiredEnvironmentVariables() { MockExternalAccountCredentialsTransportFactory transportFactory = @@ -59,13 +59,12 @@ public void shouldUseMetadataServer_withRequiredEnvironmentVariables() { .setEnv(regionKey, "awsRegion") .setEnv("AWS_ACCESS_KEY_ID", "awsAccessKeyId") .setEnv("AWS_SECRET_ACCESS_KEY", "awsSecretAccessKey"); - InternalAwsSecurityCredentialsProvider provider = - new InternalAwsSecurityCredentialsProvider( + InternalAwsSecurityCredentialsSupplier supplier = + new InternalAwsSecurityCredentialsSupplier( buildAwsImdsv2CredentialSource(transportFactory), environmentProvider, - transportFactory, - null); - assertFalse(provider.shouldUseMetadataServer()); + transportFactory); + assertFalse(supplier.shouldUseMetadataServer()); } } @@ -78,13 +77,12 @@ public void shouldUseMetadataServer_missingRegion() { environmentProvider .setEnv("AWS_ACCESS_KEY_ID", "awsAccessKeyId") .setEnv("AWS_SECRET_ACCESS_KEY", "awsSecretAccessKey"); - InternalAwsSecurityCredentialsProvider provider = - new InternalAwsSecurityCredentialsProvider( + InternalAwsSecurityCredentialsSupplier supplier = + new InternalAwsSecurityCredentialsSupplier( buildAwsImdsv2CredentialSource(transportFactory), environmentProvider, - transportFactory, - null); - assertTrue(provider.shouldUseMetadataServer()); + transportFactory); + assertTrue(supplier.shouldUseMetadataServer()); } @Test @@ -100,13 +98,12 @@ public void shouldUseMetadataServer_missingAwsAccessKeyId() { environmentProvider .setEnv(regionKey, "awsRegion") .setEnv("AWS_SECRET_ACCESS_KEY", "awsSecretAccessKey"); - InternalAwsSecurityCredentialsProvider provider = - new InternalAwsSecurityCredentialsProvider( + InternalAwsSecurityCredentialsSupplier supplier = + new InternalAwsSecurityCredentialsSupplier( buildAwsImdsv2CredentialSource(transportFactory), environmentProvider, - transportFactory, - null); - assertTrue(provider.shouldUseMetadataServer()); + transportFactory); + assertTrue(supplier.shouldUseMetadataServer()); } } @@ -123,13 +120,12 @@ public void shouldUseMetadataServer_missingAwsSecretAccessKey() { environmentProvider .setEnv(regionKey, "awsRegion") .setEnv("AWS_ACCESS_KEY_ID", "awsAccessKeyId"); - InternalAwsSecurityCredentialsProvider provider = - new InternalAwsSecurityCredentialsProvider( + InternalAwsSecurityCredentialsSupplier supplier = + new InternalAwsSecurityCredentialsSupplier( buildAwsImdsv2CredentialSource(transportFactory), environmentProvider, - transportFactory, - null); - assertTrue(provider.shouldUseMetadataServer()); + transportFactory); + assertTrue(supplier.shouldUseMetadataServer()); } } @@ -145,13 +141,12 @@ public void shouldUseMetadataServer_missingAwsSecurityCreds() { // AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY are always required. // Not set here. environmentProvider.setEnv(regionKey, "awsRegion"); - InternalAwsSecurityCredentialsProvider provider = - new InternalAwsSecurityCredentialsProvider( + InternalAwsSecurityCredentialsSupplier supplier = + new InternalAwsSecurityCredentialsSupplier( buildAwsImdsv2CredentialSource(transportFactory), environmentProvider, - transportFactory, - null); - assertTrue(provider.shouldUseMetadataServer()); + transportFactory); + assertTrue(supplier.shouldUseMetadataServer()); } } @@ -160,12 +155,11 @@ public void shouldUseMetadataServer_noEnvironmentVars() { TestEnvironmentProvider environmentProvider = new TestEnvironmentProvider(); MockExternalAccountCredentialsTransportFactory transportFactory = new MockExternalAccountCredentialsTransportFactory(); - InternalAwsSecurityCredentialsProvider provider = - new InternalAwsSecurityCredentialsProvider( + InternalAwsSecurityCredentialsSupplier supplier = + new InternalAwsSecurityCredentialsSupplier( buildAwsImdsv2CredentialSource(transportFactory), environmentProvider, - transportFactory, - null); - assertTrue(provider.shouldUseMetadataServer()); + transportFactory); + assertTrue(supplier.shouldUseMetadataServer()); } } From dd659c443d23001ce15e1dc0eb83a377912c2862 Mon Sep 17 00:00:00 2001 From: aeitzman Date: Wed, 10 Jan 2024 13:07:02 -0800 Subject: [PATCH 26/32] formatting --- .../google/auth/oauth2/AwsCredentials.java | 7 ++--- .../FileIdentityPoolSubjectTokenSupplier.java | 4 +-- .../auth/oauth2/AwsCredentialsTest.java | 29 ++++++++++++------- .../ITWorkloadIdentityFederationTest.java | 10 +++---- 4 files changed, 27 insertions(+), 23 deletions(-) diff --git a/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java b/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java index 6fa60c652..215c77a64 100644 --- a/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java @@ -103,9 +103,7 @@ public class AwsCredentials extends ExternalAccountCredentials { } else { this.awsSecurityCredentialsSupplier = new InternalAwsSecurityCredentialsSupplier( - credentialSource, - this.getEnvironmentProvider(), - this.transportFactory); + credentialSource, this.getEnvironmentProvider(), this.transportFactory); this.metricsHeaderValue = AWS_METRICS_HEADER_VALUE; } } @@ -252,8 +250,7 @@ public static class Builder extends ExternalAccountCredentials.Builder { * @return this {@code Builder} object */ @CanIgnoreReturnValue - public Builder setAwsSecurityCredentialsSupplier - ( + public Builder setAwsSecurityCredentialsSupplier( AwsSecurityCredentialsSupplier awsSecurityCredentialsSupplier) { this.awsSecurityCredentialsSupplier = awsSecurityCredentialsSupplier; return this; diff --git a/oauth2_http/java/com/google/auth/oauth2/FileIdentityPoolSubjectTokenSupplier.java b/oauth2_http/java/com/google/auth/oauth2/FileIdentityPoolSubjectTokenSupplier.java index 22edd6df4..e46df2d9e 100644 --- a/oauth2_http/java/com/google/auth/oauth2/FileIdentityPoolSubjectTokenSupplier.java +++ b/oauth2_http/java/com/google/auth/oauth2/FileIdentityPoolSubjectTokenSupplier.java @@ -81,8 +81,8 @@ public String getSubjectToken() throws IOException { } } - static String parseToken( - InputStream inputStream, IdentityPoolCredentialSource credentialSource) throws IOException { + static String parseToken(InputStream inputStream, IdentityPoolCredentialSource credentialSource) + throws IOException { if (credentialSource.credentialFormatType == CredentialFormatType.TEXT) { BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); diff --git a/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java index 7b5f5e011..d4bb90c0d 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java @@ -216,7 +216,8 @@ public void refreshAccessTokenProgrammaticRefresh_withoutServiceAccountImpersona MockExternalAccountCredentialsTransportFactory transportFactory = new MockExternalAccountCredentialsTransportFactory(); - AwsSecurityCredentialsSupplier supplier = new TestAwsSecurityCredentialsSupplier("test", programmaticAwsCreds, null); + AwsSecurityCredentialsSupplier supplier = + new TestAwsSecurityCredentialsSupplier("test", programmaticAwsCreds, null); AwsCredentials awsCredential = AwsCredentials.newBuilder() @@ -245,7 +246,8 @@ public void refreshAccessTokenProgrammaticRefresh_withServiceAccountImpersonatio transportFactory.transport.setExpireTime(TestUtils.getDefaultExpireTime()); - AwsSecurityCredentialsSupplier supplier = new TestAwsSecurityCredentialsSupplier("test", programmaticAwsCreds, null); + AwsSecurityCredentialsSupplier supplier = + new TestAwsSecurityCredentialsSupplier("test", programmaticAwsCreds, null); AwsCredentials awsCredential = AwsCredentials.newBuilder() @@ -600,7 +602,8 @@ public void retrieveSubjectToken_withProgrammaticRefresh() throws IOException { MockExternalAccountCredentialsTransportFactory transportFactory = new MockExternalAccountCredentialsTransportFactory(); - AwsSecurityCredentialsSupplier supplier = new TestAwsSecurityCredentialsSupplier("test", programmaticAwsCreds, null); + AwsSecurityCredentialsSupplier supplier = + new TestAwsSecurityCredentialsSupplier("test", programmaticAwsCreds, null); AwsCredentials awsCredential = AwsCredentials.newBuilder() @@ -641,7 +644,8 @@ public void retrieveSubjectToken_withProgrammaticRefreshSessionToken() throws IO AwsSecurityCredentials securityCredentialsWithToken = new AwsSecurityCredentials("accessToken", "secretAccessKey", "token"); - AwsSecurityCredentialsSupplier supplier = new TestAwsSecurityCredentialsSupplier("test", securityCredentialsWithToken, null); + AwsSecurityCredentialsSupplier supplier = + new TestAwsSecurityCredentialsSupplier("test", securityCredentialsWithToken, null); AwsCredentials awsCredential = AwsCredentials.newBuilder() @@ -682,7 +686,8 @@ public void retrieveSubjectToken_withProgrammaticRefreshThrowsError() throws IOE IOException testException = new IOException("test"); - AwsSecurityCredentialsSupplier supplier = new TestAwsSecurityCredentialsSupplier("test", null, testException); + AwsSecurityCredentialsSupplier supplier = + new TestAwsSecurityCredentialsSupplier("test", null, testException); AwsCredentials awsCredential = AwsCredentials.newBuilder() @@ -697,8 +702,7 @@ public void retrieveSubjectToken_withProgrammaticRefreshThrowsError() throws IOE String subjectToken = URLDecoder.decode(awsCredential.retrieveSubjectToken(), "UTF-8"); fail("retrieveSubjectToken should not succeed"); } catch (IOException e) { - assertEquals( - "test", e.getMessage()); + assertEquals("test", e.getMessage()); } } @@ -1027,7 +1031,8 @@ public void builder() throws IOException { public void builder_defaultRegionalCredentialVerificationUrlOverride() throws IOException { List scopes = Arrays.asList("scope1", "scope2"); - AwsSecurityCredentialsSupplier supplier = new TestAwsSecurityCredentialsSupplier("region", null, null); + AwsSecurityCredentialsSupplier supplier = + new TestAwsSecurityCredentialsSupplier("region", null, null); AwsCredentials credentials = AwsCredentials.newBuilder() @@ -1054,7 +1059,8 @@ public void builder_defaultRegionalCredentialVerificationUrlOverride() throws IO public void builder_supplierAndCredSourceThrows() throws IOException { List scopes = Arrays.asList("scope1", "scope2"); - AwsSecurityCredentialsSupplier supplier = new TestAwsSecurityCredentialsSupplier("region", null, null); + AwsSecurityCredentialsSupplier supplier = + new TestAwsSecurityCredentialsSupplier("region", null, null); try { AwsCredentials credentials = @@ -1212,7 +1218,8 @@ class TestAwsSecurityCredentialsSupplier implements AwsSecurityCredentialsSuppli private AwsSecurityCredentials credentials; private IOException credentialException; - TestAwsSecurityCredentialsSupplier(String region, AwsSecurityCredentials credentials, IOException credentialException){ + TestAwsSecurityCredentialsSupplier( + String region, AwsSecurityCredentials credentials, IOException credentialException) { this.region = region; this.credentials = credentials; this.credentialException = credentialException; @@ -1225,7 +1232,7 @@ public String getRegion() throws IOException { @Override public AwsSecurityCredentials getCredentials() throws IOException { - if (this.credentialException != null){ + if (this.credentialException != null) { throw this.credentialException; } return this.credentials; diff --git a/oauth2_http/javatests/com/google/auth/oauth2/ITWorkloadIdentityFederationTest.java b/oauth2_http/javatests/com/google/auth/oauth2/ITWorkloadIdentityFederationTest.java index a80acda29..8f806cd95 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/ITWorkloadIdentityFederationTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/ITWorkloadIdentityFederationTest.java @@ -55,7 +55,6 @@ import java.time.Instant; import java.util.HashMap; import java.util.Map; -import java.util.function.Supplier; import org.junit.Before; import org.junit.Test; @@ -190,7 +189,8 @@ public void awsCredentials_withProgrammaticAuth() throws Exception { AwsSecurityCredentials credentials = new AwsSecurityCredentials(awsAccessKeyId, awsSecretAccessKey, awsSessionToken); - AwsSecurityCredentialsSupplier provider = new ITAwsSecurityCredentialsProvider("us-east-2", credentials); + AwsSecurityCredentialsSupplier provider = + new ITAwsSecurityCredentialsProvider("us-east-2", credentials); AwsCredentials awsCredential = AwsCredentials.newBuilder() .setAwsSecurityCredentialsSupplier(provider) @@ -457,18 +457,18 @@ private class ITAwsSecurityCredentialsProvider implements AwsSecurityCredentials private String region; private AwsSecurityCredentials credentials; - ITAwsSecurityCredentialsProvider(String region, AwsSecurityCredentials credentials){ + ITAwsSecurityCredentialsProvider(String region, AwsSecurityCredentials credentials) { this.region = region; this.credentials = credentials; } @Override - public String getRegion(){ + public String getRegion() { return this.region; } @Override - public AwsSecurityCredentials getCredentials(){ + public AwsSecurityCredentials getCredentials() { return this.credentials; } } From 8963d688df183236ed1c6d284cc840d1e3f7854c Mon Sep 17 00:00:00 2001 From: aeitzman Date: Wed, 17 Jan 2024 13:57:29 -0800 Subject: [PATCH 27/32] updating codeowners --- .github/CODEOWNERS | 60 +++++++++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 27 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index b25d5f6db..08eaf507a 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -18,32 +18,37 @@ samples/**/*.java @googleapis/java-samples-reviewers samples/snippets/generated/ @googleapis/yoshi-java # 3PI-related files and related base classes - joint ownership between googleapis-auth and aion-sdk -oauth2_http/java/com/google/auth/oauth2/ActingParty.java @googleapis/googleapis-auth @googleapis/aion-sdk -oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java @googleapis/googleapis-auth @googleapis/aion-sdk -oauth2_http/java/com/google/auth/oauth2/AwsDates.java @googleapis/googleapis-auth @googleapis/aion-sdk -oauth2_http/java/com/google/auth/oauth2/AwsRequestSignature.java @googleapis/googleapis-auth @googleapis/aion-sdk -oauth2_http/java/com/google/auth/oauth2/AwsRequestSigner.java @googleapis/googleapis-auth @googleapis/aion-sdk -oauth2_http/java/com/google/auth/oauth2/AwsSecurityCredentials.java @googleapis/googleapis-auth @googleapis/aion-sdk -oauth2_http/java/com/google/auth/oauth2/CredentialAccessBoundary.java @googleapis/googleapis-auth @googleapis/aion-sdk -oauth2_http/java/com/google/auth/oauth2/DefaultCredentialsProvider.java @googleapis/googleapis-auth @googleapis/aion-sdk -oauth2_http/java/com/google/auth/oauth2/DownscopedCredentials.java @googleapis/googleapis-auth @googleapis/aion-sdk -oauth2_http/java/com/google/auth/oauth2/EnvironmentProvider.java @googleapis/googleapis-auth @googleapis/aion-sdk -oauth2_http/java/com/google/auth/oauth2/ExecutableHandler.java @googleapis/googleapis-auth @googleapis/aion-sdk -oauth2_http/java/com/google/auth/oauth2/ExecutableResponse.java @googleapis/googleapis-auth @googleapis/aion-sdk -oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java @googleapis/googleapis-auth @googleapis/aion-sdk -oauth2_http/java/com/google/auth/oauth2/GoogleCredentials.java @googleapis/googleapis-auth @googleapis/aion-sdk -oauth2_http/java/com/google/auth/oauth2/IdentityPoolCredentials.java @googleapis/googleapis-auth @googleapis/aion-sdk -oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java @googleapis/googleapis-auth @googleapis/aion-sdk -oauth2_http/java/com/google/auth/oauth2/OAuth2Credentials.java @googleapis/googleapis-auth @googleapis/aion-sdk -oauth2_http/java/com/google/auth/oauth2/OAuth2CredentialsWithRefresh.java @googleapis/googleapis-auth @googleapis/aion-sdk -oauth2_http/java/com/google/auth/oauth2/OAuthException.java @googleapis/googleapis-auth @googleapis/aion-sdk -oauth2_http/java/com/google/auth/oauth2/PluggableAuthCredentials.java @googleapis/googleapis-auth @googleapis/aion-sdk -oauth2_http/java/com/google/auth/oauth2/PluggableAuthException.java @googleapis/googleapis-auth @googleapis/aion-sdk -oauth2_http/java/com/google/auth/oauth2/PluggableAuthHandler.java @googleapis/googleapis-auth @googleapis/aion-sdk -oauth2_http/java/com/google/auth/oauth2/StsRequestHandler.java @googleapis/googleapis-auth @googleapis/aion-sdk -oauth2_http/java/com/google/auth/oauth2/StsTokenExchangeRequest.java @googleapis/googleapis-auth @googleapis/aion-sdk -oauth2_http/java/com/google/auth/oauth2/StsTokenExchangeResponse.java @googleapis/googleapis-auth @googleapis/aion-sdk -oauth2_http/java/com/google/auth/oauth2/SystemEnvironmentProvider.java @googleapis/googleapis-auth @googleapis/aion-sdk +oauth2_http/java/com/google/auth/oauth2/ActingParty.java @googleapis/googleapis-auth @googleapis/aion-sdk +oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java @googleapis/googleapis-auth @googleapis/aion-sdk +oauth2_http/java/com/google/auth/oauth2/AwsSecurityCredentialsSupplier.java @googleapis/googleapis-auth @googleapis/aion-sdk +oauth2_http/java/com/google/auth/oauth2/AwsDates.java @googleapis/googleapis-auth @googleapis/aion-sdk +oauth2_http/java/com/google/auth/oauth2/AwsRequestSignature.java @googleapis/googleapis-auth @googleapis/aion-sdk +oauth2_http/java/com/google/auth/oauth2/AwsRequestSigner.java @googleapis/googleapis-auth @googleapis/aion-sdk +oauth2_http/java/com/google/auth/oauth2/AwsSecurityCredentials.java @googleapis/googleapis-auth @googleapis/aion-sdk +oauth2_http/java/com/google/auth/oauth2/CredentialAccessBoundary.java @googleapis/googleapis-auth @googleapis/aion-sdk +oauth2_http/java/com/google/auth/oauth2/DefaultCredentialsProvider.java @googleapis/googleapis-auth @googleapis/aion-sdk +oauth2_http/java/com/google/auth/oauth2/DownscopedCredentials.java @googleapis/googleapis-auth @googleapis/aion-sdk +oauth2_http/java/com/google/auth/oauth2/EnvironmentProvider.java @googleapis/googleapis-auth @googleapis/aion-sdk +oauth2_http/java/com/google/auth/oauth2/ExecutableHandler.java @googleapis/googleapis-auth @googleapis/aion-sdk +oauth2_http/java/com/google/auth/oauth2/ExecutableResponse.java @googleapis/googleapis-auth @googleapis/aion-sdk +oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java @googleapis/googleapis-auth @googleapis/aion-sdk +oauth2_http/java/com/google/auth/oauth2/FileIdentityPoolSubjectTokenSupplier.java @googleapis/googleapis-auth @googleapis/aion-sdk +oauth2_http/java/com/google/auth/oauth2/GoogleCredentials.java @googleapis/googleapis-auth @googleapis/aion-sdk +oauth2_http/java/com/google/auth/oauth2/IdentityPoolCredentials.java @googleapis/googleapis-auth @googleapis/aion-sdk +oauth2_http/java/com/google/auth/oauth2/IdentityPoolSubjectTokenSupplier.java @googleapis/googleapis-auth @googleapis/aion-sdk +oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java @googleapis/googleapis-auth @googleapis/aion-sdk +oauth2_http/java/com/google/auth/oauth2/InternalAwsSecurityCredentialsSupplier.java @googleapis/googleapis-auth @googleapis/aion-sdk +oauth2_http/java/com/google/auth/oauth2/OAuth2Credentials.java @googleapis/googleapis-auth @googleapis/aion-sdk +oauth2_http/java/com/google/auth/oauth2/OAuth2CredentialsWithRefresh.java @googleapis/googleapis-auth @googleapis/aion-sdk +oauth2_http/java/com/google/auth/oauth2/OAuthException.java @googleapis/googleapis-auth @googleapis/aion-sdk +oauth2_http/java/com/google/auth/oauth2/PluggableAuthCredentials.java @googleapis/googleapis-auth @googleapis/aion-sdk +oauth2_http/java/com/google/auth/oauth2/PluggableAuthException.java @googleapis/googleapis-auth @googleapis/aion-sdk +oauth2_http/java/com/google/auth/oauth2/PluggableAuthHandler.java @googleapis/googleapis-auth @googleapis/aion-sdk +oauth2_http/java/com/google/auth/oauth2/StsRequestHandler.java @googleapis/googleapis-auth @googleapis/aion-sdk +oauth2_http/java/com/google/auth/oauth2/StsTokenExchangeRequest.java @googleapis/googleapis-auth @googleapis/aion-sdk +oauth2_http/java/com/google/auth/oauth2/StsTokenExchangeResponse.java @googleapis/googleapis-auth @googleapis/aion-sdk +oauth2_http/java/com/google/auth/oauth2/SystemEnvironmentProvider.java @googleapis/googleapis-auth @googleapis/aion-sdk +oauth2_http/java/com/google/auth/oauth2/UrlIdentityPoolSubjectTokenSupplier.java @googleapis/googleapis-auth @googleapis/aion-sdk oauth2_http/javatests/com/google/auth/AwsCredentialsTest.java @googleapis/googleapis-auth @googleapis/aion-sdk oauth2_http/javatests/com/google/auth/AwsRequestSignerTest.java @googleapis/googleapis-auth @googleapis/aion-sdk oauth2_http/javatests/com/google/auth/CredentialAccessBoundaryTest.java @googleapis/googleapis-auth @googleapis/aion-sdk @@ -55,6 +60,7 @@ oauth2_http/javatests/com/google/auth/ITDownscopingTest.java oauth2_http/javatests/com/google/auth/ITWorkloadIdentityFederationTest.java @googleapis/googleapis-auth @googleapis/aion-sdk oauth2_http/javatests/com/google/auth/IdentityPoolCredentialsTest.java @googleapis/googleapis-auth @googleapis/aion-sdk oauth2_http/javatests/com/google/auth/ImpersonatedCredentialsTest.java @googleapis/googleapis-auth @googleapis/aion-sdk +oauth2_http/javatests/com/google/auth/InternalAwsSecurityCredentialsSupplierTest.java @googleapis/googleapis-auth @googleapis/aion-sdk oauth2_http/javatests/com/google/auth/MockExternalAccountCredentialsTransport.java @googleapis/googleapis-auth @googleapis/aion-sdk oauth2_http/javatests/com/google/auth/MockStsTransport.java @googleapis/googleapis-auth @googleapis/aion-sdk oauth2_http/javatests/com/google/auth/OAuth2CredentialsTest.java @googleapis/googleapis-auth @googleapis/aion-sdk @@ -65,4 +71,4 @@ oauth2_http/javatests/com/google/auth/PluggableAuthExceptionTest.java oauth2_http/javatests/com/google/auth/PluggableAuthHandlerTest.java @googleapis/googleapis-auth @googleapis/aion-sdk oauth2_http/javatests/com/google/auth/StsRequestHandlerTest.java @googleapis/googleapis-auth @googleapis/aion-sdk oauth2_http/javatests/com/google/auth/TestEnvironmentProvider.java @googleapis/googleapis-auth @googleapis/aion-sdk -README.md @googleapis/googleapis-auth @googleapis/aion-sdk \ No newline at end of file +README.md @googleapis/googleapis-auth @googleapis/aion-sdk From 20c2f7dd71fdcf45fc6d96cd40f43dc54a37dd82 Mon Sep 17 00:00:00 2001 From: aeitzman Date: Fri, 19 Jan 2024 09:56:15 -0800 Subject: [PATCH 28/32] make subject token supplier interface public --- .../google/auth/oauth2/IdentityPoolSubjectTokenSupplier.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/oauth2_http/java/com/google/auth/oauth2/IdentityPoolSubjectTokenSupplier.java b/oauth2_http/java/com/google/auth/oauth2/IdentityPoolSubjectTokenSupplier.java index 21521d823..a057bba48 100644 --- a/oauth2_http/java/com/google/auth/oauth2/IdentityPoolSubjectTokenSupplier.java +++ b/oauth2_http/java/com/google/auth/oauth2/IdentityPoolSubjectTokenSupplier.java @@ -39,7 +39,7 @@ * Provider for retrieving subject tokens for {@Link IdentityPoolCredentials} to exchange for GCP * access tokens. */ -interface IdentityPoolSubjectTokenSupplier extends Serializable { +public interface IdentityPoolSubjectTokenSupplier extends Serializable { /** * Gets a subject token that can be exchanged for a GCP access token. From abd462b3909c96435cd36f306ee127811259f1c3 Mon Sep 17 00:00:00 2001 From: aeitzman Date: Mon, 22 Jan 2024 09:38:46 -0800 Subject: [PATCH 29/32] Making AwsSecurityCredentials public and change name to sessionToken --- .../google/auth/oauth2/AwsRequestSigner.java | 4 +-- .../auth/oauth2/AwsSecurityCredentials.java | 35 ++++++++++++++----- .../auth/oauth2/AwsCredentialsTest.java | 8 ++--- 3 files changed, 33 insertions(+), 14 deletions(-) diff --git a/oauth2_http/java/com/google/auth/oauth2/AwsRequestSigner.java b/oauth2_http/java/com/google/auth/oauth2/AwsRequestSigner.java index a890c8814..baf855124 100644 --- a/oauth2_http/java/com/google/auth/oauth2/AwsRequestSigner.java +++ b/oauth2_http/java/com/google/auth/oauth2/AwsRequestSigner.java @@ -237,8 +237,8 @@ private Map getCanonicalHeaders(String defaultDate) { headers.put("x-amz-date", defaultDate); } - if (awsSecurityCredentials.getToken() != null && !awsSecurityCredentials.getToken().isEmpty()) { - headers.put("x-amz-security-token", awsSecurityCredentials.getToken()); + if (awsSecurityCredentials.getSessionToken() != null && !awsSecurityCredentials.getSessionToken().isEmpty()) { + headers.put("x-amz-security-token", awsSecurityCredentials.getSessionToken()); } // Add all additional headers. diff --git a/oauth2_http/java/com/google/auth/oauth2/AwsSecurityCredentials.java b/oauth2_http/java/com/google/auth/oauth2/AwsSecurityCredentials.java index b7865049a..ddbaf338f 100644 --- a/oauth2_http/java/com/google/auth/oauth2/AwsSecurityCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/AwsSecurityCredentials.java @@ -37,29 +37,48 @@ * Defines AWS security credentials. These are either retrieved from the AWS security_credentials * endpoint or AWS environment variables. */ -class AwsSecurityCredentials { +public class AwsSecurityCredentials { private final String accessKeyId; private final String secretAccessKey; - @Nullable private final String token; + @Nullable private final String sessionToken; - AwsSecurityCredentials(String accessKeyId, String secretAccessKey, @Nullable String token) { + /** + * Constructor for AWSSecurityCredentials. + * + * @param accessKeyId the AWS access Key Id. + * @param secretAccessKey the AWS secret access key. + * @param sessionToken the AWS session token. Optional. + */ + public AwsSecurityCredentials(String accessKeyId, String secretAccessKey, @Nullable String sessionToken) { this.accessKeyId = accessKeyId; this.secretAccessKey = secretAccessKey; - this.token = token; + this.sessionToken = sessionToken; } - String getAccessKeyId() { + /** + * Gets the AWS access key id. + * @return the AWS access key id. + */ + public String getAccessKeyId() { return accessKeyId; } - String getSecretAccessKey() { + /** + * Gets the AWS secret access key. + * @return the AWS secret access key. + */ + public String getSecretAccessKey() { return secretAccessKey; } + /** + * Gets the AWS session token. + * @return the AWS session token. + */ @Nullable - String getToken() { - return token; + public String getSessionToken() { + return sessionToken; } } diff --git a/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java index d4bb90c0d..c56c8bdae 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java @@ -723,7 +723,7 @@ public void getAwsSecurityCredentials_fromEnvironmentVariablesNoToken() throws I assertEquals("awsAccessKeyId", credentials.getAccessKeyId()); assertEquals("awsSecretAccessKey", credentials.getSecretAccessKey()); - assertNull(credentials.getToken()); + assertNull(credentials.getSessionToken()); } @Test @@ -756,7 +756,7 @@ public void getAwsSecurityCredentials_fromEnvironmentVariablesWithToken() throws assertEquals("awsAccessKeyId", credentials.getAccessKeyId()); assertEquals("awsSecretAccessKey", credentials.getSecretAccessKey()); - assertEquals("awsSessionToken", credentials.getToken()); + assertEquals("awsSessionToken", credentials.getSessionToken()); } @Test @@ -778,7 +778,7 @@ public void getAwsSecurityCredentials_fromEnvironmentVariables_noMetadataServerC assertEquals("awsAccessKeyId", credentials.getAccessKeyId()); assertEquals("awsSecretAccessKey", credentials.getSecretAccessKey()); - assertEquals("awsSessionToken", credentials.getToken()); + assertEquals("awsSessionToken", credentials.getSessionToken()); } @Test @@ -797,7 +797,7 @@ public void getAwsSecurityCredentials_fromMetadataServer() throws IOException { assertEquals("accessKeyId", credentials.getAccessKeyId()); assertEquals("secretAccessKey", credentials.getSecretAccessKey()); - assertEquals("token", credentials.getToken()); + assertEquals("token", credentials.getSessionToken()); List requests = transportFactory.transport.getRequests(); assertEquals(2, requests.size()); From 9835df71fd98955292ad62e8e2acbc397f0aa0e9 Mon Sep 17 00:00:00 2001 From: aeitzman Date: Mon, 22 Jan 2024 09:43:29 -0800 Subject: [PATCH 30/32] lint --- .../java/com/google/auth/oauth2/AwsRequestSigner.java | 3 ++- .../java/com/google/auth/oauth2/AwsSecurityCredentials.java | 6 +++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/oauth2_http/java/com/google/auth/oauth2/AwsRequestSigner.java b/oauth2_http/java/com/google/auth/oauth2/AwsRequestSigner.java index baf855124..275c15105 100644 --- a/oauth2_http/java/com/google/auth/oauth2/AwsRequestSigner.java +++ b/oauth2_http/java/com/google/auth/oauth2/AwsRequestSigner.java @@ -237,7 +237,8 @@ private Map getCanonicalHeaders(String defaultDate) { headers.put("x-amz-date", defaultDate); } - if (awsSecurityCredentials.getSessionToken() != null && !awsSecurityCredentials.getSessionToken().isEmpty()) { + if (awsSecurityCredentials.getSessionToken() != null + && !awsSecurityCredentials.getSessionToken().isEmpty()) { headers.put("x-amz-security-token", awsSecurityCredentials.getSessionToken()); } diff --git a/oauth2_http/java/com/google/auth/oauth2/AwsSecurityCredentials.java b/oauth2_http/java/com/google/auth/oauth2/AwsSecurityCredentials.java index ddbaf338f..7101dda3e 100644 --- a/oauth2_http/java/com/google/auth/oauth2/AwsSecurityCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/AwsSecurityCredentials.java @@ -51,7 +51,8 @@ public class AwsSecurityCredentials { * @param secretAccessKey the AWS secret access key. * @param sessionToken the AWS session token. Optional. */ - public AwsSecurityCredentials(String accessKeyId, String secretAccessKey, @Nullable String sessionToken) { + public AwsSecurityCredentials( + String accessKeyId, String secretAccessKey, @Nullable String sessionToken) { this.accessKeyId = accessKeyId; this.secretAccessKey = secretAccessKey; this.sessionToken = sessionToken; @@ -59,6 +60,7 @@ public AwsSecurityCredentials(String accessKeyId, String secretAccessKey, @Nulla /** * Gets the AWS access key id. + * * @return the AWS access key id. */ public String getAccessKeyId() { @@ -67,6 +69,7 @@ public String getAccessKeyId() { /** * Gets the AWS secret access key. + * * @return the AWS secret access key. */ public String getSecretAccessKey() { @@ -75,6 +78,7 @@ public String getSecretAccessKey() { /** * Gets the AWS session token. + * * @return the AWS session token. */ @Nullable From 4371463d7adf6e62927d90ae153c9266be4a67a0 Mon Sep 17 00:00:00 2001 From: aeitzman Date: Thu, 25 Jan 2024 09:25:34 -0800 Subject: [PATCH 31/32] fix tests --- .../google/auth/oauth2/PluggableAuthCredentialsTest.java | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/oauth2_http/javatests/com/google/auth/oauth2/PluggableAuthCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/PluggableAuthCredentialsTest.java index 9c1b75c1b..d46660da3 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/PluggableAuthCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/PluggableAuthCredentialsTest.java @@ -395,11 +395,10 @@ public void pluggableAuthCredentialSource_missingExecutableCommandField_throws() public void builder_allFields() throws IOException { List scopes = Arrays.asList("scope1", "scope2"); - CredentialSource source = buildCredentialSource(); + PluggableAuthCredentialSource source = buildCredentialSource(); ExecutableHandler handler = options -> "Token"; PluggableAuthCredentials credentials = - (PluggableAuthCredentials) PluggableAuthCredentials.newBuilder() .setExecutableHandler(handler) .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) @@ -475,11 +474,10 @@ public void builder_missingUniverseDomain_defaults() throws IOException { public void newBuilder_allFields() throws IOException { List scopes = Arrays.asList("scope1", "scope2"); - CredentialSource source = buildCredentialSource(); + PluggableAuthCredentialSource source = buildCredentialSource(); ExecutableHandler handler = options -> "Token"; PluggableAuthCredentials credentials = - (PluggableAuthCredentials) PluggableAuthCredentials.newBuilder() .setExecutableHandler(handler) .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) @@ -518,11 +516,10 @@ public void newBuilder_allFields() throws IOException { public void newBuilder_noUniverseDomain_defaults() throws IOException { List scopes = Arrays.asList("scope1", "scope2"); - CredentialSource source = buildCredentialSource(); + PluggableAuthCredentialSource source = buildCredentialSource(); ExecutableHandler handler = options -> "Token"; PluggableAuthCredentials credentials = - (PluggableAuthCredentials) PluggableAuthCredentials.newBuilder() .setExecutableHandler(handler) .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) From 5b21010eba5bda851f8f66344caf4c62da942653 Mon Sep 17 00:00:00 2001 From: aeitzman Date: Thu, 25 Jan 2024 09:31:25 -0800 Subject: [PATCH 32/32] lint --- .../auth/oauth2/AwsCredentialsTest.java | 88 ++++++------- .../oauth2/IdentityPoolCredentialsTest.java | 116 +++++++++--------- .../oauth2/PluggableAuthCredentialsTest.java | 88 ++++++------- 3 files changed, 146 insertions(+), 146 deletions(-) diff --git a/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java index 4b2651879..aea3f2906 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java @@ -997,21 +997,21 @@ public void builder_allFields() throws IOException { List scopes = Arrays.asList("scope1", "scope2"); AwsCredentials credentials = - AwsCredentials.newBuilder() - .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) - .setAudience("audience") - .setSubjectTokenType("subjectTokenType") - .setTokenUrl(STS_URL) - .setTokenInfoUrl("tokenInfoUrl") - .setCredentialSource(AWS_CREDENTIAL_SOURCE) - .setTokenInfoUrl("tokenInfoUrl") - .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) - .setQuotaProjectId("quotaProjectId") - .setClientId("clientId") - .setClientSecret("clientSecret") - .setScopes(scopes) - .setUniverseDomain("universeDomain") - .build(); + AwsCredentials.newBuilder() + .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) + .setAudience("audience") + .setSubjectTokenType("subjectTokenType") + .setTokenUrl(STS_URL) + .setTokenInfoUrl("tokenInfoUrl") + .setCredentialSource(AWS_CREDENTIAL_SOURCE) + .setTokenInfoUrl("tokenInfoUrl") + .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) + .setQuotaProjectId("quotaProjectId") + .setClientId("clientId") + .setClientSecret("clientSecret") + .setScopes(scopes) + .setUniverseDomain("universeDomain") + .build(); assertEquals("audience", credentials.getAudience()); assertEquals("subjectTokenType", credentials.getSubjectTokenType()); @@ -1070,21 +1070,21 @@ public void newBuilder_allFields() throws IOException { List scopes = Arrays.asList("scope1", "scope2"); AwsCredentials credentials = - AwsCredentials.newBuilder() - .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) - .setAudience("audience") - .setSubjectTokenType("subjectTokenType") - .setTokenUrl(STS_URL) - .setTokenInfoUrl("tokenInfoUrl") - .setCredentialSource(AWS_CREDENTIAL_SOURCE) - .setTokenInfoUrl("tokenInfoUrl") - .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) - .setQuotaProjectId("quotaProjectId") - .setClientId("clientId") - .setClientSecret("clientSecret") - .setScopes(scopes) - .setUniverseDomain("universeDomain") - .build(); + AwsCredentials.newBuilder() + .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) + .setAudience("audience") + .setSubjectTokenType("subjectTokenType") + .setTokenUrl(STS_URL) + .setTokenInfoUrl("tokenInfoUrl") + .setCredentialSource(AWS_CREDENTIAL_SOURCE) + .setTokenInfoUrl("tokenInfoUrl") + .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) + .setQuotaProjectId("quotaProjectId") + .setClientId("clientId") + .setClientSecret("clientSecret") + .setScopes(scopes) + .setUniverseDomain("universeDomain") + .build(); AwsCredentials newBuilderCreds = AwsCredentials.newBuilder(credentials).build(); assertEquals(credentials.getAudience(), newBuilderCreds.getAudience()); @@ -1108,20 +1108,20 @@ public void newBuilder_noUniverseDomain_defaults() throws IOException { List scopes = Arrays.asList("scope1", "scope2"); AwsCredentials credentials = - AwsCredentials.newBuilder() - .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) - .setAudience("audience") - .setSubjectTokenType("subjectTokenType") - .setTokenUrl(STS_URL) - .setTokenInfoUrl("tokenInfoUrl") - .setCredentialSource(AWS_CREDENTIAL_SOURCE) - .setTokenInfoUrl("tokenInfoUrl") - .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) - .setQuotaProjectId("quotaProjectId") - .setClientId("clientId") - .setClientSecret("clientSecret") - .setScopes(scopes) - .build(); + AwsCredentials.newBuilder() + .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) + .setAudience("audience") + .setSubjectTokenType("subjectTokenType") + .setTokenUrl(STS_URL) + .setTokenInfoUrl("tokenInfoUrl") + .setCredentialSource(AWS_CREDENTIAL_SOURCE) + .setTokenInfoUrl("tokenInfoUrl") + .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) + .setQuotaProjectId("quotaProjectId") + .setClientId("clientId") + .setClientSecret("clientSecret") + .setScopes(scopes) + .build(); AwsCredentials newBuilderCreds = AwsCredentials.newBuilder(credentials).build(); assertEquals(credentials.getAudience(), newBuilderCreds.getAudience()); diff --git a/oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java index 180e5ec0a..0d88bbc92 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java @@ -758,20 +758,20 @@ public void builder_allFields() throws IOException { List scopes = Arrays.asList("scope1", "scope2"); IdentityPoolCredentials credentials = - IdentityPoolCredentials.newBuilder() - .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) - .setAudience("audience") - .setSubjectTokenType("subjectTokenType") - .setTokenUrl(STS_URL) - .setTokenInfoUrl("tokenInfoUrl") - .setCredentialSource(FILE_CREDENTIAL_SOURCE) - .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) - .setQuotaProjectId("quotaProjectId") - .setClientId("clientId") - .setClientSecret("clientSecret") - .setScopes(scopes) - .setUniverseDomain("universeDomain") - .build(); + IdentityPoolCredentials.newBuilder() + .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) + .setAudience("audience") + .setSubjectTokenType("subjectTokenType") + .setTokenUrl(STS_URL) + .setTokenInfoUrl("tokenInfoUrl") + .setCredentialSource(FILE_CREDENTIAL_SOURCE) + .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) + .setQuotaProjectId("quotaProjectId") + .setClientId("clientId") + .setClientSecret("clientSecret") + .setScopes(scopes) + .setUniverseDomain("universeDomain") + .build(); assertEquals("audience", credentials.getAudience()); assertEquals("subjectTokenType", credentials.getSubjectTokenType()); @@ -906,19 +906,19 @@ public void builder_missingUniverseDomain_defaults() throws IOException { List scopes = Arrays.asList("scope1", "scope2"); IdentityPoolCredentials credentials = - IdentityPoolCredentials.newBuilder() - .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) - .setAudience("audience") - .setSubjectTokenType("subjectTokenType") - .setTokenUrl(STS_URL) - .setTokenInfoUrl("tokenInfoUrl") - .setCredentialSource(FILE_CREDENTIAL_SOURCE) - .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) - .setQuotaProjectId("quotaProjectId") - .setClientId("clientId") - .setClientSecret("clientSecret") - .setScopes(scopes) - .build(); + IdentityPoolCredentials.newBuilder() + .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) + .setAudience("audience") + .setSubjectTokenType("subjectTokenType") + .setTokenUrl(STS_URL) + .setTokenInfoUrl("tokenInfoUrl") + .setCredentialSource(FILE_CREDENTIAL_SOURCE) + .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) + .setQuotaProjectId("quotaProjectId") + .setClientId("clientId") + .setClientSecret("clientSecret") + .setScopes(scopes) + .build(); assertEquals("audience", credentials.getAudience()); assertEquals("subjectTokenType", credentials.getSubjectTokenType()); @@ -940,22 +940,22 @@ public void newBuilder_allFields() throws IOException { List scopes = Arrays.asList("scope1", "scope2"); IdentityPoolCredentials credentials = - IdentityPoolCredentials.newBuilder() - .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) - .setAudience( - "//iam.googleapis.com/locations/global/workforcePools/pool/providers/provider") - .setSubjectTokenType("subjectTokenType") - .setTokenUrl(STS_URL) - .setTokenInfoUrl("tokenInfoUrl") - .setCredentialSource(FILE_CREDENTIAL_SOURCE) - .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) - .setQuotaProjectId("quotaProjectId") - .setClientId("clientId") - .setClientSecret("clientSecret") - .setScopes(scopes) - .setWorkforcePoolUserProject("workforcePoolUserProject") - .setUniverseDomain("universeDomain") - .build(); + IdentityPoolCredentials.newBuilder() + .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) + .setAudience( + "//iam.googleapis.com/locations/global/workforcePools/pool/providers/provider") + .setSubjectTokenType("subjectTokenType") + .setTokenUrl(STS_URL) + .setTokenInfoUrl("tokenInfoUrl") + .setCredentialSource(FILE_CREDENTIAL_SOURCE) + .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) + .setQuotaProjectId("quotaProjectId") + .setClientId("clientId") + .setClientSecret("clientSecret") + .setScopes(scopes) + .setWorkforcePoolUserProject("workforcePoolUserProject") + .setUniverseDomain("universeDomain") + .build(); IdentityPoolCredentials newBuilderCreds = IdentityPoolCredentials.newBuilder(credentials).build(); @@ -982,21 +982,21 @@ public void newBuilder_noUniverseDomain_defaults() throws IOException { List scopes = Arrays.asList("scope1", "scope2"); IdentityPoolCredentials credentials = - IdentityPoolCredentials.newBuilder() - .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) - .setAudience( - "//iam.googleapis.com/locations/global/workforcePools/pool/providers/provider") - .setSubjectTokenType("subjectTokenType") - .setTokenUrl(STS_URL) - .setTokenInfoUrl("tokenInfoUrl") - .setCredentialSource(FILE_CREDENTIAL_SOURCE) - .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) - .setQuotaProjectId("quotaProjectId") - .setClientId("clientId") - .setClientSecret("clientSecret") - .setScopes(scopes) - .setWorkforcePoolUserProject("workforcePoolUserProject") - .build(); + IdentityPoolCredentials.newBuilder() + .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) + .setAudience( + "//iam.googleapis.com/locations/global/workforcePools/pool/providers/provider") + .setSubjectTokenType("subjectTokenType") + .setTokenUrl(STS_URL) + .setTokenInfoUrl("tokenInfoUrl") + .setCredentialSource(FILE_CREDENTIAL_SOURCE) + .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) + .setQuotaProjectId("quotaProjectId") + .setClientId("clientId") + .setClientSecret("clientSecret") + .setScopes(scopes) + .setWorkforcePoolUserProject("workforcePoolUserProject") + .build(); IdentityPoolCredentials newBuilderCreds = IdentityPoolCredentials.newBuilder(credentials).build(); diff --git a/oauth2_http/javatests/com/google/auth/oauth2/PluggableAuthCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/PluggableAuthCredentialsTest.java index d46660da3..003a393d7 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/PluggableAuthCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/PluggableAuthCredentialsTest.java @@ -399,21 +399,21 @@ public void builder_allFields() throws IOException { ExecutableHandler handler = options -> "Token"; PluggableAuthCredentials credentials = - PluggableAuthCredentials.newBuilder() - .setExecutableHandler(handler) - .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) - .setAudience("audience") - .setSubjectTokenType("subjectTokenType") - .setTokenUrl(STS_URL) - .setTokenInfoUrl("tokenInfoUrl") - .setCredentialSource(source) - .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) - .setQuotaProjectId("quotaProjectId") - .setClientId("clientId") - .setClientSecret("clientSecret") - .setScopes(scopes) - .setUniverseDomain("universeDomain") - .build(); + PluggableAuthCredentials.newBuilder() + .setExecutableHandler(handler) + .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) + .setAudience("audience") + .setSubjectTokenType("subjectTokenType") + .setTokenUrl(STS_URL) + .setTokenInfoUrl("tokenInfoUrl") + .setCredentialSource(source) + .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) + .setQuotaProjectId("quotaProjectId") + .setClientId("clientId") + .setClientSecret("clientSecret") + .setScopes(scopes) + .setUniverseDomain("universeDomain") + .build(); assertEquals(handler, credentials.getExecutableHandler()); assertEquals("audience", credentials.getAudience()); @@ -478,21 +478,21 @@ public void newBuilder_allFields() throws IOException { ExecutableHandler handler = options -> "Token"; PluggableAuthCredentials credentials = - PluggableAuthCredentials.newBuilder() - .setExecutableHandler(handler) - .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) - .setAudience("audience") - .setSubjectTokenType("subjectTokenType") - .setTokenUrl(STS_URL) - .setTokenInfoUrl("tokenInfoUrl") - .setCredentialSource(source) - .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) - .setQuotaProjectId("quotaProjectId") - .setClientId("clientId") - .setClientSecret("clientSecret") - .setScopes(scopes) - .setUniverseDomain("universeDomain") - .build(); + PluggableAuthCredentials.newBuilder() + .setExecutableHandler(handler) + .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) + .setAudience("audience") + .setSubjectTokenType("subjectTokenType") + .setTokenUrl(STS_URL) + .setTokenInfoUrl("tokenInfoUrl") + .setCredentialSource(source) + .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) + .setQuotaProjectId("quotaProjectId") + .setClientId("clientId") + .setClientSecret("clientSecret") + .setScopes(scopes) + .setUniverseDomain("universeDomain") + .build(); PluggableAuthCredentials newBuilderCreds = PluggableAuthCredentials.newBuilder(credentials).build(); @@ -520,20 +520,20 @@ public void newBuilder_noUniverseDomain_defaults() throws IOException { ExecutableHandler handler = options -> "Token"; PluggableAuthCredentials credentials = - PluggableAuthCredentials.newBuilder() - .setExecutableHandler(handler) - .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) - .setAudience("audience") - .setSubjectTokenType("subjectTokenType") - .setTokenUrl(STS_URL) - .setTokenInfoUrl("tokenInfoUrl") - .setCredentialSource(source) - .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) - .setQuotaProjectId("quotaProjectId") - .setClientId("clientId") - .setClientSecret("clientSecret") - .setScopes(scopes) - .build(); + PluggableAuthCredentials.newBuilder() + .setExecutableHandler(handler) + .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) + .setAudience("audience") + .setSubjectTokenType("subjectTokenType") + .setTokenUrl(STS_URL) + .setTokenInfoUrl("tokenInfoUrl") + .setCredentialSource(source) + .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) + .setQuotaProjectId("quotaProjectId") + .setClientId("clientId") + .setClientSecret("clientSecret") + .setScopes(scopes) + .build(); PluggableAuthCredentials newBuilderCreds = PluggableAuthCredentials.newBuilder(credentials).build();