1 /*
2  * Copyright 2014-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License").
5  * You may not use this file except in compliance with the License.
6  * A copy of the License is located at
7  *
8  *  http://aws.amazon.com/apache2.0
9  *
10  * or in the "license" file accompanying this file. This file is distributed
11  * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12  * express or implied. See the License for the specific language governing
13  * permissions and limitations under the License.
14  */

15 package com.amazonaws.auth.profile;
16
17 import com.amazonaws.auth.AWSCredentials;
18 import com.amazonaws.auth.AWSCredentialsProvider;
19 import com.amazonaws.auth.profile.internal.AwsProfileNameLoader;
20
21 import java.util.concurrent.Semaphore;
22
23 /**
24  * Credentials provider based on AWS configuration profiles. This provider vends AWSCredentials from
25  * the profile configuration file for the default profile, or for a specific, named profile. <p> AWS
26  * credential profiles allow you to share multiple sets of AWS security credentials between
27  * different tools like the AWS SDK for Java and the AWS CLI. <p> See
28  * http://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html
29  *
30  * @see ProfilesConfigFile
31  */

32 public class ProfileCredentialsProvider implements AWSCredentialsProvider {
33
34     /**
35      * Default refresh interval
36      */

37     private static final long DEFAULT_REFRESH_INTERVAL_NANOS = 5 * 60 * 1000 * 1000 * 1000L;
38
39     /**
40      * Default force reload interval
41      */

42     private static final long DEFAULT_FORCE_RELOAD_INTERVAL_NANOS =
43             2 * DEFAULT_REFRESH_INTERVAL_NANOS;
44
45     /**
46      * The credential profiles file from which this provider loads the security credentials. Lazily
47      * loaded by the double-check idiom.
48      */

49     private volatile ProfilesConfigFile profilesConfigFile;
50
51     /**
52      * When the profiles file was last refreshed.
53      */

54     private volatile long lastRefreshed;
55
56     /**
57      * The name of the credential profile
58      */

59     private final String profileName;
60
61     /**
62      * Used to have only one thread block on refresh, for applications making at least one call
63      * every REFRESH_INTERVAL_NANOS.
64      */

65     private final Semaphore refreshSemaphore = new Semaphore(1);
66
67     /**
68      * Refresh interval. Defaults to {@link #DEFAULT_REFRESH_INTERVAL_NANOS}
69      */

70     private long refreshIntervalNanos = DEFAULT_REFRESH_INTERVAL_NANOS;
71
72     /**
73      * Force reload interval. Defaults to {@link #DEFAULT_FORCE_RELOAD_INTERVAL_NANOS}
74      */

75     private long refreshForceIntervalNanos = DEFAULT_FORCE_RELOAD_INTERVAL_NANOS;
76
77     /**
78      * Creates a new profile credentials provider that returns the AWS security credentials
79      * configured for the default profile. Loading the credential file is deferred until the
80      * getCredentials() method is called.
81      */

82     public ProfileCredentialsProvider() {
83         this(null);
84     }
85
86     /**
87      * Creates a new profile credentials provider that returns the AWS security credentials
88      * configured for the named profile. Loading the credential file is deferred until the
89      * getCredentials() method is called.
90      *
91      * @param profileName The name of a local configuration profile.
92      */

93     public ProfileCredentialsProvider(String profileName) {
94         this((ProfilesConfigFile) null, profileName);
95     }
96
97     /**
98      * Creates a new profile credentials provider that returns the AWS security credentials for the
99      * specified profiles configuration file and profile name.
100      *
101      * @param profilesConfigFilePath The file path where the profile configuration file is located.
102      * @param profileName            The name of a configuration profile in the specified
103      *                               configuration file.
104      */

105     public ProfileCredentialsProvider(String profilesConfigFilePath, String profileName) {
106         this(new ProfilesConfigFile(profilesConfigFilePath), profileName);
107     }
108
109     /**
110      * Creates a new profile credentials provider that returns the AWS security credentials for the
111      * specified profiles configuration file and profile name.
112      *
113      * @param profilesConfigFile The profile configuration file containing the profiles used by this
114      *                           credentials provider or null to defer load to first use.
115      * @param profileName        The name of a configuration profile in the specified configuration
116      *                           file.
117      */

118     public ProfileCredentialsProvider(ProfilesConfigFile profilesConfigFile, String profileName) {
119         this.profilesConfigFile = profilesConfigFile;
120         if (this.profilesConfigFile != null) {
121             this.lastRefreshed = System.nanoTime();
122         }
123         if (profileName == null) {
124             this.profileName = AwsProfileNameLoader.INSTANCE.loadProfileName();
125         } else {
126             this.profileName = profileName;
127         }
128     }
129
130     @Override
131     public AWSCredentials getCredentials() {
132         if (profilesConfigFile == null) {
133             synchronized (this) {
134                 if (profilesConfigFile == null) {
135                     profilesConfigFile = new ProfilesConfigFile();
136                     lastRefreshed = System.nanoTime();
137                 }
138             }
139         }
140
141         // Periodically check if the file on disk has been modified
142         // since we last read it.
143         //
144         // For active applications, only have one thread block.
145         // For applications that use this method in bursts, ensure the
146         // credentials are never too stale.
147         long now = System.nanoTime();
148         long age = now - lastRefreshed;
149         if (age > refreshForceIntervalNanos) {
150             refresh();
151         } else if (age > refreshIntervalNanos) {
152             if (refreshSemaphore.tryAcquire()) {
153                 try {
154                     refresh();
155                 } finally {
156                     refreshSemaphore.release();
157                 }
158             }
159         }
160
161         return profilesConfigFile.getCredentials(profileName);
162     }
163
164     @Override
165     public void refresh() {
166         if (profilesConfigFile != null) {
167             profilesConfigFile.refresh();
168             lastRefreshed = System.nanoTime();
169         }
170     }
171
172     /**
173      * Gets the refresh interval in nanoseconds.
174      *
175      * @return nanoseconds
176      */

177     public long getRefreshIntervalNanos() {
178         return refreshIntervalNanos;
179     }
180
181     /**
182      * Sets the refresh interval in nanoseconds.
183      *
184      * @param refreshIntervalNanos nanoseconds
185      */

186     public void setRefreshIntervalNanos(long refreshIntervalNanos) {
187         this.refreshIntervalNanos = refreshIntervalNanos;
188     }
189
190     /**
191      * Gets the forced refresh interval in nanoseconds.
192      *
193      * @return nanoseconds
194      */

195     public long getRefreshForceIntervalNanos() {
196         return refreshForceIntervalNanos;
197     }
198
199     /**
200      * Sets the forced refresh interval in nanoseconds.
201      */

202     public void setRefreshForceIntervalNanos(long refreshForceIntervalNanos) {
203         this.refreshForceIntervalNanos = refreshForceIntervalNanos;
204     }
205 }
206