Skip to content

Commit ec6e648

Browse files
authored
Merge pull request #4431 from aws/alexwoo/master/loginCredentialProvider_credInterface
Add LoginCredentialsProvider
2 parents 15c6e6a + f914ca1 commit ec6e648

File tree

3 files changed

+327
-0
lines changed

3 files changed

+327
-0
lines changed

core/sdk-core/src/main/java/software/amazon/awssdk/core/useragent/BusinessMetricFeatureId.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ public enum BusinessMetricFeatureId {
6464
CREDENTIALS_PROCESS("w"),
6565
CREDENTIALS_HTTP("z"),
6666
CREDENTIALS_IMDS("0"),
67+
CREDENTIALS_PROFILE_LOGIN("AC"),
68+
CREDENTIALS_LOGIN("AD"),
6769
UNKNOWN("Unknown");
6870

6971
private static final Map<String, BusinessMetricFeatureId> VALUE_MAP =
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright 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+
16+
package software.amazon.awssdk.services.signin.auth;
17+
18+
import software.amazon.awssdk.annotations.SdkInternalApi;
19+
import software.amazon.awssdk.utils.SystemSetting;
20+
21+
@SdkInternalApi
22+
public class LoginCacheDirectorySystemSetting implements SystemSetting {
23+
24+
@Override
25+
public String property() {
26+
return "aws.loginCacheDirectory";
27+
}
28+
29+
@Override
30+
public String environmentVariable() {
31+
return "AWS_LOGIN_CACHE_DIRECTORY";
32+
}
33+
34+
@Override
35+
public String defaultValue() {
36+
return null;
37+
}
38+
}
Lines changed: 287 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,287 @@
1+
/*
2+
* Copyright 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+
16+
package software.amazon.awssdk.services.signin.auth;
17+
18+
import static software.amazon.awssdk.utils.UserHomeDirectoryUtils.userHomeDirectory;
19+
import static software.amazon.awssdk.utils.Validate.notNull;
20+
import static software.amazon.awssdk.utils.Validate.paramNotBlank;
21+
22+
import java.nio.file.Path;
23+
import java.nio.file.Paths;
24+
import java.time.Duration;
25+
import java.util.Optional;
26+
import software.amazon.awssdk.annotations.SdkPublicApi;
27+
import software.amazon.awssdk.annotations.ThreadSafe;
28+
import software.amazon.awssdk.auth.credentials.AwsCredentials;
29+
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
30+
import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId;
31+
import software.amazon.awssdk.services.signin.SigninClient;
32+
import software.amazon.awssdk.services.signin.model.CreateOAuth2TokenRequest;
33+
import software.amazon.awssdk.utils.SdkAutoCloseable;
34+
import software.amazon.awssdk.utils.StringUtils;
35+
import software.amazon.awssdk.utils.builder.CopyableBuilder;
36+
import software.amazon.awssdk.utils.builder.ToCopyableBuilder;
37+
import software.amazon.awssdk.utils.cache.CachedSupplier;
38+
import software.amazon.awssdk.utils.cache.NonBlocking;
39+
import software.amazon.awssdk.utils.cache.RefreshResult;
40+
41+
/**
42+
* An implementation of {@link AwsCredentialsProvider} that loads and refreshes AWS Login Session credentials.
43+
* It periodically sends a {@link CreateOAuth2TokenRequest} to the AWS
44+
* Sign-On Service to refresh short-lived sessions to use for authentication. These sessions are updated using a single
45+
* calling thread (by default) or asynchronously (if {@link Builder#asyncCredentialUpdateEnabled(Boolean)} is set).
46+
*
47+
* If the credentials are not successfully updated before expiration, calls to {@link #resolveCredentials()} will block until
48+
* they are updated successfully.
49+
*
50+
* Users of this provider must {@link #close()} it when they are finished using it.
51+
*
52+
* This is created using {@link LoginCredentialsProvider#builder()}.
53+
*/
54+
@SdkPublicApi
55+
@ThreadSafe
56+
public class LoginCredentialsProvider implements
57+
AwsCredentialsProvider, SdkAutoCloseable,
58+
ToCopyableBuilder<LoginCredentialsProvider.Builder, LoginCredentialsProvider> {
59+
private static final String PROVIDER_NAME = BusinessMetricFeatureId.CREDENTIALS_LOGIN.value();
60+
61+
private static final Duration DEFAULT_STALE_TIME = Duration.ofMinutes(1);
62+
private static final Duration DEFAULT_PREFETCH_TIME = Duration.ofMinutes(5);
63+
private static final Path DEFAULT_TOKEN_LOCATION = Paths.get(userHomeDirectory(), ".aws", "login", "cache");
64+
65+
private static final String ASYNC_THREAD_NAME = "sdk-login-credentials-provider";
66+
67+
private final String loginSession;
68+
private final String sourceChain;
69+
private final String providerName;
70+
71+
private final SigninClient signinClient;
72+
private final Duration staleTime;
73+
private final Duration prefetchTime;
74+
private final Path tokenCacheLocation;
75+
76+
private final CachedSupplier<AwsCredentials> credentialCache;
77+
78+
private final Boolean asyncCredentialUpdateEnabled;
79+
80+
public LoginCredentialsProvider(BuilderImpl builder) {
81+
this.signinClient = notNull(builder.signinClient, "SigninClient must not be null.");
82+
this.loginSession = paramNotBlank(builder.loginSession, "LoginSession");
83+
84+
this.staleTime = Optional.ofNullable(builder.staleTime).orElse(DEFAULT_STALE_TIME);
85+
this.prefetchTime = Optional.ofNullable(builder.prefetchTime).orElse(DEFAULT_PREFETCH_TIME);
86+
this.sourceChain = builder.sourceChain;
87+
88+
this.providerName = StringUtils.isEmpty(builder.sourceChain)
89+
? PROVIDER_NAME
90+
: builder.sourceChain + "," + PROVIDER_NAME;
91+
92+
this.tokenCacheLocation = Optional.ofNullable(builder.tokenCacheLocation).orElseGet(
93+
() -> new LoginCacheDirectorySystemSetting().getStringValue()
94+
.map(p -> Paths.get(p))
95+
.orElse(DEFAULT_TOKEN_LOCATION));
96+
97+
this.asyncCredentialUpdateEnabled = builder.asyncCredentialUpdateEnabled;
98+
CachedSupplier.Builder<AwsCredentials> cacheBuilder =
99+
CachedSupplier.builder(this::updateSigninCredentials)
100+
.cachedValueName(toString());
101+
if (builder.asyncCredentialUpdateEnabled) {
102+
cacheBuilder.prefetchStrategy(new NonBlocking(ASYNC_THREAD_NAME));
103+
}
104+
105+
this.credentialCache = cacheBuilder.build();
106+
}
107+
108+
/**
109+
* Update the expiring session SSO credentials by calling SSO. Invoked by {@link CachedSupplier} when the credentials are
110+
* close to expiring.
111+
*/
112+
private RefreshResult<AwsCredentials> updateSigninCredentials() {
113+
// TODO: Future PRs will implement this
114+
throw new UnsupportedOperationException();
115+
}
116+
117+
/**
118+
* The amount of time, relative to session token expiration, that the cached credentials are considered stale and should no
119+
* longer be used. All threads will block until the value is updated.
120+
*/
121+
public Duration staleTime() {
122+
return staleTime;
123+
}
124+
125+
/**
126+
* The amount of time, relative to session token expiration, that the cached credentials are considered close to stale and
127+
* should be updated.
128+
*/
129+
public Duration prefetchTime() {
130+
return prefetchTime;
131+
}
132+
133+
/**
134+
* Get a builder for creating a custom {@link LoginCredentialsProvider}.
135+
*/
136+
public static BuilderImpl builder() {
137+
return new BuilderImpl();
138+
}
139+
140+
@Override
141+
public AwsCredentials resolveCredentials() {
142+
return credentialCache.get();
143+
}
144+
145+
@Override
146+
public void close() {
147+
credentialCache.close();
148+
}
149+
150+
@Override
151+
public Builder toBuilder() {
152+
return new BuilderImpl(this);
153+
}
154+
155+
/**
156+
* A builder for creating a custom {@link LoginCredentialsProvider}.
157+
*/
158+
public interface Builder extends CopyableBuilder<Builder, LoginCredentialsProvider> {
159+
/**
160+
* Configure the {@link SigninClient} to use when calling Signin to update the session. This client should not be shut
161+
* down as long as this credentials provider is in use.
162+
*/
163+
Builder signinClient(SigninClient signinClient);
164+
165+
/**
166+
* Configure whether the provider should fetch credentials asynchronously in the background. If this is true, threads are
167+
* less likely to block when credentials are loaded, but additional resources are used to maintain the provider.
168+
*
169+
* <p>By default, this is disabled.</p>
170+
*/
171+
Builder asyncCredentialUpdateEnabled(Boolean asyncCredentialUpdateEnabled);
172+
173+
/**
174+
* Configure the amount of time, relative to signin token expiration, that the cached credentials are considered stale and
175+
* should no longer be used. All threads will block until the value is updated.
176+
*
177+
* <p>By default, this is 1 minute.</p>
178+
*/
179+
Builder staleTime(Duration staleTime);
180+
181+
/**
182+
* Configure the amount of time, relative to signin token expiration, that the cached credentials are considered close to
183+
* stale and should be updated.
184+
* <p>
185+
* Prefetch updates will occur between the specified time and the stale time of the provider. Prefetch updates may be
186+
* asynchronous. See {@link #asyncCredentialUpdateEnabled}.
187+
*
188+
* <p>By default, this is 5 minutes.</p>
189+
*/
190+
Builder prefetchTime(Duration prefetchTime);
191+
192+
/**
193+
* The login session name used to retrieve the cached token.
194+
*/
195+
Builder loginSession(String loginSession);
196+
197+
/**
198+
* Configure the path to the token cache. Defaults to the value of the AWS_LOGIN_CACHE_DIRECTORY
199+
* environment variable or if unset to HOME/.aws/login/cache.
200+
*/
201+
Builder tokenCacheLocation(Path tokenCacheLocation);
202+
203+
/**
204+
* An optional string denoting previous credentials providers that are chained with this one. This method is primarily
205+
* intended for use by AWS SDK internal components and should not be used directly by external users.
206+
*/
207+
Builder sourceChain(String sourceChain);
208+
209+
/**
210+
* Create a {@link LoginCredentialsProvider} using the configuration applied to this builder.
211+
*
212+
* @return
213+
*/
214+
@Override
215+
LoginCredentialsProvider build();
216+
}
217+
218+
protected static final class BuilderImpl implements Builder {
219+
private Boolean asyncCredentialUpdateEnabled = false;
220+
private SigninClient signinClient;
221+
private Duration staleTime;
222+
private Duration prefetchTime;
223+
private String loginSession;
224+
private String sourceChain;
225+
private Path tokenCacheLocation;
226+
227+
BuilderImpl() {
228+
229+
}
230+
231+
public BuilderImpl(LoginCredentialsProvider provider) {
232+
this.asyncCredentialUpdateEnabled = provider.asyncCredentialUpdateEnabled;
233+
this.signinClient = provider.signinClient;
234+
this.staleTime = provider.staleTime;
235+
this.prefetchTime = provider.prefetchTime;
236+
this.loginSession = provider.loginSession;
237+
this.sourceChain = provider.sourceChain;
238+
}
239+
240+
@Override
241+
public Builder signinClient(SigninClient signinClient) {
242+
this.signinClient = signinClient;
243+
return this;
244+
}
245+
246+
@Override
247+
public Builder asyncCredentialUpdateEnabled(Boolean asyncCredentialUpdateEnabled) {
248+
this.asyncCredentialUpdateEnabled = asyncCredentialUpdateEnabled;
249+
return this;
250+
}
251+
252+
@Override
253+
public Builder staleTime(Duration staleTime) {
254+
this.staleTime = staleTime;
255+
return this;
256+
}
257+
258+
@Override
259+
public Builder prefetchTime(Duration prefetchTime) {
260+
this.prefetchTime = prefetchTime;
261+
return this;
262+
}
263+
264+
@Override
265+
public Builder loginSession(String loginSession) {
266+
this.loginSession = loginSession;
267+
return this;
268+
}
269+
270+
@Override
271+
public Builder sourceChain(String sourceChain) {
272+
this.sourceChain = sourceChain;
273+
return this;
274+
}
275+
276+
@Override
277+
public Builder tokenCacheLocation(Path tokenCacheLocation) {
278+
this.tokenCacheLocation = tokenCacheLocation;
279+
return this;
280+
}
281+
282+
@Override
283+
public LoginCredentialsProvider build() {
284+
return new LoginCredentialsProvider(this);
285+
}
286+
}
287+
}

0 commit comments

Comments
 (0)