Skip to content

DefaultCredentialsProvider reload profile #3580

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
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@

package software.amazon.awssdk.auth.credentials;

import java.util.Optional;
import software.amazon.awssdk.annotations.SdkPublicApi;
import software.amazon.awssdk.auth.credentials.internal.LazyAwsCredentialsProvider;
import software.amazon.awssdk.profiles.ProfileFile;
import software.amazon.awssdk.profiles.ProfileFileSupplier;
import software.amazon.awssdk.utils.SdkAutoCloseable;
import software.amazon.awssdk.utils.ToString;
import software.amazon.awssdk.utils.builder.CopyableBuilder;
Expand All @@ -26,8 +28,8 @@
/**
* AWS credentials provider chain that looks for credentials in this order:
* <ol>
* <li>Java System Properties - <code>aws.accessKeyId</code> and <code>aws.secretAccessKey</code></li>
* <li>Environment Variables - <code>AWS_ACCESS_KEY_ID</code> and <code>AWS_SECRET_ACCESS_KEY</code></li>
* <li>Java System Properties - {@code aws.accessKeyId} and {@code aws.secretAccessKey}</li>
* <li>Environment Variables - {@code AWS_ACCESS_KEY_ID} and {@code AWS_SECRET_ACCESS_KEY}</li>
* <li>Web Identity Token credentials from system properties or environment variables</li>
* <li>Credential profiles file at the default location (~/.aws/credentials) shared by all AWS SDKs and the AWS CLI</li>
* <li>Credentials delivered through the Amazon EC2 container service if AWS_CONTAINER_CREDENTIALS_RELATIVE_URI" environment
Expand All @@ -51,7 +53,7 @@ public final class DefaultCredentialsProvider

private final LazyAwsCredentialsProvider providerChain;

private final ProfileFile profileFile;
private final ProfileFileSupplier profileFileSupplier;

private final String profileName;

Expand All @@ -63,7 +65,7 @@ public final class DefaultCredentialsProvider
* @see #builder()
*/
private DefaultCredentialsProvider(Builder builder) {
this.profileFile = builder.profileFile;
this.profileFileSupplier = builder.profileFileSupplier;
this.profileName = builder.profileName;
this.reuseLastProviderEnabled = builder.reuseLastProviderEnabled;
this.asyncCredentialUpdateEnabled = builder.asyncCredentialUpdateEnabled;
Expand All @@ -87,21 +89,21 @@ private static LazyAwsCredentialsProvider createChain(Builder builder) {

return LazyAwsCredentialsProvider.create(() -> {
AwsCredentialsProvider[] credentialsProviders = new AwsCredentialsProvider[] {
SystemPropertyCredentialsProvider.create(),
EnvironmentVariableCredentialsProvider.create(),
WebIdentityTokenFileCredentialsProvider.create(),
ProfileCredentialsProvider.builder()
.profileFile(builder.profileFile)
.profileName(builder.profileName)
.build(),
ContainerCredentialsProvider.builder()
.asyncCredentialUpdateEnabled(asyncCredentialUpdateEnabled)
.build(),
InstanceProfileCredentialsProvider.builder()
.asyncCredentialUpdateEnabled(asyncCredentialUpdateEnabled)
.profileFile(builder.profileFile)
.profileName(builder.profileName)
.build()
SystemPropertyCredentialsProvider.create(),
EnvironmentVariableCredentialsProvider.create(),
WebIdentityTokenFileCredentialsProvider.create(),
ProfileCredentialsProvider.builder()
.profileFile(builder.profileFileSupplier)
.profileName(builder.profileName)
.build(),
ContainerCredentialsProvider.builder()
.asyncCredentialUpdateEnabled(asyncCredentialUpdateEnabled)
.build(),
InstanceProfileCredentialsProvider.builder()
.asyncCredentialUpdateEnabled(asyncCredentialUpdateEnabled)
.profileFile(builder.profileFileSupplier)
.profileName(builder.profileName)
.build()
};

return AwsCredentialsProviderChain.builder()
Expand Down Expand Up @@ -144,7 +146,7 @@ public Builder toBuilder() {
* Configuration that defines the {@link DefaultCredentialsProvider}'s behavior.
*/
public static final class Builder implements CopyableBuilder<Builder, DefaultCredentialsProvider> {
private ProfileFile profileFile;
private ProfileFileSupplier profileFileSupplier;
private String profileName;
private Boolean reuseLastProviderEnabled = true;
private Boolean asyncCredentialUpdateEnabled = false;
Expand All @@ -156,14 +158,20 @@ private Builder() {
}

private Builder(DefaultCredentialsProvider credentialsProvider) {
this.profileFile = credentialsProvider.profileFile;
this.profileFileSupplier = credentialsProvider.profileFileSupplier;
this.profileName = credentialsProvider.profileName;
this.reuseLastProviderEnabled = credentialsProvider.reuseLastProviderEnabled;
this.asyncCredentialUpdateEnabled = credentialsProvider.asyncCredentialUpdateEnabled;
}

public Builder profileFile(ProfileFile profileFile) {
this.profileFile = profileFile;
return profileFile(Optional.ofNullable(profileFile)
.map(ProfileFileSupplier::fixedProfileFile)
.orElse(null));
}

public Builder profileFile(ProfileFileSupplier profileFileSupplier) {
this.profileFileSupplier = profileFileSupplier;
return this;
}

Expand Down Expand Up @@ -198,6 +206,7 @@ public Builder asyncCredentialUpdateEnabled(Boolean asyncCredentialUpdateEnabled
/**
* Create a {@link DefaultCredentialsProvider} using the configuration defined in this builder.
*/
@Override
public DefaultCredentialsProvider build() {
return new DefaultCredentialsProvider(this);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import java.time.Instant;
import java.util.Collections;
import java.util.Map;
import java.util.Optional;
import software.amazon.awssdk.annotations.SdkPublicApi;
import software.amazon.awssdk.annotations.SdkTestInternalApi;
import software.amazon.awssdk.auth.credentials.internal.Ec2MetadataConfigProvider;
Expand All @@ -36,6 +37,7 @@
import software.amazon.awssdk.core.exception.SdkClientException;
import software.amazon.awssdk.core.exception.SdkServiceException;
import software.amazon.awssdk.profiles.ProfileFile;
import software.amazon.awssdk.profiles.ProfileFileSupplier;
import software.amazon.awssdk.profiles.ProfileFileSystemSetting;
import software.amazon.awssdk.regions.util.HttpResourcesUtils;
import software.amazon.awssdk.regions.util.ResourcesEndpointProvider;
Expand Down Expand Up @@ -77,7 +79,7 @@ public final class InstanceProfileCredentialsProvider

private final String asyncThreadName;

private final ProfileFile profileFile;
private final ProfileFileSupplier profileFileSupplier;

private final String profileName;

Expand All @@ -89,14 +91,14 @@ private InstanceProfileCredentialsProvider(BuilderImpl builder) {
this.endpoint = builder.endpoint;
this.asyncCredentialUpdateEnabled = builder.asyncCredentialUpdateEnabled;
this.asyncThreadName = builder.asyncThreadName;
this.profileFile = builder.profileFile;
this.profileFileSupplier = builder.profileFileSupplier;
this.profileName = builder.profileName;

this.httpCredentialsLoader = HttpCredentialsLoader.create();
this.configProvider =
Ec2MetadataConfigProvider.builder()
.profileFile(builder.profileFile == null ? null : () -> builder.profileFile)
.profileName(builder.profileName == null ? null : builder.profileName)
.profileFile(builder.profileFileSupplier)
.profileName(builder.profileName)
Comment on lines -99 to +101
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated change. Felt null check of builder.profileName was not needed.

.build();

if (Boolean.TRUE.equals(builder.asyncCredentialUpdateEnabled)) {
Expand Down Expand Up @@ -279,9 +281,19 @@ public interface Builder extends HttpCredentialsProvider.Builder<InstanceProfile
* Configure the profile file used for loading IMDS-related configuration, like the endpoint mode (IPv4 vs IPv6).
*
* <p>By default, {@link ProfileFile#defaultProfileFile()} is used.
*
* @see #profileFile(ProfileFileSupplier)
*/
Builder profileFile(ProfileFile profileFile);

/**
* Define the mechanism for loading profile files.
*
* @param profileFileSupplier Supplier interface for generating a ProfileFile instance.
* @see #profileFile(ProfileFile)
*/
Builder profileFile(ProfileFileSupplier profileFileSupplier);

/**
* Configure the profile name used for loading IMDS-related configuration, like the endpoint mode (IPv4 vs IPv6).
*
Expand All @@ -292,6 +304,7 @@ public interface Builder extends HttpCredentialsProvider.Builder<InstanceProfile
/**
* Build a {@link InstanceProfileCredentialsProvider} from the provided configuration.
*/
@Override
InstanceProfileCredentialsProvider build();
}

Expand All @@ -301,7 +314,7 @@ static final class BuilderImpl implements Builder {
private String endpoint;
private Boolean asyncCredentialUpdateEnabled;
private String asyncThreadName;
private ProfileFile profileFile;
private ProfileFileSupplier profileFileSupplier;
private String profileName;

private BuilderImpl() {
Expand All @@ -313,7 +326,7 @@ private BuilderImpl(InstanceProfileCredentialsProvider provider) {
this.endpoint = provider.endpoint;
this.asyncCredentialUpdateEnabled = provider.asyncCredentialUpdateEnabled;
this.asyncThreadName = provider.asyncThreadName;
this.profileFile = provider.profileFile;
this.profileFileSupplier = provider.profileFileSupplier;
this.profileName = provider.profileName;
}

Expand Down Expand Up @@ -354,14 +367,25 @@ public void setAsyncThreadName(String asyncThreadName) {

@Override
public Builder profileFile(ProfileFile profileFile) {
this.profileFile = profileFile;
return this;
return profileFile(Optional.ofNullable(profileFile)
.map(ProfileFileSupplier::fixedProfileFile)
.orElse(null));
}

public void setProfileFile(ProfileFile profileFile) {
profileFile(profileFile);
}

@Override
public Builder profileFile(ProfileFileSupplier profileFileSupplier) {
this.profileFileSupplier = profileFileSupplier;
return this;
}

public void setProfileFile(ProfileFileSupplier profileFileSupplier) {
profileFile(profileFileSupplier);
}

@Override
public Builder profileName(String profileName) {
this.profileName = profileName;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* 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.credentials;

import static org.assertj.core.api.AssertionsForClassTypes.assertThat;

import java.util.Arrays;
import java.util.List;
import org.junit.jupiter.api.Test;
import software.amazon.awssdk.profiles.ProfileFile;
import software.amazon.awssdk.profiles.ProfileFileSupplier;
import software.amazon.awssdk.utils.StringInputStream;

class DefaultCredentialsProviderTest {

@Test
void resolveCredentials_requestFallsIntoProfileCredentialsProviderWithProfileFile_returnsCredentials() {
DefaultCredentialsProvider provider = DefaultCredentialsProvider
.builder()
.profileFile(credentialFile("test", "access", "secret"))
.profileName("test")
.build();

assertThat(provider.resolveCredentials()).satisfies(awsCredentials -> {
assertThat(awsCredentials.accessKeyId()).isEqualTo("access");
assertThat(awsCredentials.secretAccessKey()).isEqualTo("secret");
});
}

@Test
void resolveCredentials_requestFallsIntoProfileCredentialsProviderWithProfileFileSupplier_returnsCredentials() {
List<ProfileFile> profileFileList = Arrays.asList(credentialFile("test", "access", "secret"),
credentialFile("test", "modified", "update"));
ProfileFileSupplier profileFileSupplier = supply(profileFileList);

DefaultCredentialsProvider provider = DefaultCredentialsProvider
.builder()
.profileFile(profileFileSupplier)
.profileName("test")
.build();

assertThat(provider.resolveCredentials()).satisfies(awsCredentials -> {
assertThat(awsCredentials.accessKeyId()).isEqualTo("access");
assertThat(awsCredentials.secretAccessKey()).isEqualTo("secret");
});

assertThat(provider.resolveCredentials()).satisfies(awsCredentials -> {
assertThat(awsCredentials.accessKeyId()).isEqualTo("modified");
assertThat(awsCredentials.secretAccessKey()).isEqualTo("update");
});
}

private ProfileFile credentialFile(String credentialFile) {
return ProfileFile.builder()
.content(new StringInputStream(credentialFile))
.type(ProfileFile.Type.CREDENTIALS)
.build();
}

private ProfileFile credentialFile(String name, String accessKeyId, String secretAccessKey) {
String contents = String.format("[%s]\naws_access_key_id = %s\naws_secret_access_key = %s\n",
name, accessKeyId, secretAccessKey);
return credentialFile(contents);
}

private static ProfileFileSupplier supply(Iterable<ProfileFile> iterable) {
return iterable.iterator()::next;
}

}
Loading