Skip to content

Millem/web identity token file stale duration config #4191

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jul 18, 2023
6 changes: 6 additions & 0 deletions .changes/next-release/feature-AWSSDKforJavav2-c5f5765.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"category": "AWS SDK for Java v2",
"contributor": "",
"type": "feature",
"description": "Allowing configuring the prefetchTime and staleTime on WebIdentityTokenFileCredentialsProvider"
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Duration;
import software.amazon.awssdk.annotations.SdkPublicApi;
import software.amazon.awssdk.auth.credentials.internal.WebIdentityCredentialsUtils;
import software.amazon.awssdk.auth.credentials.internal.WebIdentityTokenCredentialProperties;
Expand All @@ -31,18 +32,19 @@
import software.amazon.awssdk.utils.builder.ToCopyableBuilder;

/**
* A credential provider that will read web identity token file path, aws role arn
* and aws session name from system properties or environment variables for using
* web identity token credentials with STS.
* A credential provider that will read web identity token file path, aws role arn and aws session name from system properties or
* environment variables for using web identity token credentials with STS.
* <p>
* Use of this credentials provider requires the 'sts' module to be on the classpath.
* Use of this credentials provider requires the 'sts' module to be on the classpath.
* </p>
* <p>
* StsWebIdentityTokenFileCredentialsProvider in sts package can be used instead of this class if any one of following is required
*<ul>
* StsWebIdentityTokenFileCredentialsProvider in sts package can be used instead of this class if any one of following is
* required
* <ul>
* <li>Pass a custom StsClient to the provider. </li>
* <li>Periodically update credentials </li>
*</ul>
* </ul>
*
* @see AwsCredentialsProvider
*/
@SdkPublicApi
Expand All @@ -62,13 +64,22 @@ public class WebIdentityTokenFileCredentialsProvider

private final Boolean asyncCredentialUpdateEnabled;

private final Duration prefetchTime;

private final Duration staleTime;

private final Duration roleSessionDuration;

private WebIdentityTokenFileCredentialsProvider(BuilderImpl builder) {
AwsCredentialsProvider credentialsProvider = null;
RuntimeException loadException = null;
String roleArn = null;
String roleSessionName = null;
Path webIdentityTokenFile = null;
Boolean asyncCredentialUpdateEnabled = null;
Duration prefetchTime = null;
Duration staleTime = null;
Duration roleSessionDuration = null;

try {
webIdentityTokenFile =
Expand All @@ -77,7 +88,7 @@ private WebIdentityTokenFileCredentialsProvider(BuilderImpl builder) {
.getStringValueOrThrow()));

roleArn = builder.roleArn != null ? builder.roleArn
: trim(SdkSystemSetting.AWS_ROLE_ARN.getStringValueOrThrow());
: trim(SdkSystemSetting.AWS_ROLE_ARN.getStringValueOrThrow());

roleSessionName =
builder.roleSessionName != null ? builder.roleSessionName
Expand All @@ -86,12 +97,19 @@ private WebIdentityTokenFileCredentialsProvider(BuilderImpl builder) {
asyncCredentialUpdateEnabled =
builder.asyncCredentialUpdateEnabled != null ? builder.asyncCredentialUpdateEnabled : false;

prefetchTime = builder.prefetchTime;
staleTime = builder.staleTime;
roleSessionDuration = builder.roleSessionDuration;

WebIdentityTokenCredentialProperties credentialProperties =
WebIdentityTokenCredentialProperties.builder()
.roleArn(roleArn)
.roleSessionName(roleSessionName)
.webIdentityTokenFile(webIdentityTokenFile)
.asyncCredentialUpdateEnabled(asyncCredentialUpdateEnabled)
.prefetchTime(prefetchTime)
.staleTime(staleTime)
.roleSessionDuration(roleSessionDuration)
.build();

credentialsProvider = WebIdentityCredentialsUtils.factory().create(credentialProperties);
Expand All @@ -108,10 +126,12 @@ private WebIdentityTokenFileCredentialsProvider(BuilderImpl builder) {
this.roleSessionName = roleSessionName;
this.webIdentityTokenFile = webIdentityTokenFile;
this.asyncCredentialUpdateEnabled = asyncCredentialUpdateEnabled;
this.prefetchTime = prefetchTime;
this.staleTime = staleTime;
this.roleSessionDuration = roleSessionDuration;
}

public static WebIdentityTokenFileCredentialsProvider create() {

return WebIdentityTokenFileCredentialsProvider.builder().build();
}

Expand Down Expand Up @@ -165,9 +185,33 @@ public interface Builder extends CopyableBuilder<Builder, WebIdentityTokenFileCr
/**
* Define whether the provider should fetch credentials asynchronously in the background.
*/

Builder asyncCredentialUpdateEnabled(Boolean asyncCredentialUpdateEnabled);

/**
* Configure the amount of time, relative to STS token expiration, that the cached credentials are considered close to
* stale and should be updated.
*
* <p>Prefetch updates will occur between the specified time and the stale time of the provider. Prefetch
* updates may be asynchronous. See {@link #asyncCredentialUpdateEnabled}.
*
* <p>By default, this is 5 minutes.
*/
Builder prefetchTime(Duration prefetchTime);

/**
* Configure the amount of time, relative to STS token expiration, that the cached credentials are considered stale and
* must be updated. All threads will block until the value is updated.
*
* <p>By default, this is 1 minute.
*/
Builder staleTime(Duration staleTime);

/**
* @param sessionDuration
* @return
*/
Builder roleSessionDuration(Duration sessionDuration);

/**
* Create a {@link WebIdentityTokenFileCredentialsProvider} using the configuration applied to this builder.
*/
Expand All @@ -179,6 +223,9 @@ static final class BuilderImpl implements Builder {
private String roleSessionName;
private Path webIdentityTokenFile;
private Boolean asyncCredentialUpdateEnabled;
private Duration prefetchTime;
private Duration staleTime;
private Duration roleSessionDuration;

BuilderImpl() {
}
Expand All @@ -188,6 +235,9 @@ private BuilderImpl(WebIdentityTokenFileCredentialsProvider provider) {
this.roleSessionName = provider.roleSessionName;
this.webIdentityTokenFile = provider.webIdentityTokenFile;
this.asyncCredentialUpdateEnabled = provider.asyncCredentialUpdateEnabled;
this.prefetchTime = provider.prefetchTime;
this.staleTime = provider.staleTime;
this.roleSessionDuration = provider.roleSessionDuration;
}

@Override
Expand Down Expand Up @@ -230,6 +280,36 @@ public void setAsyncCredentialUpdateEnabled(Boolean asyncCredentialUpdateEnabled
asyncCredentialUpdateEnabled(asyncCredentialUpdateEnabled);
}

@Override
public Builder prefetchTime(Duration prefetchTime) {
this.prefetchTime = prefetchTime;
return this;
}

public void setPrefetchTime(Duration prefetchTime) {
prefetchTime(prefetchTime);
}

@Override
public Builder staleTime(Duration staleTime) {
this.staleTime = staleTime;
return this;
}

public void setStaleTime(Duration staleTime) {
staleTime(staleTime);
}

@Override
public Builder roleSessionDuration(Duration sessionDuration) {
this.roleSessionDuration = sessionDuration;
return this;
}

public void setRoleSessionDuration(Duration roleSessionDuration) {
roleSessionDuration(roleSessionDuration);
}

@Override
public WebIdentityTokenFileCredentialsProvider build() {
return new WebIdentityTokenFileCredentialsProvider(this);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package software.amazon.awssdk.auth.credentials.internal;

import java.nio.file.Path;
import java.time.Duration;
import software.amazon.awssdk.annotations.SdkProtectedApi;

/**
Expand All @@ -28,12 +29,18 @@ public class WebIdentityTokenCredentialProperties {
private final String roleSessionName;
private final Path webIdentityTokenFile;
private final Boolean asyncCredentialUpdateEnabled;
private final Duration prefetchTime;
private final Duration staleTime;
private final Duration roleSessionDuration;

private WebIdentityTokenCredentialProperties(Builder builder) {
this.roleArn = builder.roleArn;
this.roleSessionName = builder.roleSessionName;
this.webIdentityTokenFile = builder.webIdentityTokenFile;
this.asyncCredentialUpdateEnabled = builder.asyncCredentialUpdateEnabled;
this.prefetchTime = builder.prefetchTime;
this.staleTime = builder.staleTime;
this.roleSessionDuration = builder.roleSessionDuration;
}

public String roleArn() {
Expand All @@ -52,6 +59,18 @@ public Boolean asyncCredentialUpdateEnabled() {
return asyncCredentialUpdateEnabled;
}

public Duration prefetchTime() {
return prefetchTime;
}

public Duration staleTime() {
return staleTime;
}

public Duration roleSessionDuration() {
return this.roleSessionDuration;
}

public static Builder builder() {
return new Builder();
}
Expand All @@ -61,6 +80,9 @@ public static final class Builder {
private String roleSessionName;
private Path webIdentityTokenFile;
private Boolean asyncCredentialUpdateEnabled;
private Duration prefetchTime;
private Duration staleTime;
private Duration roleSessionDuration;

public Builder roleArn(String roleArn) {
this.roleArn = roleArn;
Expand All @@ -82,6 +104,21 @@ public Builder asyncCredentialUpdateEnabled(Boolean asyncCredentialUpdateEnabled
return this;
}

public Builder prefetchTime(Duration prefetchTime) {
this.prefetchTime = prefetchTime;
return this;
}

public Builder staleTime(Duration staleTime) {
this.staleTime = staleTime;
return this;
}

public Builder roleSessionDuration(Duration roleSessionDuration) {
this.roleSessionDuration = roleSessionDuration;
return this;
}

public WebIdentityTokenCredentialProperties build() {
return new WebIdentityTokenCredentialProperties(this);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,12 @@
import software.amazon.awssdk.services.sts.model.AssumeRoleWithWebIdentityRequest;
import software.amazon.awssdk.services.sts.model.IdpCommunicationErrorException;
import software.amazon.awssdk.utils.IoUtils;
import software.amazon.awssdk.utils.NumericUtils;
import software.amazon.awssdk.utils.SdkAutoCloseable;

/**
* An implementation of {@link WebIdentityTokenCredentialsProviderFactory} that allows users to assume a role
* using a web identity token file specified in either a {@link Profile} or environment variables.
* An implementation of {@link WebIdentityTokenCredentialsProviderFactory} that allows users to assume a role using a web identity
* token file specified in either a {@link Profile} or environment variables.
*/
@SdkProtectedApi
public final class StsWebIdentityCredentialsProviderFactory implements WebIdentityTokenCredentialsProviderFactory {
Expand Down Expand Up @@ -72,23 +73,35 @@ private StsWebIdentityCredentialsProvider(WebIdentityTokenCredentialProperties c
.overrideConfiguration(o -> o.retryPolicy(r -> r.retryCondition(retryCondition)))
.build();

AssumeRoleWithWebIdentityRequest request = AssumeRoleWithWebIdentityRequest.builder()
.roleArn(credentialProperties.roleArn())
.roleSessionName(sessionName)
.build();
AssumeRoleWithWebIdentityRequest.Builder requestBuilder = AssumeRoleWithWebIdentityRequest
.builder()
.roleArn(credentialProperties.roleArn())
.roleSessionName(sessionName);

if (credentialProperties.roleSessionDuration() != null) {
requestBuilder.durationSeconds(NumericUtils.saturatedCast(
credentialProperties.roleSessionDuration().getSeconds()));
}

AssumeRoleWithWebIdentityRequestSupplier supplier =
AssumeRoleWithWebIdentityRequestSupplier.builder()
.assumeRoleWithWebIdentityRequest(request)
.assumeRoleWithWebIdentityRequest(requestBuilder.build())
.webIdentityTokenFile(credentialProperties.webIdentityTokenFile())
.build();

this.credentialsProvider =
StsAssumeRoleWithWebIdentityCredentialsProvider.Builder builder =
StsAssumeRoleWithWebIdentityCredentialsProvider.builder()
.asyncCredentialUpdateEnabled(asyncCredentialUpdateEnabled)
.stsClient(stsClient)
.refreshRequest(supplier)
.build();
.refreshRequest(supplier);

if (credentialProperties.prefetchTime() != null) {
builder.prefetchTime(credentialProperties.prefetchTime());
}
if (credentialProperties.staleTime() != null) {
builder.staleTime(credentialProperties.staleTime());
}
this.credentialsProvider = builder.build();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,37 @@

import static org.junit.jupiter.api.Assertions.assertNotNull;

import java.nio.file.Paths;
import java.time.Duration;
import org.junit.jupiter.api.Test;
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
import software.amazon.awssdk.auth.credentials.WebIdentityTokenCredentialsProviderFactory;
import software.amazon.awssdk.auth.credentials.internal.WebIdentityCredentialsUtils;
import software.amazon.awssdk.auth.credentials.internal.WebIdentityTokenCredentialProperties;

public class StsWebIdentityCredentialsProviderFactoryTest {

class StsWebIdentityCredentialsProviderFactoryTest {

@Test
public void stsWebIdentityCredentialsProviderFactory_with_webIdentityCredentialsUtils() {
void stsWebIdentityCredentialsProviderFactory_with_webIdentityCredentialsUtils() {
WebIdentityTokenCredentialsProviderFactory factory = WebIdentityCredentialsUtils.factory();
assertNotNull(factory);
}

@Test
void stsWebIdentityCredentialsProviderFactory_withWebIdentityTokenCredentialProperties() {
WebIdentityTokenCredentialsProviderFactory factory = new StsWebIdentityCredentialsProviderFactory();
AwsCredentialsProvider provider = factory.create(
WebIdentityTokenCredentialProperties.builder()
.asyncCredentialUpdateEnabled(true)
.prefetchTime(Duration.ofMinutes(5))
.staleTime(Duration.ofMinutes(15))
.roleArn("role-arn")
.webIdentityTokenFile(Paths.get("/path/to/file"))
.roleSessionName("session-name")
.roleSessionDuration(Duration.ofMinutes(60))
.build());
assertNotNull(provider);

}

}