/*
* Copyright 2014-2017 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 com.amazonaws.auth.profile;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.auth.profile.internal.AwsProfileNameLoader;
import java.util.concurrent.Semaphore;
/**
* Credentials provider based on AWS configuration profiles. This provider vends AWSCredentials from
* the profile configuration file for the default profile, or for a specific, named profile. <p> AWS
* credential profiles allow you to share multiple sets of AWS security credentials between
* different tools like the AWS SDK for Java and the AWS CLI. <p> See
* http://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html
*
* @see ProfilesConfigFile
*/
public class ProfileCredentialsProvider implements AWSCredentialsProvider {
/**
* Default refresh interval
*/
private static final long DEFAULT_REFRESH_INTERVAL_NANOS = 5 * 60 * 1000 * 1000 * 1000L;
/**
* Default force reload interval
*/
private static final long DEFAULT_FORCE_RELOAD_INTERVAL_NANOS =
2 * DEFAULT_REFRESH_INTERVAL_NANOS;
/**
* The credential profiles file from which this provider loads the security credentials. Lazily
* loaded by the double-check idiom.
*/
private volatile ProfilesConfigFile profilesConfigFile;
/**
* When the profiles file was last refreshed.
*/
private volatile long lastRefreshed;
/**
* The name of the credential profile
*/
private final String profileName;
/**
* Used to have only one thread block on refresh, for applications making at least one call
* every REFRESH_INTERVAL_NANOS.
*/
private final Semaphore refreshSemaphore = new Semaphore(1);
/**
* Refresh interval. Defaults to {@link #DEFAULT_REFRESH_INTERVAL_NANOS}
*/
private long refreshIntervalNanos = DEFAULT_REFRESH_INTERVAL_NANOS;
/**
* Force reload interval. Defaults to {@link #DEFAULT_FORCE_RELOAD_INTERVAL_NANOS}
*/
private long refreshForceIntervalNanos = DEFAULT_FORCE_RELOAD_INTERVAL_NANOS;
/**
* Creates a new profile credentials provider that returns the AWS security credentials
* configured for the default profile. Loading the credential file is deferred until the
* getCredentials() method is called.
*/
public ProfileCredentialsProvider() {
this(null);
}
/**
* Creates a new profile credentials provider that returns the AWS security credentials
* configured for the named profile. Loading the credential file is deferred until the
* getCredentials() method is called.
*
* @param profileName The name of a local configuration profile.
*/
public ProfileCredentialsProvider(String profileName) {
this((ProfilesConfigFile) null, profileName);
}
/**
* Creates a new profile credentials provider that returns the AWS security credentials for the
* specified profiles configuration file and profile name.
*
* @param profilesConfigFilePath The file path where the profile configuration file is located.
* @param profileName The name of a configuration profile in the specified
* configuration file.
*/
public ProfileCredentialsProvider(String profilesConfigFilePath, String profileName) {
this(new ProfilesConfigFile(profilesConfigFilePath), profileName);
}
/**
* Creates a new profile credentials provider that returns the AWS security credentials for the
* specified profiles configuration file and profile name.
*
* @param profilesConfigFile The profile configuration file containing the profiles used by this
* credentials provider or null to defer load to first use.
* @param profileName The name of a configuration profile in the specified configuration
* file.
*/
public ProfileCredentialsProvider(ProfilesConfigFile profilesConfigFile, String profileName) {
this.profilesConfigFile = profilesConfigFile;
if (this.profilesConfigFile != null) {
this.lastRefreshed = System.nanoTime();
}
if (profileName == null) {
this.profileName = AwsProfileNameLoader.INSTANCE.loadProfileName();
} else {
this.profileName = profileName;
}
}
@Override
public AWSCredentials getCredentials() {
if (profilesConfigFile == null) {
synchronized (this) {
if (profilesConfigFile == null) {
profilesConfigFile = new ProfilesConfigFile();
lastRefreshed = System.nanoTime();
}
}
}
// Periodically check if the file on disk has been modified
// since we last read it.
//
// For active applications, only have one thread block.
// For applications that use this method in bursts, ensure the
// credentials are never too stale.
long now = System.nanoTime();
long age = now - lastRefreshed;
if (age > refreshForceIntervalNanos) {
refresh();
} else if (age > refreshIntervalNanos) {
if (refreshSemaphore.tryAcquire()) {
try {
refresh();
} finally {
refreshSemaphore.release();
}
}
}
return profilesConfigFile.getCredentials(profileName);
}
@Override
public void refresh() {
if (profilesConfigFile != null) {
profilesConfigFile.refresh();
lastRefreshed = System.nanoTime();
}
}
/**
* Gets the refresh interval in nanoseconds.
*
* @return nanoseconds
*/
public long getRefreshIntervalNanos() {
return refreshIntervalNanos;
}
/**
* Sets the refresh interval in nanoseconds.
*
* @param refreshIntervalNanos nanoseconds
*/
public void setRefreshIntervalNanos(long refreshIntervalNanos) {
this.refreshIntervalNanos = refreshIntervalNanos;
}
/**
* Gets the forced refresh interval in nanoseconds.
*
* @return nanoseconds
*/
public long getRefreshForceIntervalNanos() {
return refreshForceIntervalNanos;
}
/**
* Sets the forced refresh interval in nanoseconds.
*/
public void setRefreshForceIntervalNanos(long refreshForceIntervalNanos) {
this.refreshForceIntervalNanos = refreshForceIntervalNanos;
}
}