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