diff --git a/.changes/next-release/feature-AWSSDKforJavav2-c2d8ec6.json b/.changes/next-release/feature-AWSSDKforJavav2-c2d8ec6.json
new file mode 100644
index 000000000000..3ddf00d7bf2e
--- /dev/null
+++ b/.changes/next-release/feature-AWSSDKforJavav2-c2d8ec6.json
@@ -0,0 +1,6 @@
+{
+ "type": "feature",
+ "category": "AWS SDK for Java v2",
+ "contributor": "",
+ "description": "Adds business metrics tracking for credential providers and S3_Express_Bucket."
+}
diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/ChildProfileCredentialsProviderFactory.java b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/ChildProfileCredentialsProviderFactory.java
index 620e32decfe2..8c7fd93c4816 100644
--- a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/ChildProfileCredentialsProviderFactory.java
+++ b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/ChildProfileCredentialsProviderFactory.java
@@ -40,5 +40,45 @@ public interface ChildProfileCredentialsProviderFactory {
* provider.
* @return The credentials provider with permissions derived from the source credentials provider and profile.
*/
- AwsCredentialsProvider create(AwsCredentialsProvider sourceCredentialsProvider, Profile profile);
+ default AwsCredentialsProvider create(AwsCredentialsProvider sourceCredentialsProvider, Profile profile) {
+ ChildProfileCredentialsRequest request = new ChildProfileCredentialsRequest(sourceCredentialsProvider, profile, null);
+ return create(request);
+ }
+
+ /**
+ * Create a credentials provider for the provided profile, using the provided source credentials provider to authenticate
+ * with AWS. In the case of STS, the returned credentials provider is for a role that has been assumed, and the provided
+ * source credentials provider is the credentials that should be used to authenticate that the user is allowed to assume
+ * that role.
+ *
+ * @param request The request containing all parameters needed to create the child credentials provider.
+ * @return The credentials provider with permissions derived from the request parameters.
+ */
+ AwsCredentialsProvider create(ChildProfileCredentialsRequest request);
+
+ final class ChildProfileCredentialsRequest {
+ private final AwsCredentialsProvider sourceCredentialsProvider;
+ private final Profile profile;
+ private final String sourceFeatureId;
+
+ public ChildProfileCredentialsRequest(AwsCredentialsProvider sourceCredentialsProvider,
+ Profile profile,
+ String sourceFeatureId) {
+ this.sourceCredentialsProvider = sourceCredentialsProvider;
+ this.profile = profile;
+ this.sourceFeatureId = sourceFeatureId;
+ }
+
+ public AwsCredentialsProvider sourceCredentialsProvider() {
+ return sourceCredentialsProvider;
+ }
+
+ public Profile profile() {
+ return profile;
+ }
+
+ public String sourceFeatureId() {
+ return sourceFeatureId;
+ }
+ }
}
diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/ContainerCredentialsProvider.java b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/ContainerCredentialsProvider.java
index efec7ffce6bd..03ed0a2441b1 100644
--- a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/ContainerCredentialsProvider.java
+++ b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/ContainerCredentialsProvider.java
@@ -39,6 +39,7 @@
import software.amazon.awssdk.auth.credentials.internal.HttpCredentialsLoader.LoadedCredentials;
import software.amazon.awssdk.core.SdkSystemSetting;
import software.amazon.awssdk.core.exception.SdkClientException;
+import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId;
import software.amazon.awssdk.core.util.SdkUserAgent;
import software.amazon.awssdk.regions.util.ResourcesEndpointProvider;
import software.amazon.awssdk.regions.util.ResourcesEndpointRetryPolicy;
@@ -72,7 +73,8 @@
public final class ContainerCredentialsProvider
implements HttpCredentialsProvider,
ToCopyableBuilder {
- private static final String PROVIDER_NAME = "ContainerCredentialsProvider";
+ private static final String CLASS_NAME = "ContainerCredentialsProvider";
+ private static final String PROVIDER_NAME = BusinessMetricFeatureId.CREDENTIALS_HTTP.value();
private static final Predicate IS_LOOPBACK_ADDRESS = InetAddress::isLoopbackAddress;
private static final Predicate ALLOWED_HOSTS_RULES = IS_LOOPBACK_ADDRESS;
private static final String HTTPS = "https";
@@ -90,6 +92,8 @@ public final class ContainerCredentialsProvider
private final Boolean asyncCredentialUpdateEnabled;
private final String asyncThreadName;
+ private final String sourceFeatureId;
+ private final String providerName;
/**
* @see #builder()
@@ -98,7 +102,11 @@ private ContainerCredentialsProvider(BuilderImpl builder) {
this.endpoint = builder.endpoint;
this.asyncCredentialUpdateEnabled = builder.asyncCredentialUpdateEnabled;
this.asyncThreadName = builder.asyncThreadName;
- this.httpCredentialsLoader = HttpCredentialsLoader.create(PROVIDER_NAME);
+ this.sourceFeatureId = builder.sourceFeatureId;
+ this.providerName = StringUtils.isEmpty(builder.sourceFeatureId)
+ ? PROVIDER_NAME
+ : builder.sourceFeatureId + "," + PROVIDER_NAME;
+ this.httpCredentialsLoader = HttpCredentialsLoader.create(providerName());
if (Boolean.TRUE.equals(builder.asyncCredentialUpdateEnabled)) {
Validate.paramNotBlank(builder.asyncThreadName, "asyncThreadName");
@@ -126,7 +134,7 @@ public static Builder builder() {
@Override
public String toString() {
- return ToString.create(PROVIDER_NAME);
+ return ToString.create(CLASS_NAME);
}
private RefreshResult refreshCredentials() {
@@ -160,6 +168,10 @@ private Instant prefetchTime(Instant expiration) {
return ComparableUtils.minimum(oneHourFromNow, fifteenMinutesBeforeExpiration);
}
+ private String providerName() {
+ return this.providerName;
+ }
+
@Override
public AwsCredentials resolveCredentials() {
return credentialsCache.get();
@@ -318,6 +330,7 @@ private static final class BuilderImpl implements Builder {
private String endpoint;
private Boolean asyncCredentialUpdateEnabled;
private String asyncThreadName;
+ private String sourceFeatureId;
private BuilderImpl() {
asyncThreadName("container-credentials-provider");
@@ -327,6 +340,7 @@ private BuilderImpl(ContainerCredentialsProvider credentialsProvider) {
this.endpoint = credentialsProvider.endpoint;
this.asyncCredentialUpdateEnabled = credentialsProvider.asyncCredentialUpdateEnabled;
this.asyncThreadName = credentialsProvider.asyncThreadName;
+ this.sourceFeatureId = credentialsProvider.sourceFeatureId;
}
@Override
@@ -359,6 +373,16 @@ public void setAsyncThreadName(String asyncThreadName) {
asyncThreadName(asyncThreadName);
}
+ @Override
+ public Builder sourceFeatureId(String sourceFeatureId) {
+ this.sourceFeatureId = sourceFeatureId;
+ return this;
+ }
+
+ public void setSourceFeatureId(String sourceFeatureId) {
+ sourceFeatureId(sourceFeatureId);
+ }
+
@Override
public ContainerCredentialsProvider build() {
return new ContainerCredentialsProvider(this);
diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/EnvironmentVariableCredentialsProvider.java b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/EnvironmentVariableCredentialsProvider.java
index e05c24eed05a..f7eb0df32e6b 100644
--- a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/EnvironmentVariableCredentialsProvider.java
+++ b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/EnvironmentVariableCredentialsProvider.java
@@ -18,6 +18,7 @@
import java.util.Optional;
import software.amazon.awssdk.annotations.SdkPublicApi;
import software.amazon.awssdk.auth.credentials.internal.SystemSettingsCredentialsProvider;
+import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId;
import software.amazon.awssdk.utils.SystemSetting;
import software.amazon.awssdk.utils.ToString;
@@ -28,7 +29,8 @@
@SdkPublicApi
public final class EnvironmentVariableCredentialsProvider extends SystemSettingsCredentialsProvider {
- private static final String PROVIDER_NAME = "EnvironmentVariableCredentialsProvider";
+ private static final String CLASS_NAME = "EnvironmentVariableCredentialsProvider";
+ private static final String PROVIDER_NAME = BusinessMetricFeatureId.CREDENTIALS_ENV_VARS.value();
private EnvironmentVariableCredentialsProvider() {
}
@@ -52,6 +54,6 @@ protected String provider() {
@Override
public String toString() {
- return ToString.create(PROVIDER_NAME);
+ return ToString.create(CLASS_NAME);
}
}
diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/HttpCredentialsProvider.java b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/HttpCredentialsProvider.java
index ccc7e7aa7101..4138639cbca1 100644
--- a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/HttpCredentialsProvider.java
+++ b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/HttpCredentialsProvider.java
@@ -16,6 +16,7 @@
package software.amazon.awssdk.auth.credentials;
import software.amazon.awssdk.annotations.SdkPublicApi;
+import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId;
import software.amazon.awssdk.utils.SdkAutoCloseable;
/**
@@ -48,6 +49,16 @@ interface BuilderNote: This method is primarily intended for use by AWS SDK internal components
+ * and should not be used directly by external users.
+ */
+ default BuilderT sourceFeatureId(String sourceFeatureId) {
+ throw new UnsupportedOperationException();
+ }
+
/**
* Build the credentials provider based on the configuration on this builder.
*/
diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/InstanceProfileCredentialsProvider.java b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/InstanceProfileCredentialsProvider.java
index b1ddc5d7faef..34f2a189cb34 100644
--- a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/InstanceProfileCredentialsProvider.java
+++ b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/InstanceProfileCredentialsProvider.java
@@ -37,6 +37,7 @@
import software.amazon.awssdk.core.SdkSystemSetting;
import software.amazon.awssdk.core.exception.SdkClientException;
import software.amazon.awssdk.core.exception.SdkServiceException;
+import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId;
import software.amazon.awssdk.profiles.ProfileFile;
import software.amazon.awssdk.profiles.ProfileFileSupplier;
import software.amazon.awssdk.profiles.ProfileFileSystemSetting;
@@ -44,6 +45,7 @@
import software.amazon.awssdk.regions.util.HttpResourcesUtils;
import software.amazon.awssdk.regions.util.ResourcesEndpointProvider;
import software.amazon.awssdk.utils.Logger;
+import software.amazon.awssdk.utils.StringUtils;
import software.amazon.awssdk.utils.ToString;
import software.amazon.awssdk.utils.Validate;
import software.amazon.awssdk.utils.builder.CopyableBuilder;
@@ -67,7 +69,8 @@ public final class InstanceProfileCredentialsProvider
implements HttpCredentialsProvider,
ToCopyableBuilder {
private static final Logger log = Logger.loggerFor(InstanceProfileCredentialsProvider.class);
- private static final String PROVIDER_NAME = "InstanceProfileCredentialsProvider";
+ private static final String CLASS_NAME = "InstanceProfileCredentialsProvider";
+ private static final String PROVIDER_NAME = BusinessMetricFeatureId.CREDENTIALS_IMDS.value();
private static final String EC2_METADATA_TOKEN_HEADER = "x-aws-ec2-metadata-token";
private static final String SECURITY_CREDENTIALS_RESOURCE = "/latest/meta-data/iam/security-credentials/";
private static final String TOKEN_RESOURCE = "/latest/api/token";
@@ -90,6 +93,9 @@ public final class InstanceProfileCredentialsProvider
private final Duration staleTime;
+ private final String sourceFeatureId;
+ private final String providerName;
+
/**
* @see #builder()
*/
@@ -102,8 +108,12 @@ private InstanceProfileCredentialsProvider(BuilderImpl builder) {
.orElseGet(() -> ProfileFileSupplier.fixedProfileFile(ProfileFile.defaultProfileFile()));
this.profileName = Optional.ofNullable(builder.profileName)
.orElseGet(ProfileFileSystemSetting.AWS_PROFILE::getStringValueOrThrow);
+ this.sourceFeatureId = builder.sourceFeatureId;
+ this.providerName = StringUtils.isEmpty(builder.sourceFeatureId)
+ ? PROVIDER_NAME
+ : builder.sourceFeatureId + "," + PROVIDER_NAME;
- this.httpCredentialsLoader = HttpCredentialsLoader.create(PROVIDER_NAME);
+ this.httpCredentialsLoader = HttpCredentialsLoader.create(providerName());
this.configProvider =
Ec2MetadataConfigProvider.builder()
.profileFile(profileFile)
@@ -202,9 +212,13 @@ public void close() {
credentialsCache.close();
}
+ private String providerName() {
+ return this.providerName;
+ }
+
@Override
public String toString() {
- return ToString.create(PROVIDER_NAME);
+ return ToString.create(CLASS_NAME);
}
private ResourcesEndpointProvider createEndpointProvider() {
@@ -372,6 +386,7 @@ static final class BuilderImpl implements Builder {
private Supplier profileFile;
private String profileName;
private Duration staleTime;
+ private String sourceFeatureId;
private BuilderImpl() {
asyncThreadName("instance-profile-credentials-provider");
@@ -385,6 +400,7 @@ private BuilderImpl(InstanceProfileCredentialsProvider provider) {
this.profileFile = provider.profileFile;
this.profileName = provider.profileName;
this.staleTime = provider.staleTime;
+ this.sourceFeatureId = provider.sourceFeatureId;
}
Builder clock(Clock clock) {
@@ -463,6 +479,16 @@ public void setStaleTime(Duration duration) {
staleTime(duration);
}
+ @Override
+ public Builder sourceFeatureId(String sourceFeatureId) {
+ this.sourceFeatureId = sourceFeatureId;
+ return this;
+ }
+
+ public void setSourceFeatureId(String sourceFeatureId) {
+ sourceFeatureId(sourceFeatureId);
+ }
+
@Override
public InstanceProfileCredentialsProvider build() {
return new InstanceProfileCredentialsProvider(this);
diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/ProcessCredentialsProvider.java b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/ProcessCredentialsProvider.java
index e27d511d0887..840b820e8f30 100644
--- a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/ProcessCredentialsProvider.java
+++ b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/ProcessCredentialsProvider.java
@@ -25,12 +25,14 @@
import java.util.Collections;
import java.util.List;
import software.amazon.awssdk.annotations.SdkPublicApi;
+import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId;
import software.amazon.awssdk.protocols.jsoncore.JsonNode;
import software.amazon.awssdk.protocols.jsoncore.JsonNodeParser;
import software.amazon.awssdk.utils.DateUtils;
import software.amazon.awssdk.utils.IoUtils;
import software.amazon.awssdk.utils.Platform;
import software.amazon.awssdk.utils.SdkAutoCloseable;
+import software.amazon.awssdk.utils.StringUtils;
import software.amazon.awssdk.utils.ToString;
import software.amazon.awssdk.utils.Validate;
import software.amazon.awssdk.utils.builder.CopyableBuilder;
@@ -64,7 +66,8 @@ public final class ProcessCredentialsProvider
implements AwsCredentialsProvider,
SdkAutoCloseable,
ToCopyableBuilder {
- private static final String PROVIDER_NAME = "ProcessCredentialsProvider";
+ private static final String CLASS_NAME = "ProcessCredentialsProvider";
+ private static final String PROVIDER_NAME = BusinessMetricFeatureId.CREDENTIALS_PROCESS.value();
private static final JsonNodeParser PARSER = JsonNodeParser.builder()
.removeErrorLocations(true)
.build();
@@ -82,6 +85,9 @@ public final class ProcessCredentialsProvider
private final Boolean asyncCredentialUpdateEnabled;
+ private final String sourceFeatureId;
+ private final String providerName;
+
/**
* @see #builder()
*/
@@ -93,6 +99,10 @@ private ProcessCredentialsProvider(Builder builder) {
this.commandAsListOfStringsFromBuilder = builder.commandAsListOfStrings;
this.asyncCredentialUpdateEnabled = builder.asyncCredentialUpdateEnabled;
this.staticAccountId = builder.staticAccountId;
+ this.sourceFeatureId = builder.sourceFeatureId;
+ this.providerName = StringUtils.isEmpty(builder.sourceFeatureId)
+ ? PROVIDER_NAME
+ : builder.sourceFeatureId + "," + PROVIDER_NAME;
CachedSupplier.Builder cacheBuilder = CachedSupplier.builder(this::refreshCredentials)
.cachedValueName(toString());
@@ -171,6 +181,10 @@ private JsonNode parseProcessOutput(String processOutput) {
return credentialsJson;
}
+ private String providerName() {
+ return this.providerName;
+ }
+
/**
* Parse the process output to retrieve the credentials.
*/
@@ -192,13 +206,13 @@ private AwsCredentials credentials(JsonNode credentialsJson) {
.sessionToken(sessionToken)
.expirationTime(credentialExpirationTime(credentialsJson))
.accountId(resolvedAccountId)
- .providerName(PROVIDER_NAME)
+ .providerName(providerName())
.build() :
AwsBasicCredentials.builder()
.accessKeyId(accessKeyId)
.secretAccessKey(secretAccessKey)
.accountId(resolvedAccountId)
- .providerName(PROVIDER_NAME)
+ .providerName(providerName())
.build();
}
@@ -270,6 +284,7 @@ public static class Builder implements CopyableBuilder c.providerName(PROVIDER_NAME));
+ AwsBasicCredentials basicCreds = (AwsBasicCredentials) credentials;
+ if (basicCreds.providerName()
+ .map(BusinessMetricFeatureId.CREDENTIALS_PROFILE.value()::equals)
+ .orElse(false)) {
+ return basicCreds;
+ }
+ return basicCreds.copy(c -> c.providerName(PROVIDER_NAME));
}
if (credentials instanceof AwsSessionCredentials) {
- return ((AwsSessionCredentials) credentials).copy(c -> c.providerName(PROVIDER_NAME));
+ AwsSessionCredentials sessionCreds = (AwsSessionCredentials) credentials;
+ if (sessionCreds.providerName()
+ .map(BusinessMetricFeatureId.CREDENTIALS_PROFILE.value()::equals)
+ .orElse(false)) {
+ return sessionCreds;
+ }
+ return sessionCreds.copy(c -> c.providerName(PROVIDER_NAME));
}
return credentials;
}
diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/SystemPropertyCredentialsProvider.java b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/SystemPropertyCredentialsProvider.java
index bcc7d77af4e6..94ff10a0b1c5 100644
--- a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/SystemPropertyCredentialsProvider.java
+++ b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/SystemPropertyCredentialsProvider.java
@@ -18,6 +18,7 @@
import java.util.Optional;
import software.amazon.awssdk.annotations.SdkPublicApi;
import software.amazon.awssdk.auth.credentials.internal.SystemSettingsCredentialsProvider;
+import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId;
import software.amazon.awssdk.utils.SystemSetting;
import software.amazon.awssdk.utils.ToString;
@@ -28,7 +29,8 @@
@SdkPublicApi
public final class SystemPropertyCredentialsProvider extends SystemSettingsCredentialsProvider {
- private static final String PROVIDER_NAME = "SystemPropertyCredentialsProvider";
+ private static final String CLASS_NAME = "SystemPropertyCredentialsProvider";
+ private static final String PROVIDER_NAME = BusinessMetricFeatureId.CREDENTIALS_JVM_SYSTEM_PROPERTIES.value();
private SystemPropertyCredentialsProvider() {
}
@@ -52,6 +54,6 @@ protected String provider() {
@Override
public String toString() {
- return ToString.create(PROVIDER_NAME);
+ return ToString.create(CLASS_NAME);
}
}
diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/WebIdentityTokenFileCredentialsProvider.java b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/WebIdentityTokenFileCredentialsProvider.java
index 6e5f68473809..d93bcb070d97 100644
--- a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/WebIdentityTokenFileCredentialsProvider.java
+++ b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/WebIdentityTokenFileCredentialsProvider.java
@@ -24,6 +24,7 @@
import software.amazon.awssdk.auth.credentials.internal.WebIdentityCredentialsUtils;
import software.amazon.awssdk.auth.credentials.internal.WebIdentityTokenCredentialProperties;
import software.amazon.awssdk.core.SdkSystemSetting;
+import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId;
import software.amazon.awssdk.utils.IoUtils;
import software.amazon.awssdk.utils.SdkAutoCloseable;
import software.amazon.awssdk.utils.ToString;
@@ -108,6 +109,8 @@ private WebIdentityTokenFileCredentialsProvider(BuilderImpl builder) {
.prefetchTime(prefetchTime)
.staleTime(staleTime)
.roleSessionDuration(roleSessionDuration)
+ .sourceFeatureId(BusinessMetricFeatureId
+ .CREDENTIALS_ENV_VARS_STS_WEB_ID_TOKEN.value())
.build();
credentialsProvider = WebIdentityCredentialsUtils.factory().create(credentialProperties);
diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/internal/ProfileCredentialsUtils.java b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/internal/ProfileCredentialsUtils.java
index 22da5e9986fd..317ebae76081 100644
--- a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/internal/ProfileCredentialsUtils.java
+++ b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/internal/ProfileCredentialsUtils.java
@@ -40,6 +40,7 @@
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.auth.credentials.SystemPropertyCredentialsProvider;
import software.amazon.awssdk.core.internal.util.ClassLoaderHelper;
+import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId;
import software.amazon.awssdk.profiles.Profile;
import software.amazon.awssdk.profiles.ProfileFile;
import software.amazon.awssdk.profiles.ProfileProperty;
@@ -161,6 +162,7 @@ private AwsCredentialsProvider basicProfileCredentialsProvider() {
.accessKeyId(properties.get(ProfileProperty.AWS_ACCESS_KEY_ID))
.secretAccessKey(properties.get(ProfileProperty.AWS_SECRET_ACCESS_KEY))
.accountId(properties.get(ProfileProperty.AWS_ACCOUNT_ID))
+ .providerName(BusinessMetricFeatureId.CREDENTIALS_PROFILE.value())
.build();
return StaticCredentialsProvider.create(credentials);
}
@@ -177,6 +179,7 @@ private AwsCredentialsProvider sessionProfileCredentialsProvider() {
.secretAccessKey(properties.get(ProfileProperty.AWS_SECRET_ACCESS_KEY))
.sessionToken(properties.get(ProfileProperty.AWS_SESSION_TOKEN))
.accountId(properties.get(ProfileProperty.AWS_ACCOUNT_ID))
+ .providerName(BusinessMetricFeatureId.CREDENTIALS_PROFILE.value())
.build();
return StaticCredentialsProvider.create(credentials);
}
@@ -187,6 +190,7 @@ private AwsCredentialsProvider credentialProcessCredentialsProvider() {
return ProcessCredentialsProvider.builder()
.command(properties.get(ProfileProperty.CREDENTIAL_PROCESS))
.staticAccountId(properties.get(ProfileProperty.AWS_ACCOUNT_ID))
+ .sourceFeatureId(BusinessMetricFeatureId.CREDENTIALS_PROFILE_PROCESS.value())
.build();
}
@@ -195,10 +199,16 @@ private AwsCredentialsProvider credentialProcessCredentialsProvider() {
*/
private AwsCredentialsProvider ssoProfileCredentialsProvider() {
validateRequiredPropertiesForSsoCredentialsProvider();
+ boolean isLegacy = isLegacySsoConfiguration();
+ String sourceFeatureId = isLegacy ?
+ BusinessMetricFeatureId.CREDENTIALS_PROFILE_SSO_LEGACY.value() :
+ BusinessMetricFeatureId.CREDENTIALS_PROFILE_SSO.value();
+
return ssoCredentialsProviderFactory().create(
ProfileProviderCredentialsContext.builder()
.profile(profile)
.profileFile(profileFile)
+ .sourceFeatureId(sourceFeatureId)
.build());
}
@@ -211,6 +221,10 @@ private void validateRequiredPropertiesForSsoCredentialsProvider() {
}
}
+ private boolean isLegacySsoConfiguration() {
+ return !properties.containsKey(ProfileSection.SSO_SESSION.getPropertyKeyName());
+ }
+
private AwsCredentialsProvider roleAndWebIdentityTokenProfileCredentialsProvider() {
requireProperties(ProfileProperty.ROLE_ARN, ProfileProperty.WEB_IDENTITY_TOKEN_FILE);
@@ -223,6 +237,8 @@ private AwsCredentialsProvider roleAndWebIdentityTokenProfileCredentialsProvider
.roleArn(roleArn)
.roleSessionName(roleSessionName)
.webIdentityTokenFile(webIdentityTokenFile)
+ .sourceFeatureId(BusinessMetricFeatureId
+ .CREDENTIALS_PROFILE_STS_WEB_ID_TOKEN.value())
.build();
return WebIdentityCredentialsUtils.factory().create(credentialProperties);
@@ -249,7 +265,8 @@ private AwsCredentialsProvider roleAndSourceProfileBasedProfileCredentialsProvid
.credentialsProvider(children))
.orElseThrow(this::noSourceCredentialsException);
- return stsCredentialsProviderFactory().create(sourceCredentialsProvider, profile);
+ String sourceFeatureId = BusinessMetricFeatureId.CREDENTIALS_PROFILE_SOURCE_PROFILE.value();
+ return createStsCredentialsProviderWithMetrics(sourceCredentialsProvider, sourceFeatureId);
}
/**
@@ -260,8 +277,10 @@ private AwsCredentialsProvider roleAndCredentialSourceBasedProfileCredentialsPro
requireProperties(ProfileProperty.CREDENTIAL_SOURCE);
CredentialSourceType credentialSource = CredentialSourceType.parse(properties.get(ProfileProperty.CREDENTIAL_SOURCE));
+ String profileSource = BusinessMetricFeatureId.CREDENTIALS_PROFILE_NAMED_PROVIDER.value();
AwsCredentialsProvider credentialsProvider = credentialSourceCredentialProvider(credentialSource);
- return stsCredentialsProviderFactory().create(credentialsProvider, profile);
+
+ return createStsCredentialsProviderWithMetrics(credentialsProvider, profileSource);
}
private AwsCredentialsProvider credentialSourceCredentialProvider(CredentialSourceType credentialSource) {
@@ -298,6 +317,39 @@ private IllegalStateException noSourceCredentialsException() {
return new IllegalStateException(error);
}
+ /**
+ * Extract business metrics from a credentials provider by resolving credentials and checking the provider name.
+ * This is used to propagate business metrics from source credentials to assume role operations.
+ */
+ private String extractBusinessMetricsFromProvider(AwsCredentialsProvider credentialsProvider) {
+ try {
+ AwsCredentials credentials = credentialsProvider.resolveCredentials();
+ return credentials.providerName().orElse(null);
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ /**
+ * Helper method to create STS credentials provider with business metrics propagation.
+ * This method extracts business metrics from the source credentials provider and combines them
+ * with the profile-level business metrics before creating the STS credentials provider.
+ */
+ private AwsCredentialsProvider createStsCredentialsProviderWithMetrics(AwsCredentialsProvider sourceCredentialsProvider,
+ String profileMetric) {
+ String sourceMetrics = extractBusinessMetricsFromProvider(sourceCredentialsProvider);
+
+ String combinedSource = profileMetric;
+ if (sourceMetrics != null && !sourceMetrics.isEmpty()) {
+ combinedSource = profileMetric + "," + sourceMetrics;
+ }
+
+ ChildProfileCredentialsProviderFactory.ChildProfileCredentialsRequest request =
+ new ChildProfileCredentialsProviderFactory
+ .ChildProfileCredentialsRequest(sourceCredentialsProvider, profile, combinedSource);
+ return stsCredentialsProviderFactory().create(request);
+ }
+
/**
* Load the factory that can be used to create the STS credentials provider, assuming it is on the classpath.
*/
diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/internal/WebIdentityTokenCredentialProperties.java b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/internal/WebIdentityTokenCredentialProperties.java
index 91391909b7a8..6b31f6540b0a 100644
--- a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/internal/WebIdentityTokenCredentialProperties.java
+++ b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/internal/WebIdentityTokenCredentialProperties.java
@@ -32,6 +32,7 @@ public class WebIdentityTokenCredentialProperties {
private final Duration prefetchTime;
private final Duration staleTime;
private final Duration roleSessionDuration;
+ private final String sourceFeatureId;
private WebIdentityTokenCredentialProperties(Builder builder) {
this.roleArn = builder.roleArn;
@@ -41,6 +42,7 @@ private WebIdentityTokenCredentialProperties(Builder builder) {
this.prefetchTime = builder.prefetchTime;
this.staleTime = builder.staleTime;
this.roleSessionDuration = builder.roleSessionDuration;
+ this.sourceFeatureId = builder.sourceFeatureId;
}
public String roleArn() {
@@ -71,6 +73,10 @@ public Duration roleSessionDuration() {
return this.roleSessionDuration;
}
+ public String sourceFeatureId() {
+ return sourceFeatureId;
+ }
+
public static Builder builder() {
return new Builder();
}
@@ -83,6 +89,7 @@ public static final class Builder {
private Duration prefetchTime;
private Duration staleTime;
private Duration roleSessionDuration;
+ private String sourceFeatureId;
public Builder roleArn(String roleArn) {
this.roleArn = roleArn;
@@ -119,6 +126,11 @@ public Builder roleSessionDuration(Duration roleSessionDuration) {
return this;
}
+ public Builder sourceFeatureId(String sourceFeatureId) {
+ this.sourceFeatureId = sourceFeatureId;
+ return this;
+ }
+
public WebIdentityTokenCredentialProperties build() {
return new WebIdentityTokenCredentialProperties(this);
}
diff --git a/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/ContainerCredentialsProviderTest.java b/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/ContainerCredentialsProviderTest.java
index 0f20fe51a5a6..568c82724f3b 100644
--- a/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/ContainerCredentialsProviderTest.java
+++ b/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/ContainerCredentialsProviderTest.java
@@ -31,6 +31,7 @@
import org.junit.ClassRule;
import org.junit.Test;
import software.amazon.awssdk.core.exception.SdkClientException;
+import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId;
import software.amazon.awssdk.core.util.SdkUserAgent;
import software.amazon.awssdk.testutils.EnvironmentVariableHelper;
@@ -73,8 +74,13 @@ public void testEnvVariableNotSet() {
.resolveCredentials();
}
+ @Test
+ public void testClassName() {
+ assertThat(credentialsProvider.toString()).contains("ContainerCredentialsProvider");
+ }
+
/**
- * Tests that the getCredentials returns a value when it receives a valid 200 response from endpoint.
+ * Tests that the getCredentials returns a valid response from endpoint.
*/
@Test
public void testGetCredentialsReturnsValidResponseFromEcsEndpoint() {
@@ -86,7 +92,7 @@ public void testGetCredentialsReturnsValidResponseFromEcsEndpoint() {
assertThat(credentials.accessKeyId()).isEqualTo(ACCESS_KEY_ID);
assertThat(credentials.secretAccessKey()).isEqualTo(SECRET_ACCESS_KEY);
assertThat(credentials.sessionToken()).isEqualTo(TOKEN);
- assertThat(credentials.providerName()).isPresent().contains("ContainerCredentialsProvider");
+ assertThat(credentials.providerName()).isPresent().contains(BusinessMetricFeatureId.CREDENTIALS_HTTP.value());
}
/**
diff --git a/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/InstanceProfileCredentialsProviderTest.java b/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/InstanceProfileCredentialsProviderTest.java
index 671e591b17b5..055967055c25 100644
--- a/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/InstanceProfileCredentialsProviderTest.java
+++ b/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/InstanceProfileCredentialsProviderTest.java
@@ -63,6 +63,7 @@
import org.mockito.Mockito;
import software.amazon.awssdk.core.SdkSystemSetting;
import software.amazon.awssdk.core.exception.SdkClientException;
+import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId;
import software.amazon.awssdk.core.util.SdkUserAgent;
import software.amazon.awssdk.identity.spi.AwsCredentialsIdentity;
import software.amazon.awssdk.profiles.ProfileFile;
@@ -143,6 +144,12 @@ private void verifyImdsCallInsecure() {
.withHeader(USER_AGENT_HEADER, equalTo(USER_AGENT)));
}
+ @Test
+ void testClassName() {
+ InstanceProfileCredentialsProvider provider = InstanceProfileCredentialsProvider.builder().build();
+ assertThat(provider.toString()).contains("InstanceProfileCredentialsProvider");
+ }
+
@Test
void resolveCredentials_usesTokenByDefault() {
stubSecureCredentialsResponse(aResponse().withBody(STUB_CREDENTIALS));
@@ -150,7 +157,7 @@ void resolveCredentials_usesTokenByDefault() {
AwsCredentials credentials = provider.resolveCredentials();
assertThat(credentials.accessKeyId()).isEqualTo("ACCESS_KEY_ID");
assertThat(credentials.secretAccessKey()).isEqualTo("SECRET_ACCESS_KEY");
- assertThat(credentials.providerName()).isPresent().contains("InstanceProfileCredentialsProvider");
+ assertThat(credentials.providerName()).isPresent().contains(BusinessMetricFeatureId.CREDENTIALS_IMDS.value());
verifyImdsCallWithToken();
}
@@ -162,7 +169,7 @@ void resolveCredentials_WhenConnectionDelaySetToHighValue() {
AwsCredentials credentials = provider.resolveCredentials();
assertThat(credentials.accessKeyId()).isEqualTo("ACCESS_KEY_ID");
assertThat(credentials.secretAccessKey()).isEqualTo("SECRET_ACCESS_KEY");
- assertThat(credentials.providerName()).isPresent().contains("InstanceProfileCredentialsProvider");
+ assertThat(credentials.providerName()).isPresent().contains(BusinessMetricFeatureId.CREDENTIALS_IMDS.value());
verifyImdsCallWithToken();
}
@@ -186,7 +193,7 @@ void resolveIdentity_WhenConnectionDelaySetToHighValue() {
AwsCredentialsIdentity credentialsIdentity = provider.resolveIdentity().join();
assertThat(credentialsIdentity.accessKeyId()).isEqualTo("ACCESS_KEY_ID");
assertThat(credentialsIdentity.secretAccessKey()).isEqualTo("SECRET_ACCESS_KEY");
- assertThat(credentialsIdentity.providerName()).isPresent().contains("InstanceProfileCredentialsProvider");
+ assertThat(credentialsIdentity.providerName()).isPresent().contains(BusinessMetricFeatureId.CREDENTIALS_IMDS.value());
verifyImdsCallWithToken();
}
@@ -649,7 +656,7 @@ void shouldNotRetry_whenSucceeds() {
AwsCredentials credentials = provider.resolveCredentials();
assertThat(credentials.accessKeyId()).isEqualTo("ACCESS_KEY_ID");
assertThat(credentials.secretAccessKey()).isEqualTo("SECRET_ACCESS_KEY");
- assertThat(credentials.providerName()).isPresent().contains("InstanceProfileCredentialsProvider");
+ assertThat(credentials.providerName()).isPresent().contains(BusinessMetricFeatureId.CREDENTIALS_IMDS.value());
verifyImdsCallWithToken();
WireMock.verify(exactly(1), getRequestedFor(urlPathEqualTo(CREDENTIALS_RESOURCE_PATH + "some-profile")));
}
@@ -680,7 +687,7 @@ public void checkPermission(Permission perm) {
// Verify credentials are correctly resolved from instance profile
assertThat(credentials.accessKeyId()).isEqualTo("ACCESS_KEY_ID");
assertThat(credentials.secretAccessKey()).isEqualTo("SECRET_ACCESS_KEY");
- assertThat(credentials.providerName()).isPresent().contains("InstanceProfileCredentialsProvider");
+ assertThat(credentials.providerName()).isPresent().contains(BusinessMetricFeatureId.CREDENTIALS_IMDS.value());
// Verify IMDS was called
verifyImdsCallWithToken();
diff --git a/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/ProcessCredentialsProviderTest.java b/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/ProcessCredentialsProviderTest.java
index 0fdedff07646..be21f7a6b56f 100644
--- a/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/ProcessCredentialsProviderTest.java
+++ b/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/ProcessCredentialsProviderTest.java
@@ -36,6 +36,7 @@
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
+import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId;
import software.amazon.awssdk.utils.DateUtils;
import software.amazon.awssdk.utils.IoUtils;
import software.amazon.awssdk.utils.Platform;
@@ -70,6 +71,12 @@ static void teardown() {
}
}
+ @Test
+ void testToString() {
+ ProcessCredentialsProvider provider = ProcessCredentialsProvider.builder().command("test").build();
+ assertThat(provider.toString()).contains("ProcessCredentialsProvider");
+ }
+
@ParameterizedTest(name = "{index} - {0}")
@MethodSource("staticCredentialsValues")
void staticCredentialsCanBeLoaded(String description, String staticAccountId, Optional expectedValue,
@@ -143,7 +150,7 @@ public void staticCredentials_commandAsListOfStrings_CanBeLoaded() {
assertThat(credentials).isInstanceOf(AwsBasicCredentials.class);
assertThat(credentials.accessKeyId()).isEqualTo("accessKeyId");
assertThat(credentials.secretAccessKey()).isEqualTo("secretAccessKey");
- assertThat(credentials.providerName()).isPresent().contains("ProcessCredentialsProvider");
+ assertThat(credentials.providerName()).isPresent().hasValue(BusinessMetricFeatureId.CREDENTIALS_PROCESS.value());
}
@Test
@@ -186,11 +193,13 @@ void sessionCredentialsWithStaticAccountIdCanBeLoaded() {
scriptLocation, ACCESS_KEY_ID, SECRET_ACCESS_KEY, expiration))
.credentialRefreshThreshold(Duration.ofSeconds(1))
.staticAccountId("staticAccountId")
+ .sourceFeatureId("v")
.build();
AwsCredentials credentials = credentialsProvider.resolveCredentials();
verifySessionCredentials(credentials, expiration);
assertThat(credentials.accountId()).isPresent().hasValue("staticAccountId");
+ assertThat(credentials.providerName()).isPresent().hasValue("v,w");
}
private void verifySessionCredentials(AwsCredentials credentials, String expiration) {
diff --git a/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/StaticCredentialsProviderTest.java b/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/StaticCredentialsProviderTest.java
index d02b633dfd2e..5502610543b1 100644
--- a/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/StaticCredentialsProviderTest.java
+++ b/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/StaticCredentialsProviderTest.java
@@ -19,6 +19,7 @@
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import org.junit.jupiter.api.Test;
+import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId;
class StaticCredentialsProviderTest {
@Test
@@ -39,6 +40,7 @@ void getAwsCredentialsWithAccountId_ReturnsSameCredentials() {
.build();
AwsCredentials actualCredentials = StaticCredentialsProvider.create(credentials).resolveCredentials();
assertThat(actualCredentials).isEqualTo(credentials);
+ assertThat(actualCredentials.providerName()).isPresent().contains(BusinessMetricFeatureId.CREDENTIALS_CODE.value());
}
@@ -48,7 +50,7 @@ void getSessionAwsCredentials_ReturnsSameCredentials() {
AwsCredentials actualCredentials = StaticCredentialsProvider.create(credentials).resolveCredentials();
assertThat(credentials).isEqualTo(actualCredentials);
assertThat(credentials.providerName()).isNotPresent();
- assertThat(actualCredentials.providerName()).isPresent();
+ assertThat(actualCredentials.providerName()).isPresent().contains(BusinessMetricFeatureId.CREDENTIALS_CODE.value());
}
@Test
diff --git a/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/SystemSettingCredentialsProvidersTest.java b/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/SystemSettingCredentialsProvidersTest.java
index 8961c5d0a18c..9ecc620a5af4 100644
--- a/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/SystemSettingCredentialsProvidersTest.java
+++ b/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/SystemSettingCredentialsProvidersTest.java
@@ -22,13 +22,16 @@
import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;
+import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import software.amazon.awssdk.core.SdkSystemSetting;
import software.amazon.awssdk.core.exception.SdkClientException;
+import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId;
import software.amazon.awssdk.testutils.EnvironmentVariableHelper;
import software.amazon.awssdk.utils.Pair;
@@ -66,7 +69,9 @@ void configureEnvVars_resolveCredentials(String description,
configureEnvironmentVariables(systemSettings);
EnvironmentVariableCredentialsProvider provider = EnvironmentVariableCredentialsProvider.create();
if (expected != null) {
- assertThat(provider.resolveCredentials()).satisfies(expected);
+ AwsCredentials resolvedCredentials = provider.resolveCredentials();
+ assertThat(resolvedCredentials).satisfies(expected);
+ assertThat(resolvedCredentials.providerName()).isPresent().contains(BusinessMetricFeatureId.CREDENTIALS_ENV_VARS.value());
} else {
assertThatThrownBy(provider::resolveCredentials).isInstanceOf(SdkClientException.class);
}
@@ -80,7 +85,9 @@ void configureSystemProperties_resolveCredentials(String description,
configureSystemProperties(systemSettings);
SystemPropertyCredentialsProvider provider = SystemPropertyCredentialsProvider.create();
if (expected != null) {
- assertThat(provider.resolveCredentials()).satisfies(expected);
+ AwsCredentials resolvedCredentials = provider.resolveCredentials();
+ assertThat(resolvedCredentials).satisfies(expected);
+ assertThat(resolvedCredentials.providerName()).isPresent().contains(BusinessMetricFeatureId.CREDENTIALS_JVM_SYSTEM_PROPERTIES.value());
} else {
assertThatThrownBy(provider::resolveCredentials).isInstanceOf(SdkClientException.class);
}
@@ -123,6 +130,18 @@ private static List config() {
);
}
+ @Test
+ void testEnvVarsClassName() {
+ EnvironmentVariableCredentialsProvider provider = EnvironmentVariableCredentialsProvider.create();
+ Assertions.assertThat(provider.toString()).contains("EnvironmentVariableCredentialsProvider");
+ }
+
+ @Test
+ void testSystemPropertyClassName() {
+ SystemPropertyCredentialsProvider provider = SystemPropertyCredentialsProvider.create();
+ Assertions.assertThat(provider.toString()).contains("SystemPropertyCredentialsProvider");
+ }
+
private void configureEnvironmentVariables(List> systemSettings) {
for (Pair setting : systemSettings) {
ENVIRONMENT_VARIABLE_HELPER.set(setting.left(), setting.right());
diff --git a/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/SystemSettingsCredentialsProviderTest.java b/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/SystemSettingsCredentialsProviderTest.java
index 81905de526ac..95cfc899460f 100644
--- a/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/SystemSettingsCredentialsProviderTest.java
+++ b/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/SystemSettingsCredentialsProviderTest.java
@@ -47,7 +47,7 @@ void systemPropertyCredentialsProvider_resolveCredentials_returnsCredentialsWith
AwsCredentials credentials = SystemPropertyCredentialsProvider.create().resolveCredentials();
assertThat(credentials.accessKeyId()).isEqualTo("akid1");
assertThat(credentials.secretAccessKey()).isEqualTo("skid1");
- assertThat(credentials.providerName()).isPresent().contains("SystemPropertyCredentialsProvider");
+ assertThat(credentials.providerName()).isPresent().contains("f");
}
@Test
@@ -55,6 +55,6 @@ void environmentVariableCredentialsProvider_resolveCredentials_returnsCredential
AwsCredentials credentials = EnvironmentVariableCredentialsProvider.create().resolveCredentials();
assertThat(credentials.accessKeyId()).isEqualTo("akid2");
assertThat(credentials.secretAccessKey()).isEqualTo("skid2");
- assertThat(credentials.providerName()).isPresent().contains("EnvironmentVariableCredentialsProvider");
+ assertThat(credentials.providerName()).isPresent().contains("g");
}
}
diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/ApplyUserAgentStage.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/ApplyUserAgentStage.java
index ef1e3fb2cc9d..744b1f4a5d8d 100644
--- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/ApplyUserAgentStage.java
+++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/ApplyUserAgentStage.java
@@ -15,23 +15,20 @@
package software.amazon.awssdk.core.internal.http.pipeline.stages;
-import static software.amazon.awssdk.core.internal.useragent.UserAgentConstant.AUTH_SOURCE;
import static software.amazon.awssdk.core.internal.useragent.UserAgentConstant.BUSINESS_METADATA;
-import static software.amazon.awssdk.core.internal.useragent.UserAgentConstant.CONFIG_METADATA;
import static software.amazon.awssdk.core.internal.useragent.UserAgentConstant.SLASH;
import static software.amazon.awssdk.core.internal.useragent.UserAgentConstant.SPACE;
import static software.amazon.awssdk.core.internal.useragent.UserAgentConstant.appendSpaceAndField;
-import static software.amazon.awssdk.core.internal.useragent.UserAgentConstant.uaPair;
import static software.amazon.awssdk.utils.StringUtils.trim;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import java.util.Optional;
-import java.util.concurrent.CompletableFuture;
import software.amazon.awssdk.annotations.SdkInternalApi;
import software.amazon.awssdk.core.ApiName;
-import software.amazon.awssdk.core.SelectedAuthScheme;
import software.amazon.awssdk.core.client.config.SdkAdvancedClientOption;
import software.amazon.awssdk.core.client.config.SdkClientConfiguration;
import software.amazon.awssdk.core.client.config.SdkClientOption;
@@ -40,12 +37,10 @@
import software.amazon.awssdk.core.internal.http.HttpClientDependencies;
import software.amazon.awssdk.core.internal.http.RequestExecutionContext;
import software.amazon.awssdk.core.internal.http.pipeline.MutableRequestToRequestPipeline;
-import software.amazon.awssdk.core.internal.useragent.IdentityProviderNameMapping;
import software.amazon.awssdk.core.useragent.AdditionalMetadata;
import software.amazon.awssdk.core.useragent.BusinessMetricCollection;
import software.amazon.awssdk.http.SdkHttpFullRequest;
import software.amazon.awssdk.identity.spi.Identity;
-import software.amazon.awssdk.utils.CollectionUtils;
import software.amazon.awssdk.utils.CompletableFutureUtils;
import software.amazon.awssdk.utils.Logger;
import software.amazon.awssdk.utils.Pair;
@@ -118,10 +113,6 @@ private String finalizeUserAgent(RequestExecutionContext context) {
userAgentMetadata.forEach(s -> javaUserAgent.append(SPACE).append(s));
}
- //add remaining SDK user agent properties
- identityProviderName(context.executionAttributes()).ifPresent(
- authSource -> appendSpaceAndField(javaUserAgent, CONFIG_METADATA, uaPair(AUTH_SOURCE, authSource)));
-
Optional businessMetrics = getBusinessMetricsString(context.executionAttributes(), groupedApiNames.right());
businessMetrics.ifPresent(
metrics -> appendSpaceAndField(javaUserAgent, BUSINESS_METADATA, metrics)
@@ -156,29 +147,33 @@ private static Optional getBusinessMetricsString(ExecutionAttributes exe
Collection metricsFromApiNames) {
BusinessMetricCollection businessMetrics =
executionAttributes.getAttribute(SdkInternalExecutionAttribute.BUSINESS_METRICS);
- if (businessMetrics == null && CollectionUtils.isNullOrEmpty(metricsFromApiNames)) {
- return Optional.empty();
- }
if (businessMetrics == null) {
businessMetrics = new BusinessMetricCollection();
}
businessMetrics.merge(metricsFromApiNames);
- return Optional.of(businessMetrics.asBoundedString());
- }
- private static Optional identityProviderName(ExecutionAttributes executionAttributes) {
- SelectedAuthScheme> selectedAuthScheme = executionAttributes
- .getAttribute(SdkInternalExecutionAttribute.SELECTED_AUTH_SCHEME);
- if (selectedAuthScheme == null) {
+ credentialProviderBusinessMetrics(executionAttributes).ifPresent(businessMetrics::merge);
+
+ if (businessMetrics.recordedMetrics().isEmpty()) {
return Optional.empty();
}
- return providerNameFromIdentity(selectedAuthScheme);
+
+ return Optional.of(businessMetrics.asBoundedString());
}
- private static Optional providerNameFromIdentity(SelectedAuthScheme selectedAuthScheme) {
- CompletableFuture extends T> identityFuture = selectedAuthScheme.identity();
- T identity = CompletableFutureUtils.joinLikeSync(identityFuture);
- return identity.providerName().flatMap(IdentityProviderNameMapping::mapFrom);
+ private static Optional> credentialProviderBusinessMetrics(
+ ExecutionAttributes executionAttributes) {
+ return Optional.ofNullable(
+ executionAttributes.getAttribute(SdkInternalExecutionAttribute.SELECTED_AUTH_SCHEME))
+ .map(selectedAuthScheme ->
+ CompletableFutureUtils.joinLikeSync(selectedAuthScheme.identity()))
+ .flatMap(Identity::providerName)
+ .map(providerName -> {
+ if (StringUtils.isBlank(providerName)) {
+ return Collections.emptyList();
+ }
+ return Arrays.asList(providerName.split(","));
+ });
}
/**
diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/useragent/BusinessMetricFeatureId.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/useragent/BusinessMetricFeatureId.java
index 7f1483d56895..884f57bf5691 100644
--- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/useragent/BusinessMetricFeatureId.java
+++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/useragent/BusinessMetricFeatureId.java
@@ -22,7 +22,7 @@
/**
* An enum class representing a short form of identity providers to record in the UA string.
*
- * Unimplemented metrics: I,J,K,M,O,S,U-c,e-[latest]
+ * Unimplemented metrics: I,J,K,M,O,S,U-c
* Unsupported metrics (these will never be added): A,H
*/
@SdkProtectedApi
@@ -42,6 +42,27 @@ public enum BusinessMetricFeatureId {
RESOLVED_ACCOUNT_ID("T"),
DDB_MAPPER("d"),
BEARER_SERVICE_ENV_VARS("3"),
+ CREDENTIALS_CODE("e"),
+ CREDENTIALS_JVM_SYSTEM_PROPERTIES("f"),
+ CREDENTIALS_ENV_VARS("g"),
+ CREDENTIALS_ENV_VARS_STS_WEB_ID_TOKEN("h"),
+ CREDENTIALS_STS_ASSUME_ROLE("i"),
+ CREDENTIALS_STS_ASSUME_ROLE_SAML("j"),
+ CREDENTIALS_STS_ASSUME_ROLE_WEB_ID("k"),
+ CREDENTIALS_STS_FEDERATION_TOKEN("l"),
+ CREDENTIALS_STS_SESSION_TOKEN("m"),
+ CREDENTIALS_PROFILE("n"),
+ CREDENTIALS_PROFILE_SOURCE_PROFILE("o"),
+ CREDENTIALS_PROFILE_NAMED_PROVIDER("p"),
+ CREDENTIALS_PROFILE_STS_WEB_ID_TOKEN("q"),
+ CREDENTIALS_PROFILE_SSO("r"),
+ CREDENTIALS_SSO("s"),
+ CREDENTIALS_PROFILE_SSO_LEGACY("t"),
+ CREDENTIALS_SSO_LEGACY("u"),
+ CREDENTIALS_PROFILE_PROCESS("v"),
+ CREDENTIALS_PROCESS("w"),
+ CREDENTIALS_HTTP("z"),
+ CREDENTIALS_IMDS("0"),
UNKNOWN("Unknown");
private static final Map VALUE_MAP =
diff --git a/core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/http/pipeline/stages/ApplyUserAgentStageTest.java b/core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/http/pipeline/stages/ApplyUserAgentStageTest.java
index d02654a78071..4db0103b7e3c 100644
--- a/core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/http/pipeline/stages/ApplyUserAgentStageTest.java
+++ b/core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/http/pipeline/stages/ApplyUserAgentStageTest.java
@@ -60,7 +60,7 @@ public class ApplyUserAgentStageTest {
(HttpSigner) Mockito.mock(HttpSigner.class),
AuthSchemeOption.builder().schemeId("mock").build());
- private static final String PROVIDER_SOURCE = "ProcessCredentialsProvider";
+ private static final String PROVIDER_SOURCE = "w";
private static final AwsCredentialsIdentity IDENTITY_WITHOUT_SOURCE =
AwsCredentialsIdentity.create("akid", "secret");
@@ -149,7 +149,7 @@ public void when_identityContainsProvider_authSourceIsPresent() throws Exception
List userAgentHeaders = request.headers().get(HEADER_USER_AGENT);
assertThat(userAgentHeaders).isNotNull().hasSize(1);
- assertThat(userAgentHeaders.get(0)).contains("auth-source#proc");
+ assertThat(userAgentHeaders.get(0)).contains("m/w");
}
private static HttpClientDependencies dependencies(String clientUserAgent) {
diff --git a/services/sso/src/main/java/software/amazon/awssdk/services/sso/auth/SsoCredentialsProvider.java b/services/sso/src/main/java/software/amazon/awssdk/services/sso/auth/SsoCredentialsProvider.java
index ce4fbaf2ca97..e0e406b58464 100644
--- a/services/sso/src/main/java/software/amazon/awssdk/services/sso/auth/SsoCredentialsProvider.java
+++ b/services/sso/src/main/java/software/amazon/awssdk/services/sso/auth/SsoCredentialsProvider.java
@@ -25,11 +25,13 @@
import software.amazon.awssdk.auth.credentials.AwsCredentials;
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
import software.amazon.awssdk.auth.credentials.AwsSessionCredentials;
+import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId;
import software.amazon.awssdk.services.sso.SsoClient;
import software.amazon.awssdk.services.sso.internal.SessionCredentialsHolder;
import software.amazon.awssdk.services.sso.model.GetRoleCredentialsRequest;
import software.amazon.awssdk.services.sso.model.RoleCredentials;
import software.amazon.awssdk.utils.SdkAutoCloseable;
+import software.amazon.awssdk.utils.StringUtils;
import software.amazon.awssdk.utils.builder.CopyableBuilder;
import software.amazon.awssdk.utils.builder.ToCopyableBuilder;
import software.amazon.awssdk.utils.cache.CachedSupplier;
@@ -51,7 +53,7 @@
@SdkPublicApi
public final class SsoCredentialsProvider implements AwsCredentialsProvider, SdkAutoCloseable,
ToCopyableBuilder {
- private static final String PROVIDER_NAME = "SsoCredentialsProvider";
+ private static final String PROVIDER_NAME = BusinessMetricFeatureId.CREDENTIALS_SSO.value();
private static final Duration DEFAULT_STALE_TIME = Duration.ofMinutes(1);
private static final Duration DEFAULT_PREFETCH_TIME = Duration.ofMinutes(5);
@@ -59,6 +61,8 @@ public final class SsoCredentialsProvider implements AwsCredentialsProvider, Sdk
private static final String ASYNC_THREAD_NAME = "sdk-sso-credentials-provider";
private final Supplier getRoleCredentialsRequestSupplier;
+ private final String sourceFeatureId;
+ private final String providerName;
private final SsoClient ssoClient;
private final Duration staleTime;
@@ -77,6 +81,11 @@ private SsoCredentialsProvider(BuilderImpl builder) {
this.staleTime = Optional.ofNullable(builder.staleTime).orElse(DEFAULT_STALE_TIME);
this.prefetchTime = Optional.ofNullable(builder.prefetchTime).orElse(DEFAULT_PREFETCH_TIME);
+ this.sourceFeatureId = builder.sourceFeatureId;
+
+ this.providerName = StringUtils.isEmpty(builder.sourceFeatureId)
+ ? PROVIDER_NAME
+ : builder.sourceFeatureId + "," + PROVIDER_NAME;
this.asyncCredentialUpdateEnabled = builder.asyncCredentialUpdateEnabled;
CachedSupplier.Builder cacheBuilder =
@@ -95,11 +104,11 @@ private SsoCredentialsProvider(BuilderImpl builder) {
*/
private RefreshResult updateSsoCredentials() {
SessionCredentialsHolder credentials = getUpdatedCredentials(ssoClient);
- Instant acutalTokenExpiration = credentials.sessionCredentialsExpiration();
+ Instant actualTokenExpiration = credentials.sessionCredentialsExpiration();
return RefreshResult.builder(credentials)
- .staleTime(acutalTokenExpiration.minus(staleTime))
- .prefetchTime(acutalTokenExpiration.minus(prefetchTime))
+ .staleTime(actualTokenExpiration.minus(staleTime))
+ .prefetchTime(actualTokenExpiration.minus(prefetchTime))
.build();
}
@@ -112,11 +121,15 @@ private SessionCredentialsHolder getUpdatedCredentials(SsoClient ssoClient) {
.secretAccessKey(roleCredentials.secretAccessKey())
.sessionToken(roleCredentials.sessionToken())
.accountId(request.accountId())
- .providerName(PROVIDER_NAME)
+ .providerName(providerName())
.build();
return new SessionCredentialsHolder(sessionCredentials, Instant.ofEpochMilli(roleCredentials.expiration()));
}
+ private String providerName() {
+ return this.providerName;
+ }
+
/**
* The amount of time, relative to session token expiration, that the cached credentials are considered stale and
* should no longer be used. All threads will block until the value is updated.
@@ -206,6 +219,13 @@ public interface Builder extends CopyableBuilder getRoleCredentialsRequestSupplier);
+ /**
+ * An optional string list of {@link BusinessMetricFeatureId} denoting previous
+ * credentials providers that are chained with this one. This method is primarily intended for use by AWS SDK internal
+ * components and should not be used directly by external users.
+ */
+ Builder sourceFeatureId(String sourceFeatureId);
+
/**
* Create a {@link SsoCredentialsProvider} using the configuration applied to this builder.
* @return
@@ -220,6 +240,7 @@ protected static final class BuilderImpl implements Builder {
private Duration staleTime;
private Duration prefetchTime;
private Supplier getRoleCredentialsRequestSupplier;
+ private String sourceFeatureId;
BuilderImpl() {
@@ -231,6 +252,7 @@ public BuilderImpl(SsoCredentialsProvider provider) {
this.staleTime = provider.staleTime;
this.prefetchTime = provider.prefetchTime;
this.getRoleCredentialsRequestSupplier = provider.getRoleCredentialsRequestSupplier;
+ this.sourceFeatureId = provider.sourceFeatureId;
}
@Override
@@ -268,6 +290,12 @@ public Builder refreshRequest(Supplier getRoleCredent
return this;
}
+ @Override
+ public Builder sourceFeatureId(String sourceFeatureId) {
+ this.sourceFeatureId = sourceFeatureId;
+ return this;
+ }
+
@Override
public SsoCredentialsProvider build() {
return new SsoCredentialsProvider(this);
diff --git a/services/sso/src/main/java/software/amazon/awssdk/services/sso/auth/SsoProfileCredentialsProviderFactory.java b/services/sso/src/main/java/software/amazon/awssdk/services/sso/auth/SsoProfileCredentialsProviderFactory.java
index f3b910c3e1fa..9584f22f4576 100644
--- a/services/sso/src/main/java/software/amazon/awssdk/services/sso/auth/SsoProfileCredentialsProviderFactory.java
+++ b/services/sso/src/main/java/software/amazon/awssdk/services/sso/auth/SsoProfileCredentialsProviderFactory.java
@@ -63,10 +63,7 @@ public class SsoProfileCredentialsProviderFactory implements ProfileCredentialsP
*/
@Override
public AwsCredentialsProvider create(ProfileProviderCredentialsContext credentialsContext) {
- return new SsoProfileCredentialsProvider(credentialsContext.profile(),
- credentialsContext.profileFile(),
- sdkTokenProvider(credentialsContext.profile(),
- credentialsContext.profileFile()));
+ return new SsoProfileCredentialsProvider(credentialsContext, sdkTokenProvider(credentialsContext));
}
/**
@@ -74,26 +71,27 @@ public AwsCredentialsProvider create(ProfileProviderCredentialsContext credentia
* This method is only used for testing.
*/
@SdkTestInternalApi
- public AwsCredentialsProvider create(Profile profile, ProfileFile profileFile,
+ public AwsCredentialsProvider create(ProfileProviderCredentialsContext credentialsContext,
SdkTokenProvider tokenProvider) {
- return new SsoProfileCredentialsProvider(profile, profileFile, tokenProvider);
+ return new SsoProfileCredentialsProvider(credentialsContext, tokenProvider);
}
/**
* A wrapper for a {@link SsoCredentialsProvider} that is returned by this factory when {@link
- * #create(ProfileProviderCredentialsContext)} * or {@link #create(Profile, ProfileFile, SdkTokenProvider)} is invoked. This
- * wrapper is important because it ensures * the parent credentials provider is closed when the sso credentials provider is no
- * longer needed.
+ * #create(ProfileProviderCredentialsContext)} * or {@link #create(ProfileProviderCredentialsContext, SdkTokenProvider)}
+ * is invoked. This wrapper is important because it ensures * the parent credentials provider is closed when the sso
+ * credentials provider is no longer needed.
*/
private static final class SsoProfileCredentialsProvider implements AwsCredentialsProvider, SdkAutoCloseable {
private final SsoClient ssoClient;
private final SsoCredentialsProvider credentialsProvider;
- private SsoProfileCredentialsProvider(Profile profile, ProfileFile profileFile,
+ private SsoProfileCredentialsProvider(ProfileProviderCredentialsContext credentialsContext,
SdkTokenProvider tokenProvider) {
+ Profile profile = credentialsContext.profile();
String ssoAccountId = profile.properties().get(ProfileProperty.SSO_ACCOUNT_ID);
String ssoRoleName = profile.properties().get(ProfileProperty.SSO_ROLE_NAME);
- String ssoRegion = regionFromProfileOrSession(profile, profileFile);
+ String ssoRegion = regionFromProfileOrSession(profile, credentialsContext.profileFile());
this.ssoClient = SsoClient.builder()
.credentialsProvider(AnonymousCredentialsProvider.create())
@@ -114,6 +112,7 @@ private SsoProfileCredentialsProvider(Profile profile, ProfileFile profileFile,
this.credentialsProvider = SsoCredentialsProvider.builder()
.ssoClient(ssoClient)
.refreshRequest(supplier)
+ .sourceFeatureId(credentialsContext.sourceFeatureId())
.build();
}
@@ -157,7 +156,9 @@ private static Profile ssoSessionInProfile(String sessionName, ProfileFile profi
return ssoProfile;
}
- private static SdkTokenProvider sdkTokenProvider(Profile profile, ProfileFile profileFile) {
+ private static SdkTokenProvider sdkTokenProvider(ProfileProviderCredentialsContext credentialsContext) {
+ Profile profile = credentialsContext.profile();
+ ProfileFile profileFile = credentialsContext.profileFile();
Optional ssoSession = profile.property(ProfileSection.SSO_SESSION.getPropertyKeyName());
@@ -172,11 +173,9 @@ private static SdkTokenProvider sdkTokenProvider(Profile profile, ProfileFile pr
.profileFile(() -> profileFile)
.profileName(profile.name())
.build());
- } else {
- return new SsoAccessTokenProvider(generateCachedTokenPath(
- profile.properties().get(ProfileProperty.SSO_START_URL), TOKEN_DIRECTORY));
-
}
+ return new SsoAccessTokenProvider(generateCachedTokenPath(profile.properties().get(ProfileProperty.SSO_START_URL),
+ TOKEN_DIRECTORY));
}
private static void validateCommonProfileProperties(Profile profile, Profile ssoSessionProfileFile, String propertyName) {
diff --git a/services/sso/src/test/java/software/amazon/awssdk/services/sso/auth/SsoCredentialsProviderTest.java b/services/sso/src/test/java/software/amazon/awssdk/services/sso/auth/SsoCredentialsProviderTest.java
index 9540a77ba6c6..d7be6cdd852c 100644
--- a/services/sso/src/test/java/software/amazon/awssdk/services/sso/auth/SsoCredentialsProviderTest.java
+++ b/services/sso/src/test/java/software/amazon/awssdk/services/sso/auth/SsoCredentialsProviderTest.java
@@ -27,6 +27,7 @@
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import software.amazon.awssdk.auth.credentials.AwsSessionCredentials;
+import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId;
import software.amazon.awssdk.services.sso.SsoClient;
import software.amazon.awssdk.services.sso.model.GetRoleCredentialsRequest;
import software.amazon.awssdk.services.sso.model.GetRoleCredentialsResponse;
@@ -136,7 +137,7 @@ private void callClientWithCredentialsProvider(Instant credentialsExpirationDate
assertThat(actualCredentials.accessKeyId()).isEqualTo("a");
assertThat(actualCredentials.secretAccessKey()).isEqualTo("b");
assertThat(actualCredentials.sessionToken()).isEqualTo("c");
- assertThat(actualCredentials.providerName()).isPresent().contains("SsoCredentialsProvider");
+ assertThat(actualCredentials.providerName()).isPresent().contains(BusinessMetricFeatureId.CREDENTIALS_SSO.value());
assertThat(actualCredentials.accountId()).isPresent().contains("123456789");
}
}
diff --git a/services/sso/src/test/java/software/amazon/awssdk/services/sso/auth/SsoProfileCredentialsProviderFactoryTest.java b/services/sso/src/test/java/software/amazon/awssdk/services/sso/auth/SsoProfileCredentialsProviderFactoryTest.java
index c5cb2b57834d..8da326bf589f 100644
--- a/services/sso/src/test/java/software/amazon/awssdk/services/sso/auth/SsoProfileCredentialsProviderFactoryTest.java
+++ b/services/sso/src/test/java/software/amazon/awssdk/services/sso/auth/SsoProfileCredentialsProviderFactoryTest.java
@@ -79,9 +79,12 @@ public void createSsoCredentialsProviderWithFactorySucceed() throws IOException
cachedTokenFilePath);
SsoProfileCredentialsProviderFactory factory = new SsoProfileCredentialsProviderFactory();
- assertThat(factory.create(profileFile.profile("foo").get(),
- profileFile,
- tokenProvider)).isInstanceOf(AwsCredentialsProvider.class);
+ assertThat(factory.create(ProfileProviderCredentialsContext.builder()
+ .profile(profileFile.profile("foo").get())
+ .profileFile(profileFile)
+ .build(),
+ tokenProvider))
+ .isInstanceOf(AwsCredentialsProvider.class);
}
private Path prepareTestCachedTokenFile(String tokenFileContent, String generatedTokenFileName) throws IOException {
@@ -169,7 +172,10 @@ public void tokenResolvedFromTokenProvider(@Mock SdkTokenProvider sdkTokenProvid
"sso_start_url=https//d-abc123.awsapps.com/start");
SsoProfileCredentialsProviderFactory factory = new SsoProfileCredentialsProviderFactory();
when(sdkTokenProvider.resolveToken()).thenReturn(SsoAccessToken.builder().accessToken("sample").expiresAt(Instant.now()).build());
- AwsCredentialsProvider credentialsProvider = factory.create(profileFile.profile("test").get(), profileFile, sdkTokenProvider);
+ AwsCredentialsProvider credentialsProvider = factory.create(ProfileProviderCredentialsContext.builder()
+ .profile(profileFile.profile("test").get())
+ .profileFile(profileFile)
+ .build(), sdkTokenProvider);
try {
credentialsProvider.resolveCredentials();
} catch (Exception e) {
diff --git a/services/sts/src/main/java/software/amazon/awssdk/services/sts/auth/StsAssumeRoleCredentialsProvider.java b/services/sts/src/main/java/software/amazon/awssdk/services/sts/auth/StsAssumeRoleCredentialsProvider.java
index a59570be0103..34a2dcd76f90 100644
--- a/services/sts/src/main/java/software/amazon/awssdk/services/sts/auth/StsAssumeRoleCredentialsProvider.java
+++ b/services/sts/src/main/java/software/amazon/awssdk/services/sts/auth/StsAssumeRoleCredentialsProvider.java
@@ -25,9 +25,11 @@
import software.amazon.awssdk.annotations.ThreadSafe;
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
import software.amazon.awssdk.auth.credentials.AwsSessionCredentials;
+import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId;
import software.amazon.awssdk.services.sts.StsClient;
import software.amazon.awssdk.services.sts.model.AssumeRoleRequest;
import software.amazon.awssdk.services.sts.model.AssumeRoleResponse;
+import software.amazon.awssdk.utils.StringUtils;
import software.amazon.awssdk.utils.ToString;
import software.amazon.awssdk.utils.Validate;
import software.amazon.awssdk.utils.builder.ToCopyableBuilder;
@@ -49,8 +51,10 @@
public final class StsAssumeRoleCredentialsProvider
extends StsCredentialsProvider
implements ToCopyableBuilder {
- private static final String PROVIDER_NAME = "StsAssumeRoleCredentialsProvider";
+ private static final String PROVIDER_NAME = BusinessMetricFeatureId.CREDENTIALS_STS_ASSUME_ROLE.value();
private final Supplier assumeRoleRequestSupplier;
+ private final String sourceFeatureId;
+ private final String providerName;
/**
* @see #builder()
@@ -60,6 +64,10 @@ private StsAssumeRoleCredentialsProvider(Builder builder) {
Validate.notNull(builder.assumeRoleRequestSupplier, "Assume role request must not be null.");
this.assumeRoleRequestSupplier = builder.assumeRoleRequestSupplier;
+ this.sourceFeatureId = builder.sourceFeatureId;
+ this.providerName = StringUtils.isEmpty(builder.sourceFeatureId)
+ ? PROVIDER_NAME
+ : builder.sourceFeatureId + "," + PROVIDER_NAME;
}
/**
@@ -75,7 +83,7 @@ protected AwsSessionCredentials getUpdatedCredentials(StsClient stsClient) {
Validate.notNull(assumeRoleRequest, "Assume role request must not be null.");
AssumeRoleResponse assumeRoleResponse = stsClient.assumeRole(assumeRoleRequest);
return fromStsCredentials(assumeRoleResponse.credentials(),
- PROVIDER_NAME,
+ providerName(),
accountIdFromArn(assumeRoleResponse.assumedRoleUser()));
}
@@ -93,7 +101,7 @@ public Builder toBuilder() {
@Override
String providerName() {
- return PROVIDER_NAME;
+ return this.providerName;
}
/**
@@ -103,6 +111,7 @@ String providerName() {
@NotThreadSafe
public static final class Builder extends BaseBuilder {
private Supplier assumeRoleRequestSupplier;
+ private String sourceFeatureId;
private Builder() {
super(StsAssumeRoleCredentialsProvider::new);
@@ -111,6 +120,7 @@ private Builder() {
private Builder(StsAssumeRoleCredentialsProvider provider) {
super(StsAssumeRoleCredentialsProvider::new, provider);
this.assumeRoleRequestSupplier = provider.assumeRoleRequestSupplier;
+ this.sourceFeatureId = provider.sourceFeatureId;
}
/**
@@ -145,6 +155,17 @@ public Builder refreshRequest(Consumer assumeRoleRequ
return refreshRequest(AssumeRoleRequest.builder().applyMutation(assumeRoleRequest).build());
}
+ /**
+ * An optional string list of {@link BusinessMetricFeatureId} denoting previous credentials providers
+ * that are chained with this one.
+ * Note: This method is primarily intended for use by AWS SDK internal components
+ * and should not be used directly by external users.
+ */
+ public Builder sourceFeatureId(String sourceFeatureId) {
+ this.sourceFeatureId = sourceFeatureId;
+ return this;
+ }
+
@Override
public StsAssumeRoleCredentialsProvider build() {
return super.build();
diff --git a/services/sts/src/main/java/software/amazon/awssdk/services/sts/auth/StsAssumeRoleWithSamlCredentialsProvider.java b/services/sts/src/main/java/software/amazon/awssdk/services/sts/auth/StsAssumeRoleWithSamlCredentialsProvider.java
index 6d99b555e311..4845b1b61c7b 100644
--- a/services/sts/src/main/java/software/amazon/awssdk/services/sts/auth/StsAssumeRoleWithSamlCredentialsProvider.java
+++ b/services/sts/src/main/java/software/amazon/awssdk/services/sts/auth/StsAssumeRoleWithSamlCredentialsProvider.java
@@ -25,9 +25,11 @@
import software.amazon.awssdk.annotations.ThreadSafe;
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
import software.amazon.awssdk.auth.credentials.AwsSessionCredentials;
+import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId;
import software.amazon.awssdk.services.sts.StsClient;
import software.amazon.awssdk.services.sts.model.AssumeRoleWithSamlRequest;
import software.amazon.awssdk.services.sts.model.AssumeRoleWithSamlResponse;
+import software.amazon.awssdk.utils.StringUtils;
import software.amazon.awssdk.utils.Validate;
import software.amazon.awssdk.utils.builder.ToCopyableBuilder;
@@ -48,8 +50,10 @@
public final class StsAssumeRoleWithSamlCredentialsProvider
extends StsCredentialsProvider
implements ToCopyableBuilder {
- private static final String PROVIDER_NAME = "StsAssumeRoleWithSamlCredentialsProvider";
+ private static final String PROVIDER_NAME = BusinessMetricFeatureId.CREDENTIALS_STS_ASSUME_ROLE_SAML.value();
private final Supplier assumeRoleWithSamlRequestSupplier;
+ private final String sourceFeatureId;
+ private final String providerName;
/**
@@ -60,6 +64,10 @@ private StsAssumeRoleWithSamlCredentialsProvider(Builder builder) {
Validate.notNull(builder.assumeRoleWithSamlRequestSupplier, "Assume role with SAML request must not be null.");
this.assumeRoleWithSamlRequestSupplier = builder.assumeRoleWithSamlRequestSupplier;
+ this.sourceFeatureId = builder.sourceFeatureId;
+ this.providerName = StringUtils.isEmpty(builder.sourceFeatureId)
+ ? PROVIDER_NAME
+ : builder.sourceFeatureId + "," + PROVIDER_NAME;
}
/**
@@ -75,7 +83,7 @@ protected AwsSessionCredentials getUpdatedCredentials(StsClient stsClient) {
Validate.notNull(assumeRoleWithSamlRequest, "Assume role with saml request must not be null.");
AssumeRoleWithSamlResponse assumeRoleResponse = stsClient.assumeRoleWithSAML(assumeRoleWithSamlRequest);
return fromStsCredentials(assumeRoleResponse.credentials(),
- PROVIDER_NAME,
+ providerName(),
accountIdFromArn(assumeRoleResponse.assumedRoleUser()));
}
@@ -86,7 +94,7 @@ public Builder toBuilder() {
@Override
String providerName() {
- return PROVIDER_NAME;
+ return this.providerName;
}
/**
@@ -96,6 +104,7 @@ String providerName() {
@NotThreadSafe
public static final class Builder extends BaseBuilder {
private Supplier assumeRoleWithSamlRequestSupplier;
+ private String sourceFeatureId;
private Builder() {
super(StsAssumeRoleWithSamlCredentialsProvider::new);
@@ -104,6 +113,7 @@ private Builder() {
public Builder(StsAssumeRoleWithSamlCredentialsProvider provider) {
super(StsAssumeRoleWithSamlCredentialsProvider::new, provider);
this.assumeRoleWithSamlRequestSupplier = provider.assumeRoleWithSamlRequestSupplier;
+ this.sourceFeatureId = provider.sourceFeatureId;
}
/**
@@ -138,6 +148,21 @@ public Builder refreshRequest(Consumer assume
return refreshRequest(AssumeRoleWithSamlRequest.builder().applyMutation(assumeRoleWithSamlRequest).build());
}
+ /**
+ * Configure the source of this credentials provider. This is used for business metrics tracking
+ * to identify the credential provider chain.
+ *
+ * Note: This method is primarily intended for use by AWS SDK internal components
+ * and should not be used directly by external users.
+ *
+ * @param sourceFeatureId The source identifier for business metrics tracking.
+ * @return This object for chained calls.
+ */
+ public Builder sourceFeatureId(String sourceFeatureId) {
+ this.sourceFeatureId = sourceFeatureId;
+ return this;
+ }
+
@Override
public StsAssumeRoleWithSamlCredentialsProvider build() {
return super.build();
diff --git a/services/sts/src/main/java/software/amazon/awssdk/services/sts/auth/StsAssumeRoleWithWebIdentityCredentialsProvider.java b/services/sts/src/main/java/software/amazon/awssdk/services/sts/auth/StsAssumeRoleWithWebIdentityCredentialsProvider.java
index 4cbb325f7458..1d14fe6fefe1 100644
--- a/services/sts/src/main/java/software/amazon/awssdk/services/sts/auth/StsAssumeRoleWithWebIdentityCredentialsProvider.java
+++ b/services/sts/src/main/java/software/amazon/awssdk/services/sts/auth/StsAssumeRoleWithWebIdentityCredentialsProvider.java
@@ -26,9 +26,11 @@
import software.amazon.awssdk.annotations.ThreadSafe;
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
import software.amazon.awssdk.auth.credentials.AwsSessionCredentials;
+import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId;
import software.amazon.awssdk.services.sts.StsClient;
import software.amazon.awssdk.services.sts.model.AssumeRoleWithWebIdentityRequest;
import software.amazon.awssdk.services.sts.model.AssumeRoleWithWebIdentityResponse;
+import software.amazon.awssdk.utils.StringUtils;
import software.amazon.awssdk.utils.builder.ToCopyableBuilder;
/**
@@ -49,8 +51,10 @@ public final class StsAssumeRoleWithWebIdentityCredentialsProvider
extends StsCredentialsProvider
implements ToCopyableBuilder {
- private static final String PROVIDER_NAME = "StsAssumeRoleWithWebIdentityCredentialsProvider";
+ private static final String PROVIDER_NAME = BusinessMetricFeatureId.CREDENTIALS_STS_ASSUME_ROLE_WEB_ID.value();
private final Supplier assumeRoleWithWebIdentityRequest;
+ private final String sourceFeatureId;
+ private final String providerName;
/**
* @see #builder()
@@ -60,6 +64,10 @@ private StsAssumeRoleWithWebIdentityCredentialsProvider(Builder builder) {
notNull(builder.assumeRoleWithWebIdentityRequestSupplier, "Assume role with web identity request must not be null.");
this.assumeRoleWithWebIdentityRequest = builder.assumeRoleWithWebIdentityRequestSupplier;
+ this.sourceFeatureId = builder.sourceFeatureId;
+ this.providerName = StringUtils.isEmpty(builder.sourceFeatureId)
+ ? PROVIDER_NAME
+ : builder.sourceFeatureId + "," + PROVIDER_NAME;
}
/**
@@ -75,7 +83,7 @@ protected AwsSessionCredentials getUpdatedCredentials(StsClient stsClient) {
notNull(request, "AssumeRoleWithWebIdentityRequest can't be null");
AssumeRoleWithWebIdentityResponse assumeRoleResponse = stsClient.assumeRoleWithWebIdentity(request);
return fromStsCredentials(assumeRoleResponse.credentials(),
- PROVIDER_NAME,
+ providerName(),
accountIdFromArn(assumeRoleResponse.assumedRoleUser()));
}
@@ -86,7 +94,7 @@ public Builder toBuilder() {
@Override
String providerName() {
- return PROVIDER_NAME;
+ return this.providerName;
}
/**
@@ -96,6 +104,7 @@ String providerName() {
@NotThreadSafe
public static final class Builder extends BaseBuilder {
private Supplier assumeRoleWithWebIdentityRequestSupplier;
+ private String sourceFeatureId;
private Builder() {
super(StsAssumeRoleWithWebIdentityCredentialsProvider::new);
@@ -104,6 +113,7 @@ private Builder() {
public Builder(StsAssumeRoleWithWebIdentityCredentialsProvider provider) {
super(StsAssumeRoleWithWebIdentityCredentialsProvider::new, provider);
this.assumeRoleWithWebIdentityRequestSupplier = provider.assumeRoleWithWebIdentityRequest;
+ this.sourceFeatureId = provider.sourceFeatureId;
}
/**
@@ -139,6 +149,21 @@ public Builder refreshRequest(Consumer
.build());
}
+ /**
+ * Configure the source of this credentials provider. This is used for business metrics tracking
+ * to identify the credential provider chain.
+ *
+ * Note: This method is primarily intended for use by AWS SDK internal components
+ * and should not be used directly by external users.
+ *
+ * @param sourceFeatureId The source identifier for business metrics tracking.
+ * @return This object for chained calls.
+ */
+ public Builder sourceFeatureId(String sourceFeatureId) {
+ this.sourceFeatureId = sourceFeatureId;
+ return this;
+ }
+
@Override
public StsAssumeRoleWithWebIdentityCredentialsProvider build() {
return super.build();
diff --git a/services/sts/src/main/java/software/amazon/awssdk/services/sts/auth/StsGetFederationTokenCredentialsProvider.java b/services/sts/src/main/java/software/amazon/awssdk/services/sts/auth/StsGetFederationTokenCredentialsProvider.java
index da28815b686e..b0f78cdbbba9 100644
--- a/services/sts/src/main/java/software/amazon/awssdk/services/sts/auth/StsGetFederationTokenCredentialsProvider.java
+++ b/services/sts/src/main/java/software/amazon/awssdk/services/sts/auth/StsGetFederationTokenCredentialsProvider.java
@@ -23,11 +23,13 @@
import software.amazon.awssdk.annotations.ThreadSafe;
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
import software.amazon.awssdk.auth.credentials.AwsSessionCredentials;
+import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId;
import software.amazon.awssdk.services.sts.StsClient;
import software.amazon.awssdk.services.sts.endpoints.internal.Arn;
import software.amazon.awssdk.services.sts.model.FederatedUser;
import software.amazon.awssdk.services.sts.model.GetFederationTokenRequest;
import software.amazon.awssdk.services.sts.model.GetFederationTokenResponse;
+import software.amazon.awssdk.utils.StringUtils;
import software.amazon.awssdk.utils.Validate;
import software.amazon.awssdk.utils.builder.ToCopyableBuilder;
@@ -48,9 +50,11 @@
public class StsGetFederationTokenCredentialsProvider
extends StsCredentialsProvider
implements ToCopyableBuilder {
- private static final String PROVIDER_NAME = "StsGetFederationTokenCredentialsProvider";
+ private static final String PROVIDER_NAME = BusinessMetricFeatureId.CREDENTIALS_STS_FEDERATION_TOKEN.value();
private final GetFederationTokenRequest getFederationTokenRequest;
+ private final String sourceFeatureId;
+ private final String providerName;
/**
* @see #builder()
@@ -60,6 +64,10 @@ private StsGetFederationTokenCredentialsProvider(Builder builder) {
Validate.notNull(builder.getFederationTokenRequest, "Get session token request must not be null.");
this.getFederationTokenRequest = builder.getFederationTokenRequest;
+ this.sourceFeatureId = builder.sourceFeatureId;
+ this.providerName = StringUtils.isEmpty(builder.sourceFeatureId)
+ ? PROVIDER_NAME
+ : builder.sourceFeatureId + "," + PROVIDER_NAME;
}
/**
@@ -73,7 +81,7 @@ public static Builder builder() {
protected AwsSessionCredentials getUpdatedCredentials(StsClient stsClient) {
GetFederationTokenResponse federationToken = stsClient.getFederationToken(getFederationTokenRequest);
return fromStsCredentials(federationToken.credentials(),
- PROVIDER_NAME,
+ providerName(),
accountIdFromArn(federationToken.federatedUser()));
}
@@ -93,7 +101,7 @@ public Builder toBuilder() {
@Override
String providerName() {
- return PROVIDER_NAME;
+ return this.providerName;
}
/**
@@ -103,6 +111,7 @@ String providerName() {
@NotThreadSafe
public static final class Builder extends BaseBuilder {
private GetFederationTokenRequest getFederationTokenRequest;
+ private String sourceFeatureId;
private Builder() {
super(StsGetFederationTokenCredentialsProvider::new);
@@ -111,6 +120,7 @@ private Builder() {
public Builder(StsGetFederationTokenCredentialsProvider provider) {
super(StsGetFederationTokenCredentialsProvider::new, provider);
this.getFederationTokenRequest = provider.getFederationTokenRequest;
+ this.sourceFeatureId = provider.sourceFeatureId;
}
/**
@@ -134,6 +144,21 @@ public Builder refreshRequest(Consumer getFed
return refreshRequest(GetFederationTokenRequest.builder().applyMutation(getFederationTokenRequest).build());
}
+ /**
+ * Configure the source of this credentials provider. This is used for business metrics tracking
+ * to identify the credential provider chain.
+ *
+ * Note: This method is primarily intended for use by AWS SDK internal components
+ * and should not be used directly by external users.
+ *
+ * @param sourceFeatureId The source identifier for business metrics tracking.
+ * @return This object for chained calls.
+ */
+ public Builder sourceFeatureId(String sourceFeatureId) {
+ this.sourceFeatureId = sourceFeatureId;
+ return this;
+ }
+
@Override
public StsGetFederationTokenCredentialsProvider build() {
return super.build();
diff --git a/services/sts/src/main/java/software/amazon/awssdk/services/sts/auth/StsGetSessionTokenCredentialsProvider.java b/services/sts/src/main/java/software/amazon/awssdk/services/sts/auth/StsGetSessionTokenCredentialsProvider.java
index 8ca66114d2be..1c31532ba5c4 100644
--- a/services/sts/src/main/java/software/amazon/awssdk/services/sts/auth/StsGetSessionTokenCredentialsProvider.java
+++ b/services/sts/src/main/java/software/amazon/awssdk/services/sts/auth/StsGetSessionTokenCredentialsProvider.java
@@ -23,9 +23,11 @@
import software.amazon.awssdk.annotations.ThreadSafe;
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
import software.amazon.awssdk.auth.credentials.AwsSessionCredentials;
+import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId;
import software.amazon.awssdk.services.sts.StsClient;
import software.amazon.awssdk.services.sts.model.GetSessionTokenRequest;
import software.amazon.awssdk.services.sts.model.GetSessionTokenResponse;
+import software.amazon.awssdk.utils.StringUtils;
import software.amazon.awssdk.utils.Validate;
import software.amazon.awssdk.utils.builder.ToCopyableBuilder;
@@ -46,9 +48,11 @@
public class StsGetSessionTokenCredentialsProvider
extends StsCredentialsProvider
implements ToCopyableBuilder {
- private static final String PROVIDER_NAME = "StsGetSessionTokenCredentialsProvider";
+ private static final String PROVIDER_NAME = BusinessMetricFeatureId.CREDENTIALS_STS_SESSION_TOKEN.value();
private final GetSessionTokenRequest getSessionTokenRequest;
+ private final String sourceFeatureId;
+ private final String providerName;
/**
* @see #builder()
@@ -58,6 +62,10 @@ private StsGetSessionTokenCredentialsProvider(Builder builder) {
Validate.notNull(builder.getSessionTokenRequest, "Get session token request must not be null.");
this.getSessionTokenRequest = builder.getSessionTokenRequest;
+ this.sourceFeatureId = builder.sourceFeatureId;
+ this.providerName = StringUtils.isEmpty(builder.sourceFeatureId)
+ ? PROVIDER_NAME
+ : builder.sourceFeatureId + "," + PROVIDER_NAME;
}
/**
@@ -70,7 +78,7 @@ public static Builder builder() {
@Override
protected AwsSessionCredentials getUpdatedCredentials(StsClient stsClient) {
GetSessionTokenResponse sessionToken = stsClient.getSessionToken(getSessionTokenRequest);
- return fromStsCredentials(sessionToken.credentials(), PROVIDER_NAME);
+ return fromStsCredentials(sessionToken.credentials(), providerName());
}
@Override
@@ -80,7 +88,7 @@ public Builder toBuilder() {
@Override
String providerName() {
- return PROVIDER_NAME;
+ return this.providerName;
}
/**
@@ -90,6 +98,7 @@ String providerName() {
@NotThreadSafe
public static final class Builder extends BaseBuilder {
private GetSessionTokenRequest getSessionTokenRequest = GetSessionTokenRequest.builder().build();
+ private String sourceFeatureId;
private Builder() {
super(StsGetSessionTokenCredentialsProvider::new);
@@ -98,6 +107,7 @@ private Builder() {
public Builder(StsGetSessionTokenCredentialsProvider provider) {
super(StsGetSessionTokenCredentialsProvider::new, provider);
this.getSessionTokenRequest = provider.getSessionTokenRequest;
+ this.sourceFeatureId = provider.sourceFeatureId;
}
/**
@@ -122,6 +132,21 @@ public Builder refreshRequest(GetSessionTokenRequest getSessionTokenRequest) {
public Builder refreshRequest(Consumer getFederationTokenRequest) {
return refreshRequest(GetSessionTokenRequest.builder().applyMutation(getFederationTokenRequest).build());
}
+
+ /**
+ * Configure the source of this credentials provider. This is used for business metrics tracking
+ * to identify the credential provider chain.
+ *
+ * Note: This method is primarily intended for use by AWS SDK internal components
+ * and should not be used directly by external users.
+ *
+ * @param sourceFeatureId The source identifier for business metrics tracking.
+ * @return This object for chained calls.
+ */
+ public Builder sourceFeatureId(String sourceFeatureId) {
+ this.sourceFeatureId = sourceFeatureId;
+ return this;
+ }
@Override
public StsGetSessionTokenCredentialsProvider build() {
diff --git a/services/sts/src/main/java/software/amazon/awssdk/services/sts/auth/StsWebIdentityTokenFileCredentialsProvider.java b/services/sts/src/main/java/software/amazon/awssdk/services/sts/auth/StsWebIdentityTokenFileCredentialsProvider.java
index c812da56e21e..c4ca16469e8b 100644
--- a/services/sts/src/main/java/software/amazon/awssdk/services/sts/auth/StsWebIdentityTokenFileCredentialsProvider.java
+++ b/services/sts/src/main/java/software/amazon/awssdk/services/sts/auth/StsWebIdentityTokenFileCredentialsProvider.java
@@ -22,6 +22,7 @@
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Supplier;
import software.amazon.awssdk.annotations.SdkPublicApi;
@@ -30,6 +31,7 @@
import software.amazon.awssdk.auth.credentials.AwsSessionCredentials;
import software.amazon.awssdk.auth.credentials.internal.WebIdentityTokenCredentialProperties;
import software.amazon.awssdk.core.SdkSystemSetting;
+import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId;
import software.amazon.awssdk.services.sts.StsClient;
import software.amazon.awssdk.services.sts.internal.AssumeRoleWithWebIdentityRequestSupplier;
import software.amazon.awssdk.services.sts.model.AssumeRoleWithWebIdentityRequest;
@@ -56,7 +58,7 @@
public final class StsWebIdentityTokenFileCredentialsProvider
extends StsCredentialsProvider
implements ToCopyableBuilder {
- private static final String PROVIDER_NAME = "StsWebIdentityTokenFileCredentialsProvider";
+ private static final String PROVIDER_NAME = BusinessMetricFeatureId.CREDENTIALS_ENV_VARS_STS_WEB_ID_TOKEN.value();
private final AwsCredentialsProvider credentialsProvider;
private final RuntimeException loadException;
@@ -132,7 +134,16 @@ public AwsCredentials resolveCredentials() {
if (loadException != null) {
throw loadException;
}
- return credentialsProvider.resolveCredentials();
+ AwsCredentials awsCredentials = credentialsProvider.resolveCredentials();
+ if (awsCredentials instanceof AwsSessionCredentials) {
+ AwsSessionCredentials sessionCredentials = (AwsSessionCredentials) awsCredentials;
+ Optional providerName = awsCredentials.providerName();
+ if (providerName.isPresent() && !providerName.get().isEmpty()) {
+ return sessionCredentials.copy(s -> s.providerName(providerName.get() + "," + PROVIDER_NAME));
+ }
+ return sessionCredentials.copy(s -> s.providerName(PROVIDER_NAME));
+ }
+ return awsCredentials;
}
@Override
@@ -303,4 +314,4 @@ public StsWebIdentityTokenFileCredentialsProvider build() {
}
}
-}
\ No newline at end of file
+}
diff --git a/services/sts/src/main/java/software/amazon/awssdk/services/sts/internal/AssumeRoleWithWebIdentityRequestSupplier.java b/services/sts/src/main/java/software/amazon/awssdk/services/sts/internal/AssumeRoleWithWebIdentityRequestSupplier.java
index 03b91890af8a..696658493f08 100644
--- a/services/sts/src/main/java/software/amazon/awssdk/services/sts/internal/AssumeRoleWithWebIdentityRequestSupplier.java
+++ b/services/sts/src/main/java/software/amazon/awssdk/services/sts/internal/AssumeRoleWithWebIdentityRequestSupplier.java
@@ -20,6 +20,7 @@
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.util.Optional;
import java.util.function.Supplier;
import software.amazon.awssdk.annotations.SdkInternalApi;
import software.amazon.awssdk.services.sts.model.AssumeRoleWithWebIdentityRequest;
@@ -31,12 +32,13 @@ public class AssumeRoleWithWebIdentityRequestSupplier implements Supplier sourceFeatureId() {
+ return Optional.ofNullable(sourceFeatureId);
+ }
+
//file extraction
private String getToken(Path file) {
try (InputStream webIdentityTokenStream = Files.newInputStream(file)) {
@@ -63,6 +69,7 @@ public static class Builder {
private Path webIdentityTokenFile;
+ private String sourceFeatureId;
public Builder assumeRoleWithWebIdentityRequest(AssumeRoleWithWebIdentityRequest request) {
this.request = request;
@@ -78,6 +85,11 @@ public AssumeRoleWithWebIdentityRequestSupplier build() {
return new AssumeRoleWithWebIdentityRequestSupplier(this);
}
+ public Builder sourceFeatureId(String sourceFeatureId) {
+ this.sourceFeatureId = sourceFeatureId;
+ return this;
+ }
+
}
-}
\ No newline at end of file
+}
diff --git a/services/sts/src/main/java/software/amazon/awssdk/services/sts/internal/StsProfileCredentialsProviderFactory.java b/services/sts/src/main/java/software/amazon/awssdk/services/sts/internal/StsProfileCredentialsProviderFactory.java
index 4e5559e73680..d50e6155f488 100644
--- a/services/sts/src/main/java/software/amazon/awssdk/services/sts/internal/StsProfileCredentialsProviderFactory.java
+++ b/services/sts/src/main/java/software/amazon/awssdk/services/sts/internal/StsProfileCredentialsProviderFactory.java
@@ -41,21 +41,23 @@ public final class StsProfileCredentialsProviderFactory implements ChildProfileC
+ "'%s' profile.";
@Override
- public AwsCredentialsProvider create(AwsCredentialsProvider sourceCredentialsProvider, Profile profile) {
- return new StsProfileCredentialsProvider(sourceCredentialsProvider, profile);
+ public AwsCredentialsProvider create(ChildProfileCredentialsRequest request) {
+ return new StsProfileCredentialsProvider(request.sourceCredentialsProvider(), request.profile(),
+ request.sourceFeatureId());
}
/**
* A wrapper for a {@link StsAssumeRoleCredentialsProvider} that is returned by this factory when
- * {@link #create(AwsCredentialsProvider, Profile)} is invoked. This wrapper is important because it ensures the parent
- * credentials provider is closed when the assume-role credentials provider is no longer needed.
+ * {@link #create(ChildProfileCredentialsRequest)} is invoked. This wrapper is important because it ensures the
+ * parent credentials provider is closed when the assume-role credentials provider is no longer needed.
*/
private static final class StsProfileCredentialsProvider implements AwsCredentialsProvider, SdkAutoCloseable {
private final StsClient stsClient;
private final AwsCredentialsProvider parentCredentialsProvider;
private final StsAssumeRoleCredentialsProvider credentialsProvider;
- private StsProfileCredentialsProvider(AwsCredentialsProvider parentCredentialsProvider, Profile profile) {
+ private StsProfileCredentialsProvider(AwsCredentialsProvider parentCredentialsProvider, Profile profile,
+ String sourceFeatureId) {
String roleArn = requireProperty(profile, ProfileProperty.ROLE_ARN);
String roleSessionName = profile.property(ProfileProperty.ROLE_SESSION_NAME)
.orElseGet(() -> "aws-sdk-java-" + System.currentTimeMillis());
@@ -76,6 +78,7 @@ private StsProfileCredentialsProvider(AwsCredentialsProvider parentCredentialsPr
this.credentialsProvider = StsAssumeRoleCredentialsProvider.builder()
.stsClient(stsClient)
.refreshRequest(assumeRoleRequest)
+ .sourceFeatureId(sourceFeatureId)
.build();
}
diff --git a/services/sts/src/main/java/software/amazon/awssdk/services/sts/internal/StsWebIdentityCredentialsProviderFactory.java b/services/sts/src/main/java/software/amazon/awssdk/services/sts/internal/StsWebIdentityCredentialsProviderFactory.java
index 86340d4f857d..f1ffad1950cd 100644
--- a/services/sts/src/main/java/software/amazon/awssdk/services/sts/internal/StsWebIdentityCredentialsProviderFactory.java
+++ b/services/sts/src/main/java/software/amazon/awssdk/services/sts/internal/StsWebIdentityCredentialsProviderFactory.java
@@ -87,13 +87,15 @@ private StsWebIdentityCredentialsProvider(WebIdentityTokenCredentialProperties c
AssumeRoleWithWebIdentityRequestSupplier.builder()
.assumeRoleWithWebIdentityRequest(requestBuilder.build())
.webIdentityTokenFile(credentialProperties.webIdentityTokenFile())
+ .sourceFeatureId(credentialProperties.sourceFeatureId())
.build();
StsAssumeRoleWithWebIdentityCredentialsProvider.Builder builder =
StsAssumeRoleWithWebIdentityCredentialsProvider.builder()
.asyncCredentialUpdateEnabled(asyncCredentialUpdateEnabled)
.stsClient(stsClient)
- .refreshRequest(supplier);
+ .refreshRequest(supplier)
+ .sourceFeatureId(credentialProperties.sourceFeatureId());
if (credentialProperties.prefetchTime() != null) {
builder.prefetchTime(credentialProperties.prefetchTime());
diff --git a/services/sts/src/test/java/software/amazon/awssdk/services/sts/auth/StsAssumeRoleCredentialsProviderTest.java b/services/sts/src/test/java/software/amazon/awssdk/services/sts/auth/StsAssumeRoleCredentialsProviderTest.java
index e4d7b6c6bc5c..b36cd6e67613 100644
--- a/services/sts/src/test/java/software/amazon/awssdk/services/sts/auth/StsAssumeRoleCredentialsProviderTest.java
+++ b/services/sts/src/test/java/software/amazon/awssdk/services/sts/auth/StsAssumeRoleCredentialsProviderTest.java
@@ -15,6 +15,7 @@
package software.amazon.awssdk.services.sts.auth;
+import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId;
import software.amazon.awssdk.services.sts.StsClient;
import software.amazon.awssdk.services.sts.model.AssumeRoleRequest;
import software.amazon.awssdk.services.sts.model.AssumeRoleResponse;
@@ -51,6 +52,6 @@ protected AssumeRoleResponse callClient(StsClient client, AssumeRoleRequest requ
@Override
protected String providerName() {
- return "StsAssumeRoleCredentialsProvider";
+ return BusinessMetricFeatureId.CREDENTIALS_STS_ASSUME_ROLE.value();
}
}
diff --git a/services/sts/src/test/java/software/amazon/awssdk/services/sts/auth/StsAssumeRoleWithSamlCredentialsProviderTest.java b/services/sts/src/test/java/software/amazon/awssdk/services/sts/auth/StsAssumeRoleWithSamlCredentialsProviderTest.java
index fb4729f98f79..34c503ac37da 100644
--- a/services/sts/src/test/java/software/amazon/awssdk/services/sts/auth/StsAssumeRoleWithSamlCredentialsProviderTest.java
+++ b/services/sts/src/test/java/software/amazon/awssdk/services/sts/auth/StsAssumeRoleWithSamlCredentialsProviderTest.java
@@ -15,6 +15,7 @@
package software.amazon.awssdk.services.sts.auth;
+import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId;
import software.amazon.awssdk.services.sts.StsClient;
import software.amazon.awssdk.services.sts.auth.StsAssumeRoleWithSamlCredentialsProvider.Builder;
import software.amazon.awssdk.services.sts.model.AssumeRoleWithSamlRequest;
@@ -54,6 +55,6 @@ protected AssumeRoleWithSamlResponse callClient(StsClient client, AssumeRoleWith
@Override
protected String providerName() {
- return "StsAssumeRoleWithSamlCredentialsProvider";
+ return BusinessMetricFeatureId.CREDENTIALS_STS_ASSUME_ROLE_SAML.value();
}
}
diff --git a/services/sts/src/test/java/software/amazon/awssdk/services/sts/auth/StsAssumeRoleWithWebIdentityCredentialsProviderTest.java b/services/sts/src/test/java/software/amazon/awssdk/services/sts/auth/StsAssumeRoleWithWebIdentityCredentialsProviderTest.java
index d037597897a2..8f1e1c4808c3 100644
--- a/services/sts/src/test/java/software/amazon/awssdk/services/sts/auth/StsAssumeRoleWithWebIdentityCredentialsProviderTest.java
+++ b/services/sts/src/test/java/software/amazon/awssdk/services/sts/auth/StsAssumeRoleWithWebIdentityCredentialsProviderTest.java
@@ -15,6 +15,7 @@
package software.amazon.awssdk.services.sts.auth;
+import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId;
import software.amazon.awssdk.services.sts.StsClient;
import software.amazon.awssdk.services.sts.auth.StsAssumeRoleWithWebIdentityCredentialsProvider.Builder;
import software.amazon.awssdk.services.sts.model.AssumeRoleWithWebIdentityRequest;
@@ -53,6 +54,6 @@ protected AssumeRoleWithWebIdentityResponse callClient(StsClient client, AssumeR
@Override
protected String providerName() {
- return "StsAssumeRoleWithWebIdentityCredentialsProvider";
+ return BusinessMetricFeatureId.CREDENTIALS_STS_ASSUME_ROLE_WEB_ID.value();
}
}
diff --git a/services/sts/src/test/java/software/amazon/awssdk/services/sts/auth/StsGetFederationTokenCredentialsProviderTest.java b/services/sts/src/test/java/software/amazon/awssdk/services/sts/auth/StsGetFederationTokenCredentialsProviderTest.java
index bdc50a817aaa..b5154f646ff6 100644
--- a/services/sts/src/test/java/software/amazon/awssdk/services/sts/auth/StsGetFederationTokenCredentialsProviderTest.java
+++ b/services/sts/src/test/java/software/amazon/awssdk/services/sts/auth/StsGetFederationTokenCredentialsProviderTest.java
@@ -15,6 +15,7 @@
package software.amazon.awssdk.services.sts.auth;
+import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId;
import software.amazon.awssdk.services.sts.StsClient;
import software.amazon.awssdk.services.sts.auth.StsGetFederationTokenCredentialsProvider.Builder;
import software.amazon.awssdk.services.sts.model.AssumedRoleUser;
@@ -54,6 +55,6 @@ protected GetFederationTokenResponse callClient(StsClient client, GetFederationT
@Override
protected String providerName() {
- return "StsGetFederationTokenCredentialsProvider";
+ return BusinessMetricFeatureId.CREDENTIALS_STS_FEDERATION_TOKEN.value();
}
}
diff --git a/services/sts/src/test/java/software/amazon/awssdk/services/sts/auth/StsGetSessionTokenCredentialsProviderTest.java b/services/sts/src/test/java/software/amazon/awssdk/services/sts/auth/StsGetSessionTokenCredentialsProviderTest.java
index 18f9feadf796..1ab263152602 100644
--- a/services/sts/src/test/java/software/amazon/awssdk/services/sts/auth/StsGetSessionTokenCredentialsProviderTest.java
+++ b/services/sts/src/test/java/software/amazon/awssdk/services/sts/auth/StsGetSessionTokenCredentialsProviderTest.java
@@ -15,6 +15,7 @@
package software.amazon.awssdk.services.sts.auth;
+import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId;
import software.amazon.awssdk.services.sts.StsClient;
import software.amazon.awssdk.services.sts.auth.StsGetSessionTokenCredentialsProvider.Builder;
import software.amazon.awssdk.services.sts.model.AssumedRoleUser;
@@ -52,6 +53,6 @@ protected GetSessionTokenResponse callClient(StsClient client, GetSessionTokenRe
@Override
protected String providerName() {
- return "StsGetSessionTokenCredentialsProvider";
+ return BusinessMetricFeatureId.CREDENTIALS_STS_SESSION_TOKEN.value();
}
}
diff --git a/services/sts/src/test/java/software/amazon/awssdk/services/sts/auth/StsWebIdentityTokenCredentialsProviderBaseTest.java b/services/sts/src/test/java/software/amazon/awssdk/services/sts/auth/StsWebIdentityTokenCredentialsProviderBaseTest.java
index cb3ca75140bf..7d64f194edde 100644
--- a/services/sts/src/test/java/software/amazon/awssdk/services/sts/auth/StsWebIdentityTokenCredentialsProviderBaseTest.java
+++ b/services/sts/src/test/java/software/amazon/awssdk/services/sts/auth/StsWebIdentityTokenCredentialsProviderBaseTest.java
@@ -25,6 +25,7 @@
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import software.amazon.awssdk.core.SdkSystemSetting;
+import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId;
import software.amazon.awssdk.services.sts.StsClient;
import software.amazon.awssdk.services.sts.auth.StsWebIdentityTokenFileCredentialsProvider.Builder;
import software.amazon.awssdk.services.sts.model.AssumeRoleWithWebIdentityRequest;
@@ -83,7 +84,8 @@ protected AssumeRoleWithWebIdentityResponse callClient(StsClient client, AssumeR
@Override
protected String providerName() {
- return "StsAssumeRoleWithWebIdentityCredentialsProvider";
+ return String.format("%s,%s", BusinessMetricFeatureId.CREDENTIALS_STS_ASSUME_ROLE_WEB_ID,
+ BusinessMetricFeatureId.CREDENTIALS_ENV_VARS_STS_WEB_ID_TOKEN.value());
}
private String getToken(Path file) {
diff --git a/test/auth-tests/pom.xml b/test/auth-tests/pom.xml
index 869dd1b8b4a8..0d7ab0774505 100644
--- a/test/auth-tests/pom.xml
+++ b/test/auth-tests/pom.xml
@@ -65,6 +65,12 @@
${awsjavasdk.version}
test
+
+ software.amazon.awssdk
+ regions
+ ${awsjavasdk.version}
+ test
+
software.amazon.awssdk
ssooidc
@@ -141,6 +147,10 @@
log4j-slf4j-impl
test
+
+ software.amazon.awssdk
+ test-utils
+
diff --git a/test/auth-tests/src/it/java/software/amazon/awssdk/auth/source/ContainerCredentialsProviderUserAgentTest.java b/test/auth-tests/src/it/java/software/amazon/awssdk/auth/source/ContainerCredentialsProviderUserAgentTest.java
new file mode 100644
index 000000000000..f64591fefd5b
--- /dev/null
+++ b/test/auth-tests/src/it/java/software/amazon/awssdk/auth/source/ContainerCredentialsProviderUserAgentTest.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package software.amazon.awssdk.auth.source;
+
+import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
+import static com.github.tomakehurst.wiremock.client.WireMock.get;
+import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo;
+import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import com.github.tomakehurst.wiremock.junit5.WireMockExtension;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.List;
+import java.util.stream.Stream;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.extension.RegisterExtension;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import software.amazon.awssdk.auth.credentials.ContainerCredentialsProvider;
+import software.amazon.awssdk.core.SdkSystemSetting;
+import software.amazon.awssdk.http.AbortableInputStream;
+import software.amazon.awssdk.http.HttpExecuteResponse;
+import software.amazon.awssdk.http.SdkHttpClient;
+import software.amazon.awssdk.http.SdkHttpRequest;
+import software.amazon.awssdk.http.SdkHttpResponse;
+import software.amazon.awssdk.identity.spi.AwsCredentialsIdentity;
+import software.amazon.awssdk.identity.spi.IdentityProvider;
+import software.amazon.awssdk.services.sts.StsClient;
+import software.amazon.awssdk.testutils.service.http.MockSyncHttpClient;
+import software.amazon.awssdk.utils.DateUtils;
+import software.amazon.awssdk.utils.StringInputStream;
+
+/**
+ * Test class to verify that ContainerCredentialsProvider correctly includes
+ * business metrics in the User-Agent header. This test focuses specifically on the
+ * CREDENTIALS_HTTP ("z") business metric feature ID.
+ */
+class ContainerCredentialsProviderUserAgentTest {
+ private static final String CONTAINER_CREDENTIALS_PATH = "/v2/credentials/test-role-arn";
+ private static final String CONTAINER_SERVICE_ENDPOINT = "http://localhost:";
+
+ private MockSyncHttpClient mockHttpClient;
+
+ @RegisterExtension
+ static WireMockExtension wireMockServer = WireMockExtension.newInstance()
+ .options(wireMockConfig().dynamicPort())
+ .configureStaticDsl(true)
+ .build();
+
+ @BeforeEach
+ public void setup() {
+
+ System.setProperty(SdkSystemSetting.AWS_CONTAINER_SERVICE_ENDPOINT.property(),
+ CONTAINER_SERVICE_ENDPOINT + wireMockServer.getPort());
+ System.setProperty(SdkSystemSetting.AWS_CONTAINER_CREDENTIALS_RELATIVE_URI.property(),
+ CONTAINER_CREDENTIALS_PATH);
+
+ mockHttpClient = new MockSyncHttpClient();
+ mockHttpClient.stubNextResponse(mockStsResponse());
+
+ stubContainerCredentialsResponses();
+ }
+
+ @AfterAll
+ public static void teardown() {
+ System.clearProperty(SdkSystemSetting.AWS_CONTAINER_SERVICE_ENDPOINT.property());
+ System.clearProperty(SdkSystemSetting.AWS_CONTAINER_CREDENTIALS_RELATIVE_URI.property());
+ System.clearProperty(SdkSystemSetting.AWS_CONTAINER_AUTHORIZATION_TOKEN.property());
+ }
+
+ private static HttpExecuteResponse mockStsResponse() {
+ return HttpExecuteResponse.builder()
+ .response(SdkHttpResponse.builder().statusCode(200).build())
+ .responseBody(AbortableInputStream.create(new StringInputStream("")))
+ .build();
+ }
+
+ private void stubContainerCredentialsResponses() {
+ String credentialsResponse = createCredentialsResponse("ACCESS_KEY_ID", "SECRET_ACCESS_KEY", null);
+ wireMockServer.stubFor(get(urlPathEqualTo(CONTAINER_CREDENTIALS_PATH))
+ .willReturn(aResponse().withBody(credentialsResponse)));
+ }
+
+ private void stubContainerCredentialsResponsesWithSessionToken() {
+ String credentialsResponse = createCredentialsResponse("ACCESS_KEY_ID", "SECRET_ACCESS_KEY", "SESSION_TOKEN");
+ wireMockServer.stubFor(get(urlPathEqualTo(CONTAINER_CREDENTIALS_PATH))
+ .willReturn(aResponse().withBody(credentialsResponse)));
+ }
+
+ private void stubContainerCredentialsResponsesWithAuthToken() {
+ System.setProperty(SdkSystemSetting.AWS_CONTAINER_AUTHORIZATION_TOKEN.property(), "test-auth-token");
+
+ String credentialsResponse = createCredentialsResponse("ACCESS_KEY_ID", "SECRET_ACCESS_KEY", null);
+ wireMockServer.stubFor(get(urlPathEqualTo(CONTAINER_CREDENTIALS_PATH))
+ .willReturn(aResponse().withBody(credentialsResponse)));
+ }
+
+ private String createCredentialsResponse(String accessKeyId, String secretAccessKey, String sessionToken) {
+ StringBuilder response = new StringBuilder();
+ response.append("{");
+ response.append("\"AccessKeyId\":\"").append(accessKeyId).append("\",");
+ response.append("\"SecretAccessKey\":\"").append(secretAccessKey).append("\",");
+ if (sessionToken != null) {
+ response.append("\"Token\":\"").append(sessionToken).append("\",");
+ }
+ response.append("\"Expiration\":\"").append(DateUtils.formatIso8601Date(Instant.now().plus(Duration.ofHours(1)))).append("\"");
+ response.append("}");
+ return response.toString();
+ }
+
+ @ParameterizedTest
+ @MethodSource("containerCredentialProviders")
+ void userAgentString_containsContainerBusinessMetric_WhenUsingContainerCredentials(
+ IdentityProvider extends AwsCredentialsIdentity> provider, String expected) throws Exception {
+
+ stsClient(provider, mockHttpClient).getCallerIdentity();
+
+ SdkHttpRequest lastRequest = mockHttpClient.getLastRequest();
+ assertThat(lastRequest).isNotNull();
+
+ List userAgentHeaders = lastRequest.headers().get("User-Agent");
+ assertThat(userAgentHeaders).isNotNull().hasSize(1);
+ assertThat(userAgentHeaders.get(0)).contains(expected);
+ }
+
+ private static Stream containerCredentialProviders() {
+ return Stream.of(
+ Arguments.of(ContainerCredentialsProvider.create(), "m/D,z"),
+
+ Arguments.of(ContainerCredentialsProvider.builder()
+ .endpoint(CONTAINER_SERVICE_ENDPOINT + wireMockServer.getPort())
+ .build(), "m/D,z")
+ );
+ }
+
+ @ParameterizedTest
+ @MethodSource("containerCredentialProvidersWithSessionToken")
+ void userAgentString_containsContainerBusinessMetric_WhenUsingContainerCredentialsWithSessionToken(
+ IdentityProvider extends AwsCredentialsIdentity> provider, String expected) throws Exception {
+
+ stubContainerCredentialsResponsesWithSessionToken();
+
+ stsClient(provider, mockHttpClient).getCallerIdentity();
+
+ SdkHttpRequest lastRequest = mockHttpClient.getLastRequest();
+ assertThat(lastRequest).isNotNull();
+
+ List userAgentHeaders = lastRequest.headers().get("User-Agent");
+ assertThat(userAgentHeaders).isNotNull().hasSize(1);
+ assertThat(userAgentHeaders.get(0)).contains(expected);
+ }
+
+ private static Stream containerCredentialProvidersWithSessionToken() {
+ return Stream.of(
+ Arguments.of(ContainerCredentialsProvider.create(), "m/D,z")
+ );
+ }
+
+ @ParameterizedTest
+ @MethodSource("containerCredentialProvidersWithAuthToken")
+ void userAgentString_containsContainerBusinessMetric_WhenUsingContainerCredentialsWithAuthToken(
+ IdentityProvider extends AwsCredentialsIdentity> provider, String expected) throws Exception {
+
+ stubContainerCredentialsResponsesWithAuthToken();
+
+ stsClient(provider, mockHttpClient).getCallerIdentity();
+
+ SdkHttpRequest lastRequest = mockHttpClient.getLastRequest();
+ assertThat(lastRequest).isNotNull();
+
+ List userAgentHeaders = lastRequest.headers().get("User-Agent");
+ assertThat(userAgentHeaders).isNotNull().hasSize(1);
+ assertThat(userAgentHeaders.get(0)).contains(expected);
+ }
+
+ private static Stream containerCredentialProvidersWithAuthToken() {
+ return Stream.of(
+ Arguments.of(ContainerCredentialsProvider.create(), "m/D,z")
+ );
+ }
+
+ private static StsClient stsClient(IdentityProvider extends AwsCredentialsIdentity> provider, SdkHttpClient httpClient) {
+ return StsClient.builder()
+ .credentialsProvider(provider)
+ .httpClient(httpClient)
+ .build();
+ }
+}
diff --git a/test/auth-tests/src/it/java/software/amazon/awssdk/auth/source/EnvironmentVariableCredentialsProviderUserAgentTest.java b/test/auth-tests/src/it/java/software/amazon/awssdk/auth/source/EnvironmentVariableCredentialsProviderUserAgentTest.java
new file mode 100644
index 000000000000..8c171a3ed57a
--- /dev/null
+++ b/test/auth-tests/src/it/java/software/amazon/awssdk/auth/source/EnvironmentVariableCredentialsProviderUserAgentTest.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package software.amazon.awssdk.auth.source;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.List;
+import java.util.stream.Stream;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import software.amazon.awssdk.auth.credentials.EnvironmentVariableCredentialsProvider;
+import software.amazon.awssdk.core.SdkSystemSetting;
+import software.amazon.awssdk.http.AbortableInputStream;
+import software.amazon.awssdk.http.HttpExecuteResponse;
+import software.amazon.awssdk.http.SdkHttpClient;
+import software.amazon.awssdk.http.SdkHttpRequest;
+import software.amazon.awssdk.http.SdkHttpResponse;
+import software.amazon.awssdk.identity.spi.AwsCredentialsIdentity;
+import software.amazon.awssdk.identity.spi.IdentityProvider;
+import software.amazon.awssdk.services.sts.StsClient;
+import software.amazon.awssdk.testutils.service.http.MockSyncHttpClient;
+import software.amazon.awssdk.utils.StringInputStream;
+import software.amazon.awssdk.testutils.EnvironmentVariableHelper;
+
+/**
+ * Test class to verify that EnvironmentVariableCredentialsProvider correctly includes
+ * business metrics in the User-Agent header. This test focuses specifically on the
+ * CREDENTIALS_ENV_VARS ("g") business metric feature ID.
+ */
+class EnvironmentVariableCredentialsProviderUserAgentTest {
+
+ private MockSyncHttpClient mockHttpClient;
+ private static final EnvironmentVariableHelper ENVIRONMENT_VARIABLE_HELPER = new EnvironmentVariableHelper();
+
+ @BeforeEach
+ public void setup() {
+
+ // Configure environment variable credentials
+ System.setProperty(SdkSystemSetting.AWS_ACCESS_KEY_ID.property(), "test-access-key");
+ System.setProperty(SdkSystemSetting.AWS_SECRET_ACCESS_KEY.property(), "test-secret-key");
+ ENVIRONMENT_VARIABLE_HELPER.set(SdkSystemSetting.AWS_ACCESS_KEY_ID.environmentVariable(), "akid2");
+ ENVIRONMENT_VARIABLE_HELPER.set(SdkSystemSetting.AWS_SECRET_ACCESS_KEY.environmentVariable(), "skid2");
+
+ mockHttpClient = new MockSyncHttpClient();
+ mockHttpClient.stubNextResponse(mockStsResponse());
+ }
+
+ @AfterAll
+ public static void teardown() {
+ System.clearProperty(SdkSystemSetting.AWS_ACCESS_KEY_ID.property());
+ System.clearProperty(SdkSystemSetting.AWS_SECRET_ACCESS_KEY.property());
+ System.clearProperty(SdkSystemSetting.AWS_SESSION_TOKEN.property());
+ }
+
+ private static HttpExecuteResponse mockStsResponse() {
+ return HttpExecuteResponse.builder()
+ .response(SdkHttpResponse.builder().statusCode(200).build())
+ .responseBody(AbortableInputStream.create(new StringInputStream("")))
+ .build();
+ }
+
+ @ParameterizedTest
+ @MethodSource("environmentVariableCredentialProviders")
+ void userAgentString_containsEnvironmentVariableBusinessMetric_WhenUsingEnvironmentVariableCredentials(
+ IdentityProvider extends AwsCredentialsIdentity> provider, String expected) throws Exception {
+
+ stsClient(provider, mockHttpClient).getCallerIdentity();
+
+ SdkHttpRequest lastRequest = mockHttpClient.getLastRequest();
+ assertThat(lastRequest).isNotNull();
+
+ List userAgentHeaders = lastRequest.headers().get("User-Agent");
+ assertThat(userAgentHeaders).isNotNull().hasSize(1);
+ assertThat(userAgentHeaders.get(0)).contains(expected);
+
+ }
+
+ private static Stream environmentVariableCredentialProviders() {
+ return Stream.of(
+ Arguments.of(EnvironmentVariableCredentialsProvider.create(), "m/D,g")
+ );
+ }
+
+ @ParameterizedTest
+ @MethodSource("environmentVariableCredentialProvidersWithSessionToken")
+ void userAgentString_containsEnvironmentVariableBusinessMetric_WhenUsingEnvironmentVariableCredentialsWithSessionToken(
+ IdentityProvider extends AwsCredentialsIdentity> provider, String expected) throws Exception {
+
+ System.setProperty(SdkSystemSetting.AWS_SESSION_TOKEN.property(), "test-session-token");
+
+ stsClient(provider, mockHttpClient).getCallerIdentity();
+
+ SdkHttpRequest lastRequest = mockHttpClient.getLastRequest();
+ assertThat(lastRequest).isNotNull();
+
+ List userAgentHeaders = lastRequest.headers().get("User-Agent");
+ assertThat(userAgentHeaders).isNotNull().hasSize(1);
+ assertThat(userAgentHeaders.get(0)).contains(expected);
+ }
+
+ private static Stream environmentVariableCredentialProvidersWithSessionToken() {
+ return Stream.of(
+ Arguments.of(EnvironmentVariableCredentialsProvider.create(), "m/D,g")
+ );
+ }
+
+ private static StsClient stsClient(IdentityProvider extends AwsCredentialsIdentity> provider, SdkHttpClient httpClient) {
+ return StsClient.builder()
+ .credentialsProvider(provider)
+ .httpClient(httpClient)
+ .build();
+ }
+}
diff --git a/test/auth-tests/src/it/java/software/amazon/awssdk/auth/source/ImdsUserAgentProviderTest.java b/test/auth-tests/src/it/java/software/amazon/awssdk/auth/source/ImdsUserAgentProviderTest.java
new file mode 100644
index 000000000000..07bf771d62c9
--- /dev/null
+++ b/test/auth-tests/src/it/java/software/amazon/awssdk/auth/source/ImdsUserAgentProviderTest.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package software.amazon.awssdk.auth.source;
+
+import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
+import static com.github.tomakehurst.wiremock.client.WireMock.get;
+import static com.github.tomakehurst.wiremock.client.WireMock.put;
+import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo;
+import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import com.github.tomakehurst.wiremock.junit5.WireMockExtension;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.List;
+import java.util.stream.Stream;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.extension.RegisterExtension;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import software.amazon.awssdk.auth.credentials.InstanceProfileCredentialsProvider;
+import software.amazon.awssdk.core.SdkSystemSetting;
+import software.amazon.awssdk.http.AbortableInputStream;
+import software.amazon.awssdk.http.HttpExecuteResponse;
+import software.amazon.awssdk.http.SdkHttpClient;
+import software.amazon.awssdk.http.SdkHttpRequest;
+import software.amazon.awssdk.http.SdkHttpResponse;
+import software.amazon.awssdk.identity.spi.AwsCredentialsIdentity;
+import software.amazon.awssdk.identity.spi.IdentityProvider;
+import software.amazon.awssdk.services.sts.StsClient;
+import software.amazon.awssdk.testutils.service.http.MockSyncHttpClient;
+import software.amazon.awssdk.utils.DateUtils;
+import software.amazon.awssdk.utils.StringInputStream;
+
+/**
+ * Test class to verify that InstanceProfileCredentialsProvider (IMDS) correctly includes
+ * business metrics in the User-Agent header.
+ */
+class ImdsUserAgentProviderTest {
+ private static final String TOKEN_RESOURCE_PATH = "/latest/api/token";
+ private static final String CREDENTIALS_RESOURCE_PATH = "/latest/meta-data/iam/security-credentials/";
+ private static final String TEST_ROLE_NAME = "test-role";
+ private static final String TOKEN_STUB = "test-token";
+
+ private MockSyncHttpClient mockHttpClient;
+
+ @RegisterExtension
+ static WireMockExtension wireMockServer = WireMockExtension.newInstance()
+ .options(wireMockConfig().dynamicPort())
+ .configureStaticDsl(true)
+ .build();
+
+ @BeforeEach
+ public void setup() {
+
+ System.setProperty(SdkSystemSetting.AWS_EC2_METADATA_SERVICE_ENDPOINT.property(),
+ "http://localhost:" + wireMockServer.getPort());
+
+ mockHttpClient = new MockSyncHttpClient();
+ mockHttpClient.stubNextResponse(mockStsResponse());
+
+ stubImdsResponses();
+ }
+
+ @AfterAll
+ public static void teardown() {
+ System.clearProperty(SdkSystemSetting.AWS_EC2_METADATA_SERVICE_ENDPOINT.property());
+ }
+
+ private static HttpExecuteResponse mockStsResponse() {
+ return HttpExecuteResponse.builder()
+ .response(SdkHttpResponse.builder().statusCode(200).build())
+ .responseBody(AbortableInputStream.create(new StringInputStream("")))
+ .build();
+ }
+
+ private void stubImdsResponses() {
+ wireMockServer.stubFor(put(urlPathEqualTo(TOKEN_RESOURCE_PATH))
+ .willReturn(aResponse().withBody(TOKEN_STUB)));
+
+ wireMockServer.stubFor(get(urlPathEqualTo(CREDENTIALS_RESOURCE_PATH))
+ .willReturn(aResponse().withBody(TEST_ROLE_NAME)));
+
+ String credentialsResponse = createCredentialsResponse("ACCESS_KEY_ID", "SECRET_ACCESS_KEY",
+ null);
+ wireMockServer.stubFor(get(urlPathEqualTo(CREDENTIALS_RESOURCE_PATH + TEST_ROLE_NAME))
+ .willReturn(aResponse().withBody(credentialsResponse)));
+ }
+
+ private void stubImdsResponsesWithSessionToken() {
+ wireMockServer.stubFor(put(urlPathEqualTo(TOKEN_RESOURCE_PATH))
+ .willReturn(aResponse().withBody(TOKEN_STUB)));
+
+ wireMockServer.stubFor(get(urlPathEqualTo(CREDENTIALS_RESOURCE_PATH))
+ .willReturn(aResponse().withBody(TEST_ROLE_NAME)));
+
+ String credentialsResponse = createCredentialsResponse("ACCESS_KEY_ID", "SECRET_ACCESS_KEY",
+ "SESSION_TOKEN");
+ wireMockServer.stubFor(get(urlPathEqualTo(CREDENTIALS_RESOURCE_PATH + TEST_ROLE_NAME))
+ .willReturn(aResponse().withBody(credentialsResponse)));
+ }
+
+ private String createCredentialsResponse(String accessKeyId, String secretAccessKey, String sessionToken) {
+ StringBuilder response = new StringBuilder();
+ response.append("{");
+ response.append("\"AccessKeyId\":\"").append(accessKeyId).append("\",");
+ response.append("\"SecretAccessKey\":\"").append(secretAccessKey).append("\",");
+ if (sessionToken != null) {
+ response.append("\"Token\":\"").append(sessionToken).append("\",");
+ }
+ response.append("\"Expiration\":\"").append(DateUtils.formatIso8601Date(Instant.now().plus(Duration.ofHours(1))))
+ .append("\"");
+ response.append("}");
+ return response.toString();
+ }
+
+ @ParameterizedTest
+ @MethodSource("imdsCredentialProviders")
+ void userAgentString_containsImdsBusinessMetric_WhenUsingInstanceProfileCredentials(
+ IdentityProvider extends AwsCredentialsIdentity> provider, String expected) throws Exception {
+
+ stsClient(provider, mockHttpClient).getCallerIdentity();
+
+ SdkHttpRequest lastRequest = mockHttpClient.getLastRequest();
+ assertThat(lastRequest).isNotNull();
+
+ List userAgentHeaders = lastRequest.headers().get("User-Agent");
+ assertThat(userAgentHeaders).isNotNull().hasSize(1);
+ assertThat(userAgentHeaders.get(0)).contains(expected);
+ }
+
+ private static Stream imdsCredentialProviders() {
+ return Stream.of(
+ Arguments.of(InstanceProfileCredentialsProvider.create(), "m/D,0"),
+
+ Arguments.of(InstanceProfileCredentialsProvider.builder()
+ .endpoint("http://localhost:" + wireMockServer.getPort())
+ .build(), "m/D,0")
+ );
+ }
+
+ @ParameterizedTest
+ @MethodSource("imdsCredentialProvidersWithSessionToken")
+ void userAgentString_containsImdsBusinessMetric_WhenUsingInstanceProfileCredentialsWithSessionToken(
+ IdentityProvider extends AwsCredentialsIdentity> provider, String expected) throws Exception {
+
+ stubImdsResponsesWithSessionToken();
+
+ stsClient(provider, mockHttpClient).getCallerIdentity();
+
+ SdkHttpRequest lastRequest = mockHttpClient.getLastRequest();
+ assertThat(lastRequest).isNotNull();
+
+ List userAgentHeaders = lastRequest.headers().get("User-Agent");
+ assertThat(userAgentHeaders).isNotNull().hasSize(1);
+ assertThat(userAgentHeaders.get(0)).contains(expected);
+ }
+
+ private static Stream imdsCredentialProvidersWithSessionToken() {
+ return Stream.of(
+ Arguments.of(InstanceProfileCredentialsProvider.create(), "m/D,0")
+ );
+ }
+
+ private static StsClient stsClient(IdentityProvider extends AwsCredentialsIdentity> provider, SdkHttpClient httpClient) {
+ return StsClient.builder()
+ .credentialsProvider(provider)
+ .httpClient(httpClient)
+ .build();
+ }
+}
diff --git a/test/auth-tests/src/it/java/software/amazon/awssdk/auth/source/ProcessCredentialsProviderUserAgentTest.java b/test/auth-tests/src/it/java/software/amazon/awssdk/auth/source/ProcessCredentialsProviderUserAgentTest.java
new file mode 100644
index 000000000000..0731a48b071f
--- /dev/null
+++ b/test/auth-tests/src/it/java/software/amazon/awssdk/auth/source/ProcessCredentialsProviderUserAgentTest.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package software.amazon.awssdk.auth.source;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Stream;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import software.amazon.awssdk.auth.credentials.ProcessCredentialsProvider;
+import software.amazon.awssdk.http.AbortableInputStream;
+import software.amazon.awssdk.http.HttpExecuteResponse;
+import software.amazon.awssdk.http.SdkHttpClient;
+import software.amazon.awssdk.http.SdkHttpRequest;
+import software.amazon.awssdk.http.SdkHttpResponse;
+import software.amazon.awssdk.identity.spi.AwsCredentialsIdentity;
+import software.amazon.awssdk.identity.spi.IdentityProvider;
+import software.amazon.awssdk.services.sts.StsClient;
+import software.amazon.awssdk.testutils.service.http.MockSyncHttpClient;
+import software.amazon.awssdk.utils.DateUtils;
+import software.amazon.awssdk.utils.StringInputStream;
+
+/**
+ * Test class to verify that ProcessCredentialsProvider correctly includes
+ * business metrics in the User-Agent header. This test focuses specifically on the
+ * CREDENTIALS_PROCESS ("w") business metric feature ID.
+ */
+class ProcessCredentialsProviderUserAgentTest {
+
+ private MockSyncHttpClient mockHttpClient;
+
+ @BeforeEach
+ public void setup() {
+ mockHttpClient = new MockSyncHttpClient();
+ mockHttpClient.stubNextResponse(mockStsResponse());
+ }
+
+ private static HttpExecuteResponse mockStsResponse() {
+ return HttpExecuteResponse.builder()
+ .response(SdkHttpResponse.builder().statusCode(200).build())
+ .responseBody(AbortableInputStream.create(new StringInputStream("")))
+ .build();
+ }
+
+ @ParameterizedTest
+ @MethodSource("processCredentialProviders")
+ void userAgentString_containsProcessBusinessMetric_WhenUsingProcessCredentials(
+ IdentityProvider extends AwsCredentialsIdentity> provider, String expected) throws Exception {
+
+ stsClient(provider, mockHttpClient).getCallerIdentity();
+
+ SdkHttpRequest lastRequest = mockHttpClient.getLastRequest();
+ assertThat(lastRequest).isNotNull();
+
+ List userAgentHeaders = lastRequest.headers().get("User-Agent");
+ assertThat(userAgentHeaders).isNotNull().hasSize(1);
+ assertThat(userAgentHeaders.get(0)).contains(expected);
+ }
+
+ private static Stream processCredentialProviders() {
+ String mockCommand = createMockCredentialsCommand(false);
+ List mockCommandList = createMockCredentialsCommandList(false);
+
+ return Stream.of(
+ Arguments.of(ProcessCredentialsProvider.builder()
+ .command(mockCommand)
+ .build(), "m/D,w"),
+
+ Arguments.of(ProcessCredentialsProvider.builder()
+ .command(mockCommandList)
+ .build(), "m/D,w")
+ );
+ }
+
+ @ParameterizedTest
+ @MethodSource("processCredentialProvidersWithSessionToken")
+ void userAgentString_containsProcessBusinessMetric_WhenUsingProcessCredentialsWithSessionToken(
+ IdentityProvider extends AwsCredentialsIdentity> provider, String expected) throws Exception {
+
+ stsClient(provider, mockHttpClient).getCallerIdentity();
+
+ SdkHttpRequest lastRequest = mockHttpClient.getLastRequest();
+ assertThat(lastRequest).isNotNull();
+
+ List userAgentHeaders = lastRequest.headers().get("User-Agent");
+ assertThat(userAgentHeaders).isNotNull().hasSize(1);
+ assertThat(userAgentHeaders.get(0)).contains(expected);
+ }
+
+ private static Stream processCredentialProvidersWithSessionToken() {
+ String mockCommand = createMockCredentialsCommand(true);
+
+ return Stream.of(
+ Arguments.of(ProcessCredentialsProvider.builder()
+ .command(mockCommand)
+ .build(), "m/D,w")
+ );
+ }
+
+ private static String createMockCredentialsCommand(boolean includeSessionToken) {
+ String credentialsJson = createCredentialsJson(includeSessionToken);
+
+ return "echo '" + credentialsJson + "'";
+ }
+
+ private static List createMockCredentialsCommandList(boolean includeSessionToken) {
+ String credentialsJson = createCredentialsJson(includeSessionToken);
+
+ // Use echo command as a list
+ return Arrays.asList("echo", credentialsJson);
+ }
+
+ private static String createCredentialsJson(boolean includeSessionToken) {
+ StringBuilder json = new StringBuilder();
+ json.append("{");
+ json.append("\"Version\": 1,");
+ json.append("\"AccessKeyId\": \"test-access-key\",");
+ json.append("\"SecretAccessKey\": \"test-secret-key\"");
+
+ if (includeSessionToken) {
+ json.append(",\"SessionToken\": \"test-session-token\"");
+ }
+
+ // Add expiration time (1 hour from now)
+ String expiration = DateUtils.formatIso8601Date(Instant.now().plus(1, ChronoUnit.HOURS));
+ json.append(",\"Expiration\": \"").append(expiration).append("\"");
+
+ json.append("}");
+ return json.toString();
+ }
+
+ private static StsClient stsClient(IdentityProvider extends AwsCredentialsIdentity> provider, SdkHttpClient httpClient) {
+ return StsClient.builder()
+ .credentialsProvider(provider)
+ .httpClient(httpClient)
+ .build();
+ }
+}
diff --git a/test/auth-tests/src/it/java/software/amazon/awssdk/auth/source/ProfileCredentialProviderUserAgentTest.java b/test/auth-tests/src/it/java/software/amazon/awssdk/auth/source/ProfileCredentialProviderUserAgentTest.java
new file mode 100644
index 000000000000..1bf7a1ecd3b7
--- /dev/null
+++ b/test/auth-tests/src/it/java/software/amazon/awssdk/auth/source/ProfileCredentialProviderUserAgentTest.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package software.amazon.awssdk.auth.source;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import software.amazon.awssdk.auth.credentials.ProfileCredentialsProvider;
+import software.amazon.awssdk.http.AbortableInputStream;
+import software.amazon.awssdk.http.HttpExecuteResponse;
+import software.amazon.awssdk.http.SdkHttpRequest;
+import software.amazon.awssdk.http.SdkHttpResponse;
+import software.amazon.awssdk.profiles.ProfileFile;
+import software.amazon.awssdk.services.sts.StsClient;
+import software.amazon.awssdk.testutils.service.http.MockSyncHttpClient;
+import software.amazon.awssdk.utils.StringInputStream;
+
+/**
+ * Test class to verify Profile credentials provider business metrics.
+ */
+class ProfileCredentialProviderUserAgentTest {
+
+ private MockSyncHttpClient mockHttpClient;
+ private Path tempConfigFile;
+
+ @BeforeEach
+ public void setup() {
+ mockHttpClient = new MockSyncHttpClient();
+ mockHttpClient.stubNextResponse(mockStsResponse());
+ }
+
+ @AfterEach
+ public void teardown() throws IOException {
+ if (tempConfigFile != null && Files.exists(tempConfigFile)) {
+ Files.delete(tempConfigFile);
+ }
+ }
+
+ private static HttpExecuteResponse mockStsResponse() {
+ return HttpExecuteResponse.builder()
+ .response(SdkHttpResponse.builder().statusCode(200).build())
+ .responseBody(AbortableInputStream.create(new StringInputStream("")))
+ .build();
+ }
+
+ // Basic profile credentials - Expected Feature ID: "n"
+ @Test
+ void basicProfileCredentials_containsFeatureIdN() throws Exception {
+ String configContent =
+ "[profile A]\n" +
+ "aws_access_key_id = abc123\n" +
+ "aws_secret_access_key = def456\n";
+
+ tempConfigFile = Files.createTempFile("aws-config-basic-", ".tmp");
+ Files.write(tempConfigFile, configContent.getBytes());
+
+ ProfileFile profileFile = ProfileFile.builder()
+ .content(tempConfigFile)
+ .type(ProfileFile.Type.CONFIGURATION)
+ .build();
+
+ ProfileCredentialsProvider credentialsProvider = ProfileCredentialsProvider.builder()
+ .profileFile(profileFile)
+ .profileName("A")
+ .build();
+
+ StsClient stsClient = StsClient.builder()
+ .credentialsProvider(credentialsProvider)
+ .httpClient(mockHttpClient)
+ .build();
+
+ stsClient.getCallerIdentity();
+
+ assertThat(mockHttpClient.getRequests()).hasSize(1);
+
+ SdkHttpRequest lastRequest = mockHttpClient.getLastRequest();
+ assertThat(lastRequest).isNotNull();
+
+ List userAgentHeaders = lastRequest.headers().get("User-Agent");
+ assertThat(userAgentHeaders).isNotNull().hasSize(1);
+ String userAgent = userAgentHeaders.get(0);
+
+ assertThat(userAgent).contains("m/D,n");
+
+ credentialsProvider.close();
+ stsClient.close();
+ }
+
+ //Profile with credential_process - Expected Feature IDs: "v,w"
+ @Test
+ void profileWithCredentialProcess_containsFeatureIdVW() throws Exception {
+ String configContent =
+ "[profile A]\n" +
+ "credential_process = echo '{\"Version\": 1, \"AccessKeyId\": \"abc123\", \"SecretAccessKey\": \"def456\"}'\n";
+
+ tempConfigFile = Files.createTempFile("aws-config-process-", ".tmp");
+ Files.write(tempConfigFile, configContent.getBytes());
+
+ ProfileFile profileFile = ProfileFile.builder()
+ .content(tempConfigFile)
+ .type(ProfileFile.Type.CONFIGURATION)
+ .build();
+
+ ProfileCredentialsProvider credentialsProvider = ProfileCredentialsProvider.builder()
+ .profileFile(profileFile)
+ .profileName("A")
+ .build();
+
+ StsClient stsClient = StsClient.builder()
+ .credentialsProvider(credentialsProvider)
+ .httpClient(mockHttpClient)
+ .build();
+
+ stsClient.getCallerIdentity();
+
+ assertThat(mockHttpClient.getRequests()).hasSize(1);
+
+ SdkHttpRequest lastRequest = mockHttpClient.getLastRequest();
+ assertThat(lastRequest).isNotNull();
+
+ List userAgentHeaders = lastRequest.headers().get("User-Agent");
+ assertThat(userAgentHeaders).isNotNull().hasSize(1);
+ String userAgent = userAgentHeaders.get(0);
+
+ assertThat(userAgent).contains("m/D,v,w");
+
+ credentialsProvider.close();
+ stsClient.close();
+ }
+
+}
diff --git a/test/auth-tests/src/it/java/software/amazon/awssdk/auth/source/SystemPropertyCredentialsProviderUserAgentTest.java b/test/auth-tests/src/it/java/software/amazon/awssdk/auth/source/SystemPropertyCredentialsProviderUserAgentTest.java
new file mode 100644
index 000000000000..de57169198fb
--- /dev/null
+++ b/test/auth-tests/src/it/java/software/amazon/awssdk/auth/source/SystemPropertyCredentialsProviderUserAgentTest.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package software.amazon.awssdk.auth.source;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.List;
+import java.util.stream.Stream;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import software.amazon.awssdk.auth.credentials.SystemPropertyCredentialsProvider;
+import software.amazon.awssdk.http.AbortableInputStream;
+import software.amazon.awssdk.http.HttpExecuteResponse;
+import software.amazon.awssdk.http.SdkHttpClient;
+import software.amazon.awssdk.http.SdkHttpRequest;
+import software.amazon.awssdk.http.SdkHttpResponse;
+import software.amazon.awssdk.identity.spi.AwsCredentialsIdentity;
+import software.amazon.awssdk.identity.spi.IdentityProvider;
+import software.amazon.awssdk.services.sts.StsClient;
+import software.amazon.awssdk.testutils.service.http.MockSyncHttpClient;
+import software.amazon.awssdk.utils.StringInputStream;
+
+/**
+ * Test class to verify that SystemPropertyCredentialsProvider correctly includes
+ * business metrics in the User-Agent header. This test focuses specifically on the
+ * CREDENTIALS_JVM_SYSTEM_PROPERTIES ("f") business metric feature ID.
+ */
+class SystemPropertyCredentialsProviderUserAgentTest {
+
+ private MockSyncHttpClient mockHttpClient;
+
+ @BeforeEach
+ public void setup() {
+
+ System.setProperty("aws.accessKeyId", "test-access-key");
+ System.setProperty("aws.secretAccessKey", "test-secret-key");
+
+ // Setup mock HTTP client for STS calls
+ mockHttpClient = new MockSyncHttpClient();
+ mockHttpClient.stubNextResponse(mockStsResponse());
+ }
+
+ @AfterAll
+ public static void teardown() {
+ System.clearProperty("aws.accessKeyId");
+ System.clearProperty("aws.secretAccessKey");
+ System.clearProperty("aws.sessionToken");
+ }
+
+ private static HttpExecuteResponse mockStsResponse() {
+ return HttpExecuteResponse.builder()
+ .response(SdkHttpResponse.builder().statusCode(200).build())
+ .responseBody(AbortableInputStream.create(new StringInputStream("")))
+ .build();
+ }
+
+ @ParameterizedTest
+ @MethodSource("systemPropertyCredentialProviders")
+ void userAgentString_containsSystemPropertyBusinessMetric_WhenUsingSystemPropertyCredentials(
+ IdentityProvider extends AwsCredentialsIdentity> provider, String expected) throws Exception {
+
+ stsClient(provider, mockHttpClient).getCallerIdentity();
+
+ SdkHttpRequest lastRequest = mockHttpClient.getLastRequest();
+ assertThat(lastRequest).isNotNull();
+
+ List userAgentHeaders = lastRequest.headers().get("User-Agent");
+ assertThat(userAgentHeaders).isNotNull().hasSize(1);
+ assertThat(userAgentHeaders.get(0)).contains(expected);
+ }
+
+ private static Stream systemPropertyCredentialProviders() {
+ return Stream.of(
+ Arguments.of(SystemPropertyCredentialsProvider.create(), "m/D,f")
+ );
+ }
+
+ @ParameterizedTest
+ @MethodSource("systemPropertyCredentialProvidersWithSessionToken")
+ void userAgentString_containsSystemPropertyBusinessMetric_WhenUsingSystemPropertyCredentialsWithSessionToken(
+ IdentityProvider extends AwsCredentialsIdentity> provider, String expected) throws Exception {
+
+ System.setProperty("aws.sessionToken", "test-session-token");
+
+ stsClient(provider, mockHttpClient).getCallerIdentity();
+
+ SdkHttpRequest lastRequest = mockHttpClient.getLastRequest();
+ assertThat(lastRequest).isNotNull();
+
+ List userAgentHeaders = lastRequest.headers().get("User-Agent");
+ assertThat(userAgentHeaders).isNotNull().hasSize(1);
+ assertThat(userAgentHeaders.get(0)).contains(expected);
+ }
+
+ private static Stream systemPropertyCredentialProvidersWithSessionToken() {
+ return Stream.of(
+ Arguments.of(SystemPropertyCredentialsProvider.create(), "m/D,f")
+ );
+ }
+
+ private static StsClient stsClient(IdentityProvider extends AwsCredentialsIdentity> provider, SdkHttpClient httpClient) {
+ return StsClient.builder()
+ .credentialsProvider(provider)
+ .httpClient(httpClient)
+ .build();
+ }
+}
diff --git a/test/auth-tests/src/it/java/software/amazon/awssdk/auth/source/UserAgentProviderTest.java b/test/auth-tests/src/it/java/software/amazon/awssdk/auth/source/UserAgentProviderTest.java
index ffe82176afff..4ee93a9afd32 100644
--- a/test/auth-tests/src/it/java/software/amazon/awssdk/auth/source/UserAgentProviderTest.java
+++ b/test/auth-tests/src/it/java/software/amazon/awssdk/auth/source/UserAgentProviderTest.java
@@ -46,7 +46,7 @@ class UserAgentProviderTest {
private MockSyncHttpClient mockHttpClient;
@BeforeEach
- public void setup() throws UnsupportedEncodingException {
+ public void setup() {
mockHttpClient = new MockSyncHttpClient();
mockHttpClient.stubNextResponse(mockResponse());
}
@@ -74,8 +74,8 @@ void userAgentString_containsCredentialProviderNames_IfPresent(IdentityProvider<
private static Stream credentialProviders() {
return Stream.of(
- Arguments.of(StaticCredentialsProvider.create(SESSION_IDENTITY), "stat"),
- Arguments.of(StaticCredentialsProvider.create(BASIC_IDENTITY), "stat")
+ Arguments.of(StaticCredentialsProvider.create(SESSION_IDENTITY), "m/D,e"),
+ Arguments.of(StaticCredentialsProvider.create(BASIC_IDENTITY), "m/D,e")
);
}
diff --git a/test/auth-tests/src/it/java/software/amazon/awssdk/auth/sts/StsCredentialsProviderUserAgentTest.java b/test/auth-tests/src/it/java/software/amazon/awssdk/auth/sts/StsCredentialsProviderUserAgentTest.java
new file mode 100644
index 000000000000..57c3d7aa1294
--- /dev/null
+++ b/test/auth-tests/src/it/java/software/amazon/awssdk/auth/sts/StsCredentialsProviderUserAgentTest.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package software.amazon.awssdk.auth.sts;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.stream.Stream;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
+import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
+import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
+import software.amazon.awssdk.http.AbortableInputStream;
+import software.amazon.awssdk.http.HttpExecuteResponse;
+import software.amazon.awssdk.http.SdkHttpRequest;
+import software.amazon.awssdk.http.SdkHttpResponse;
+import software.amazon.awssdk.services.sts.StsClient;
+import software.amazon.awssdk.services.sts.auth.StsAssumeRoleCredentialsProvider;
+import software.amazon.awssdk.services.sts.auth.StsAssumeRoleWithSamlCredentialsProvider;
+import software.amazon.awssdk.services.sts.auth.StsAssumeRoleWithWebIdentityCredentialsProvider;
+import software.amazon.awssdk.services.sts.auth.StsGetFederationTokenCredentialsProvider;
+import software.amazon.awssdk.services.sts.auth.StsGetSessionTokenCredentialsProvider;
+import software.amazon.awssdk.services.sts.auth.StsWebIdentityTokenFileCredentialsProvider;
+import software.amazon.awssdk.core.SdkSystemSetting;
+import software.amazon.awssdk.testutils.service.http.MockSyncHttpClient;
+import software.amazon.awssdk.utils.StringInputStream;
+
+/**
+ * Tests STS credentials provider business metrics emission in User-Agent headers.
+ *
+ * Tests the following business metrics:
+ * - CREDENTIALS_STS_ASSUME_ROLE("i") - StsAssumeRoleCredentialsProvider
+ * - CREDENTIALS_STS_ASSUME_ROLE_SAML("j") - StsAssumeRoleWithSamlCredentialsProvider
+ * - CREDENTIALS_STS_ASSUME_ROLE_WEB_ID("k") - StsAssumeRoleWithWebIdentityCredentialsProvider
+ * - CREDENTIALS_STS_FEDERATION_TOKEN("l") - StsGetFederationTokenCredentialsProvider
+ * - CREDENTIALS_STS_SESSION_TOKEN("m") - StsGetSessionTokenCredentialsProvider
+ * - CREDENTIALS_ENV_VARS_STS_WEB_ID_TOKEN("h") - WebIdentityTokenFileCredentialsProvider
+ */
+class StsCredentialsProviderUserAgentTest {
+
+ private MockSyncHttpClient mockHttpClient;
+
+ @BeforeEach
+ public void setup() {
+ mockHttpClient = new MockSyncHttpClient();
+ mockHttpClient.stubNextResponse(mockStsResponse());
+ }
+
+ @ParameterizedTest
+ @MethodSource("stsCredentialsProviders")
+ void stsCredentialsProvider_emitsCorrectBusinessMetrics(AwsCredentialsProvider provider,
+ String expected,
+ String providerName) throws Exception {
+ StsClient stsClient = StsClient.builder()
+ .credentialsProvider(provider)
+ .httpClient(mockHttpClient)
+ .build();
+
+ stsClient.getCallerIdentity();
+
+ SdkHttpRequest lastRequest = mockHttpClient.getLastRequest();
+ assertThat(lastRequest).isNotNull();
+
+ List userAgentHeaders = lastRequest.headers().get("User-Agent");
+ assertThat(userAgentHeaders).isNotNull().hasSize(1);
+ assertThat(userAgentHeaders.get(0)).contains(expected);
+
+ stsClient.close();
+ }
+
+ private static Stream stsCredentialsProviders() throws Exception {
+ return Stream.of(
+ Arguments.of(createAssumeRoleProvider(), "m/D,i", "StsAssumeRoleCredentialsProvider"),
+ Arguments.of(createAssumeRoleWithSamlProvider(), "m/D,j", "StsAssumeRoleWithSamlCredentialsProvider"),
+ Arguments.of(createAssumeRoleWithWebIdentityProvider(), "m/D,k", "StsAssumeRoleWithWebIdentityCredentialsProvider"),
+ Arguments.of(createFederationTokenProvider(), "m/D,l", "StsGetFederationTokenCredentialsProvider"),
+ Arguments.of(createSessionTokenProvider(), "m/D,m", "StsGetSessionTokenCredentialsProvider"),
+ Arguments.of(createWebIdentityTokenFileProvider(), "m/D,k,h", "StsWebIdentityTokenFileCredentialsProvider")
+ );
+ }
+
+ private static AwsCredentialsProvider createAssumeRoleProvider() {
+ MockSyncHttpClient mockHttpClient = new MockSyncHttpClient();
+ mockHttpClient.stubNextResponse(createStsResponse("AssumeRole"));
+
+ AwsBasicCredentials staticCredentials = AwsBasicCredentials.create("AKIATEST", "test-secret");
+
+ StsClient stsClient = StsClient.builder()
+ .httpClient(mockHttpClient)
+ .credentialsProvider(StaticCredentialsProvider.create(staticCredentials))
+ .build();
+
+ return StsAssumeRoleCredentialsProvider.builder()
+ .stsClient(stsClient)
+ .refreshRequest(r -> r.roleArn("arn:aws:iam::123456789012:role/TestRole")
+ .roleSessionName("test-session"))
+ .build();
+ }
+
+ private static AwsCredentialsProvider createAssumeRoleWithSamlProvider() {
+ MockSyncHttpClient mockHttpClient = new MockSyncHttpClient();
+ mockHttpClient.stubNextResponse(createStsResponse("AssumeRoleWithSAML"));
+
+ StsClient stsClient = StsClient.builder()
+ .httpClient(mockHttpClient)
+ .build();
+
+ String samlAssertion = "PHNhbWw6QXNzZXJ0aW9uPjwvc2FtbDpBc3NlcnRpb24+";
+
+ return StsAssumeRoleWithSamlCredentialsProvider.builder()
+ .stsClient(stsClient)
+ .refreshRequest(r -> r.roleArn("arn:aws:iam::123456789012:role/TestRole")
+ .principalArn("arn:aws:iam::123456789012:saml-provider/TestProvider")
+ .samlAssertion(samlAssertion))
+ .build();
+ }
+
+ private static AwsCredentialsProvider createAssumeRoleWithWebIdentityProvider() {
+ MockSyncHttpClient mockHttpClient = new MockSyncHttpClient();
+ mockHttpClient.stubNextResponse(createStsResponse("AssumeRoleWithWebIdentity"));
+
+ StsClient stsClient = StsClient.builder()
+ .httpClient(mockHttpClient)
+ .region(software.amazon.awssdk.regions.Region.US_EAST_1)
+ .build();
+
+ String webIdentityToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c";
+
+ return StsAssumeRoleWithWebIdentityCredentialsProvider.builder()
+ .stsClient(stsClient)
+ .refreshRequest(r -> r.roleArn("arn:aws:iam::123456789012:role/TestRole")
+ .webIdentityToken(webIdentityToken)
+ .roleSessionName("test-session"))
+ .build();
+ }
+
+ private static AwsCredentialsProvider createFederationTokenProvider() {
+ MockSyncHttpClient mockHttpClient = new MockSyncHttpClient();
+ mockHttpClient.stubNextResponse(createStsResponse("GetFederationToken"));
+
+ AwsBasicCredentials staticCredentials = AwsBasicCredentials.create("AKIATEST", "test-secret");
+
+ StsClient stsClient = StsClient.builder()
+ .httpClient(mockHttpClient)
+ .credentialsProvider(StaticCredentialsProvider.create(staticCredentials))
+ .build();
+
+ return StsGetFederationTokenCredentialsProvider.builder()
+ .stsClient(stsClient)
+ .refreshRequest(r -> r.name("test-user"))
+ .build();
+ }
+
+ private static AwsCredentialsProvider createSessionTokenProvider() {
+ MockSyncHttpClient mockHttpClient = new MockSyncHttpClient();
+ mockHttpClient.stubNextResponse(createStsResponse("GetSessionToken"));
+
+ AwsBasicCredentials staticCredentials = AwsBasicCredentials.create("AKIATEST", "test-secret");
+
+ StsClient stsClient = StsClient.builder()
+ .httpClient(mockHttpClient)
+ .credentialsProvider(StaticCredentialsProvider.create(staticCredentials))
+ .build();
+
+ return StsGetSessionTokenCredentialsProvider.builder()
+ .stsClient(stsClient)
+ .build();
+ }
+
+ private static AwsCredentialsProvider createWebIdentityTokenFileProvider() throws Exception {
+ // Create temporary token file
+ Path tempTokenFile = Files.createTempFile("test-token", ".jwt");
+ Files.write(tempTokenFile, "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c".getBytes());
+
+ System.setProperty(SdkSystemSetting.AWS_ROLE_ARN.property(), "arn:aws:iam::123456789012:role/TestRole");
+ System.setProperty(SdkSystemSetting.AWS_WEB_IDENTITY_TOKEN_FILE.property(), tempTokenFile.toString());
+ System.setProperty(SdkSystemSetting.AWS_ROLE_SESSION_NAME.property(), "test-session");
+
+ MockSyncHttpClient mockHttpClient = new MockSyncHttpClient();
+ mockHttpClient.stubNextResponse(createStsResponse("AssumeRoleWithWebIdentity"));
+
+ StsClient stsClient = StsClient.builder()
+ .httpClient(mockHttpClient)
+ .build();
+
+ return StsWebIdentityTokenFileCredentialsProvider.builder()
+ .stsClient(stsClient)
+ .build();
+ }
+
+ private static HttpExecuteResponse mockStsResponse() {
+ String getCallerIdentityResponseBody = "\n" +
+ " \n" +
+ " arn:aws:sts::123456789012:assumed-role/TestRole/test-session\n" +
+ " AROATEST:test-session\n" +
+ " 123456789012\n" +
+ " \n" +
+ "";
+
+ return HttpExecuteResponse.builder()
+ .response(SdkHttpResponse.builder().statusCode(200).build())
+ .responseBody(AbortableInputStream.create(new StringInputStream(getCallerIdentityResponseBody)))
+ .build();
+ }
+
+ private static HttpExecuteResponse createStsResponse(String operation) {
+ String responseBody = "\n"
+ + " \n"
+ + " \n"
+ + " AKIATEST\n"
+ + " test-secret\n"
+ + " test-session-token\n"
+ + " 2099-12-31T23:59:59Z\n"
+ + " \n"
+ + " \n"
+ + " arn:aws:sts::123456789012:assumed-role/TestRole/test-session\n"
+ + " AROATEST:test-session\n"
+ + " \n"
+ + " \n"
+ + "";
+
+ return HttpExecuteResponse.builder()
+ .response(SdkHttpResponse.builder().statusCode(200).build())
+ .responseBody(AbortableInputStream.create(new StringInputStream(responseBody)))
+ .build();
+ }
+}