Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -73,6 +74,7 @@ class InternalAwsSecurityCredentialsSupplier implements AwsSecurityCredentialsSu
private final AwsCredentialSource awsCredentialSource;
private EnvironmentProvider environmentProvider;
private transient HttpTransportFactory transportFactory;
private final String transportFactoryClassName;

/**
* Constructor for InternalAwsSecurityCredentialsProvider
Expand All @@ -83,11 +85,25 @@ class InternalAwsSecurityCredentialsSupplier implements AwsSecurityCredentialsSu
*/
InternalAwsSecurityCredentialsSupplier(
AwsCredentialSource awsCredentialSource,
EnvironmentProvider environmentProvider,
HttpTransportFactory transportFactory) {
this.environmentProvider = environmentProvider;
@Nullable EnvironmentProvider environmentProvider,
@Nullable HttpTransportFactory transportFactory) {
this.environmentProvider =
environmentProvider == null ? SystemEnvironmentProvider.getInstance() : environmentProvider;
this.awsCredentialSource = awsCredentialSource;
this.transportFactory = transportFactory;
this.transportFactory =
transportFactory != null ? transportFactory : OAuth2Utils.HTTP_TRANSPORT_FACTORY;
this.transportFactoryClassName = this.transportFactory.getClass().getName();
}

@SuppressWarnings("unused")
private void readObject(ObjectInputStream input) throws IOException, ClassNotFoundException {
input.defaultReadObject();
transportFactory = OAuth2Credentials.newInstance(transportFactoryClassName);
}

@VisibleForTesting
HttpTransportFactory getTransportFactory() {
return transportFactory;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1248,6 +1248,36 @@ void serialize() throws IOException, ClassNotFoundException {
assertSame(Clock.SYSTEM, deserializedCredentials.clock);
}

/**
* Verifies that {@link AwsCredentials} can successfully refresh access tokens after being
* serialized and deserialized.
*/
@Test
void serialize_refreshAccessToken_success() throws IOException, ClassNotFoundException {
// Uses an in-memory MockHttpTransport (no network calls) that returns canned HTTP responses
// for both AWS IMDS metadata endpoints and the GCP STS token exchange endpoint.
MockExternalAccountCredentialsTransportFactory transportFactory =
new MockExternalAccountCredentialsTransportFactory();

// Use an IMDS credential source so that token refresh is forced to retrieve AWS credentials
// and region from the metadata server via HTTP, exercising the supplier's transportFactory.
AwsCredentials awsCredential =
AwsCredentials.newBuilder(AWS_CREDENTIAL)
.setTokenUrl(transportFactory.transport.getStsUrl())
.setHttpTransportFactory(transportFactory)
.setCredentialSource(buildAwsCredentialSource(transportFactory))
.build();

AwsCredentials deserialized = serializeAndDeserialize(awsCredential);

// refreshAccessToken() calls getCredentials(), getRegion(), and the STS token endpoint,
// verifying that the restored transportFactory is used for all HTTP requests.
AccessToken accessToken = deserialized.refreshAccessToken();

// Verifies the access token returned from the simulated STS exchange matches the mock.
assertEquals(transportFactory.transport.getAccessToken(), accessToken.getTokenValue());
}

private static void ValidateRequest(
MockLowLevelHttpRequest request, String expectedUrl, Map<String, String> expectedHeaders) {
assertEquals(expectedUrl, request.getUrl());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@
package com.google.auth.oauth2;

import static com.google.auth.oauth2.AwsCredentialsTest.buildAwsImdsv2CredentialSource;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertTrue;

import com.google.auth.oauth2.ExternalAccountCredentialsTest.MockExternalAccountCredentialsTransportFactory;
Expand All @@ -41,7 +44,7 @@
import org.junit.jupiter.api.Test;

/** Tests for {@link InternalAwsSecurityCredentialsSupplier}. */
class InternalAwsSecurityCredentialsSupplierTest {
class InternalAwsSecurityCredentialsSupplierTest extends BaseSerializationTest {
@Test
void shouldUseMetadataServer_withRequiredEnvironmentVariables() {
MockExternalAccountCredentialsTransportFactory transportFactory =
Expand Down Expand Up @@ -159,4 +162,79 @@ void shouldUseMetadataServer_noEnvironmentVars() {
transportFactory);
assertTrue(supplier.shouldUseMetadataServer());
}

/**
* Verifies that {@link InternalAwsSecurityCredentialsSupplier} restores its {@code
* transportFactory} upon deserialization, enabling successful retrieval of AWS security
* credentials and region from the AWS EC2 metadata server.
*/
@Test
void serializeAndDeserialize_retrievesCredentialsAndRegionSuccessfully() throws Exception {
MockExternalAccountCredentialsTransportFactory transportFactory =
new MockExternalAccountCredentialsTransportFactory();
InternalAwsSecurityCredentialsSupplier supplier =
new InternalAwsSecurityCredentialsSupplier(
buildAwsImdsv2CredentialSource(transportFactory),
// Pass null to use the default SystemEnvironmentProvider, which implements Serializable
// (unlike TestEnvironmentProvider).
/* environmentProvider= */ null,
transportFactory);

InternalAwsSecurityCredentialsSupplier deserialized = serializeAndDeserialize(supplier);
assertEquals(
MockExternalAccountCredentialsTransportFactory.class,
deserialized.getTransportFactory().getClass());

// Credentials and region are not serialized fields; they are retrieved on demand via HTTP.
// Calling getCredentials() and getRegion() verifies that the restored transportFactory
// successfully constructs and executes HTTP requests against the mock metadata server
// (rather than failing with a NullPointerException).
AwsSecurityCredentials credentials = deserialized.getCredentials(null);
assertNotNull(credentials);
assertEquals("accessKeyId", credentials.getAccessKeyId());
assertEquals("secretAccessKey", credentials.getSecretAccessKey());
assertEquals("token", credentials.getSessionToken());

String region = deserialized.getRegion(null);
assertEquals("us-east-1", region);
}

/**
* Verifies that {@link InternalAwsSecurityCredentialsSupplier} deserializes cleanly and falls
* back to the default {@link OAuth2Utils#HTTP_TRANSPORT_FACTORY} when no custom transport factory
* was provided.
*/
@Test
void serializeAndDeserialize_defaultTransportFactory_success() throws Exception {
MockExternalAccountCredentialsTransportFactory transportFactory =
new MockExternalAccountCredentialsTransportFactory();
InternalAwsSecurityCredentialsSupplier supplier =
new InternalAwsSecurityCredentialsSupplier(
buildAwsImdsv2CredentialSource(transportFactory),
/* environmentProvider= */ null,
/* transportFactory= */ null);

InternalAwsSecurityCredentialsSupplier deserialized = serializeAndDeserialize(supplier);
assertNotNull(deserialized);
assertSame(OAuth2Utils.HTTP_TRANSPORT_FACTORY, deserialized.getTransportFactory());
}

/**
* Verifies that {@link InternalAwsSecurityCredentialsSupplier} can be serialized and deserialized
* when an explicit {@link EnvironmentProvider} is provided.
*/
@Test
void serializeAndDeserialize_withEnvironmentVariables_success() throws Exception {
MockExternalAccountCredentialsTransportFactory transportFactory =
new MockExternalAccountCredentialsTransportFactory();
SystemEnvironmentProvider environmentProvider = SystemEnvironmentProvider.getInstance();
InternalAwsSecurityCredentialsSupplier supplier =
new InternalAwsSecurityCredentialsSupplier(
buildAwsImdsv2CredentialSource(transportFactory),
environmentProvider,
transportFactory);

InternalAwsSecurityCredentialsSupplier deserialized = serializeAndDeserialize(supplier);
assertNotNull(deserialized);
}
}
Loading