software.amazon.awssdk.auth.credentials.ProfileCredentialsProvider Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of auth Show documentation
Show all versions of auth Show documentation
The AWS SDK for Java - Auth module holds the classes that are used for authentication with services
/*
* 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 java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Supplier;
import software.amazon.awssdk.annotations.SdkPublicApi;
import software.amazon.awssdk.annotations.SdkTestInternalApi;
import software.amazon.awssdk.auth.credentials.internal.ProfileCredentialsUtils;
import software.amazon.awssdk.core.exception.SdkClientException;
import software.amazon.awssdk.profiles.ProfileFile;
import software.amazon.awssdk.profiles.ProfileFileSupplier;
import software.amazon.awssdk.profiles.ProfileFileSystemSetting;
import software.amazon.awssdk.utils.IoUtils;
import software.amazon.awssdk.utils.SdkAutoCloseable;
import software.amazon.awssdk.utils.ToString;
import software.amazon.awssdk.utils.builder.CopyableBuilder;
import software.amazon.awssdk.utils.builder.ToCopyableBuilder;
/**
* Credentials provider based on AWS configuration profiles. This loads credentials from a {@link ProfileFile}, allowing you to
* share multiple sets of AWS security credentials between different tools like the AWS SDK for Java and the AWS CLI.
*
* See http://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html
*
* If this credentials provider is loading assume-role credentials from STS, it should be cleaned up with {@link #close()} if
* it is no longer being used.
*
* @see ProfileFile
*/
@SdkPublicApi
public final class ProfileCredentialsProvider
implements AwsCredentialsProvider,
SdkAutoCloseable,
ToCopyableBuilder {
private volatile AwsCredentialsProvider credentialsProvider;
private final RuntimeException loadException;
private final Supplier profileFile;
private volatile ProfileFile currentProfileFile;
private final String profileName;
private final Supplier defaultProfileFileLoader;
private final Object credentialsProviderLock = new Object();
/**
* @see #builder()
*/
private ProfileCredentialsProvider(BuilderImpl builder) {
this.defaultProfileFileLoader = builder.defaultProfileFileLoader;
RuntimeException thrownException = null;
String selectedProfileName = null;
Supplier selectedProfileSupplier = null;
try {
selectedProfileName = Optional.ofNullable(builder.profileName)
.orElseGet(ProfileFileSystemSetting.AWS_PROFILE::getStringValueOrThrow);
selectedProfileSupplier =
Optional.ofNullable(builder.profileFile)
.orElseGet(() -> ProfileFileSupplier.fixedProfileFile(builder.defaultProfileFileLoader.get()));
} catch (RuntimeException e) {
// If we couldn't load the credentials provider for some reason, save an exception describing why. This exception
// will only be raised on calls to resolveCredentials. We don't want to raise an exception here because it may be
// expected (eg. in the default credential chain).
thrownException = e;
}
this.loadException = thrownException;
this.profileName = selectedProfileName;
this.profileFile = selectedProfileSupplier;
}
/**
* Create a {@link ProfileCredentialsProvider} using the {@link ProfileFile#defaultProfileFile()} and default profile name.
* Use {@link #builder()} for defining a custom {@link ProfileCredentialsProvider}.
*/
public static ProfileCredentialsProvider create() {
return builder().build();
}
/**
* Create a {@link ProfileCredentialsProvider} using the given profile name and {@link ProfileFile#defaultProfileFile()}. Use
* {@link #builder()} for defining a custom {@link ProfileCredentialsProvider}.
*
* @param profileName the name of the profile to use from the {@link ProfileFile#defaultProfileFile()}
*/
public static ProfileCredentialsProvider create(String profileName) {
return builder().profileName(profileName).build();
}
/**
* Get a builder for creating a custom {@link ProfileCredentialsProvider}.
*/
public static Builder builder() {
return new BuilderImpl();
}
@Override
public AwsCredentials resolveCredentials() {
if (loadException != null) {
throw loadException;
}
ProfileFile cachedOrRefreshedProfileFile = refreshProfileFile();
if (shouldUpdateCredentialsProvider(cachedOrRefreshedProfileFile)) {
synchronized (credentialsProviderLock) {
if (shouldUpdateCredentialsProvider(cachedOrRefreshedProfileFile)) {
currentProfileFile = cachedOrRefreshedProfileFile;
handleProfileFileReload(cachedOrRefreshedProfileFile);
}
}
}
return credentialsProvider.resolveCredentials();
}
private void handleProfileFileReload(ProfileFile profileFile) {
credentialsProvider = createCredentialsProvider(profileFile, profileName);
}
private ProfileFile refreshProfileFile() {
return profileFile.get();
}
private boolean shouldUpdateCredentialsProvider(ProfileFile profileFile) {
return credentialsProvider == null || !Objects.equals(currentProfileFile, profileFile);
}
@Override
public String toString() {
return ToString.builder("ProfileCredentialsProvider")
.add("profileName", profileName)
.add("profileFile", currentProfileFile)
.build();
}
@Override
public void close() {
// The delegate credentials provider may be closeable (eg. if it's an STS credentials provider). In this case, we should
// clean it up when this credentials provider is closed.
IoUtils.closeIfCloseable(credentialsProvider, null);
}
@Override
public Builder toBuilder() {
return new BuilderImpl(this);
}
private AwsCredentialsProvider createCredentialsProvider(ProfileFile profileFile, String profileName) {
// Load the profile and credentials provider
return profileFile.profile(profileName)
.flatMap(p -> new ProfileCredentialsUtils(profileFile, p, profileFile::profile).credentialsProvider())
.orElseThrow(() -> {
String errorMessage = String.format("Profile file contained no credentials for " +
"profile '%s': %s", profileName, profileFile);
return SdkClientException.builder().message(errorMessage).build();
});
}
/**
* A builder for creating a custom {@link ProfileCredentialsProvider}.
*/
public interface Builder extends CopyableBuilder {
/**
* Define the profile file that should be used by this credentials provider. By default, the
* {@link ProfileFile#defaultProfileFile()} is used.
* @see #profileFile(Supplier)
*/
Builder profileFile(ProfileFile profileFile);
/**
* Similar to {@link #profileFile(ProfileFile)}, but takes a lambda to configure a new {@link ProfileFile.Builder}. This
* removes the need to called {@link ProfileFile#builder()} and {@link ProfileFile.Builder#build()}.
*/
Builder profileFile(Consumer profileFile);
/**
* Define the mechanism for loading profile files.
*
* @param profileFileSupplier Supplier interface for generating a ProfileFile instance.
* @see #profileFile(ProfileFile)
*/
Builder profileFile(Supplier profileFileSupplier);
/**
* Define the name of the profile that should be used by this credentials provider. By default, the value in
* {@link ProfileFileSystemSetting#AWS_PROFILE} is used.
*/
Builder profileName(String profileName);
/**
* Create a {@link ProfileCredentialsProvider} using the configuration applied to this builder.
*/
@Override
ProfileCredentialsProvider build();
}
static final class BuilderImpl implements Builder {
private Supplier profileFile;
private String profileName;
private Supplier defaultProfileFileLoader = ProfileFile::defaultProfileFile;
BuilderImpl() {
}
BuilderImpl(ProfileCredentialsProvider provider) {
this.profileName = provider.profileName;
this.defaultProfileFileLoader = provider.defaultProfileFileLoader;
this.profileFile = provider.profileFile;
}
@Override
public Builder profileFile(ProfileFile profileFile) {
return profileFile(Optional.ofNullable(profileFile)
.map(ProfileFileSupplier::fixedProfileFile)
.orElse(null));
}
public void setProfileFile(ProfileFile profileFile) {
profileFile(profileFile);
}
@Override
public Builder profileFile(Consumer profileFile) {
return profileFile(ProfileFile.builder().applyMutation(profileFile).build());
}
@Override
public Builder profileFile(Supplier profileFileSupplier) {
this.profileFile = profileFileSupplier;
return this;
}
public void setProfileFile(Supplier supplier) {
profileFile(supplier);
}
@Override
public Builder profileName(String profileName) {
this.profileName = profileName;
return this;
}
public void setProfileName(String profileName) {
profileName(profileName);
}
@Override
public ProfileCredentialsProvider build() {
return new ProfileCredentialsProvider(this);
}
/**
* Override the default configuration file to be used when the customer does not explicitly set
* profileFile(ProfileFile) or profileFileSupplier(supplier);
* {@link #profileFile(ProfileFile)}. Use of this method is
* only useful for testing the default behavior.
*/
@SdkTestInternalApi
Builder defaultProfileFileLoader(Supplier defaultProfileFileLoader) {
this.defaultProfileFileLoader = defaultProfileFileLoader;
return this;
}
}
}