Skip to content

Commit bc4ac1e

Browse files
committed
More cleanups
1 parent 511b5af commit bc4ac1e

16 files changed

Lines changed: 93 additions & 108 deletions

File tree

core/auth/src/main/java/software/amazon/awssdk/auth/credentials/AwsCredentialsProviderChain.java

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -132,23 +132,24 @@ public AwsCredentials resolveCredentials() {
132132
}
133133

134134
@Override
135+
@SuppressWarnings("unchecked")
135136
public CompletableFuture<Void> invalidate(AwsCredentialsIdentity identity) {
136-
List<CompletableFuture<Void>> futures = new ArrayList<>();
137-
for (IdentityProvider<? extends AwsCredentialsIdentity> provider : credentialsProviders) {
138-
try {
139-
@SuppressWarnings("unchecked")
140-
IdentityProvider<AwsCredentialsIdentity> typedProvider =
141-
(IdentityProvider<AwsCredentialsIdentity>) provider;
142-
CompletableFuture<Void> future = typedProvider.invalidate(identity);
143-
futures.add(future.exceptionally(e -> {
137+
CompletableFuture<?>[] futures = credentialsProviders.stream()
138+
.map(provider -> {
139+
try {
140+
return ((IdentityProvider<AwsCredentialsIdentity>) provider)
141+
.invalidate(identity)
142+
.exceptionally(e -> {
143+
log.debug(() -> "Failed to invalidate provider " + provider + ": " + e.getMessage(), e);
144+
return null;
145+
});
146+
} catch (Exception e) {
144147
log.debug(() -> "Failed to invalidate provider " + provider + ": " + e.getMessage(), e);
145-
return null;
146-
}));
147-
} catch (Exception e) {
148-
log.debug(() -> "Failed to invalidate provider " + provider + ": " + e.getMessage(), e);
149-
}
150-
}
151-
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
148+
return CompletableFuture.<Void>completedFuture(null);
149+
}
150+
})
151+
.toArray(CompletableFuture<?>[]::new);
152+
return CompletableFuture.allOf(futures);
152153
}
153154

154155
@Override

core/auth/src/main/java/software/amazon/awssdk/auth/credentials/ContainerCredentialsProvider.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ public AwsCredentials resolveCredentials() {
192192
@Override
193193
public CompletableFuture<Void> invalidate(AwsCredentialsIdentity identity) {
194194
return CredentialsInvalidationUtils.invalidateCredentialsCache(
195-
identity, credentialsCache, cachedCreds -> (AwsCredentialsIdentity) cachedCreds);
195+
identity, credentialsCache, AwsCredentialsIdentity::accessKeyId);
196196
}
197197

198198
@Override

core/auth/src/main/java/software/amazon/awssdk/auth/credentials/InstanceProfileCredentialsProvider.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ public AwsCredentials resolveCredentials() {
173173
@Override
174174
public CompletableFuture<Void> invalidate(AwsCredentialsIdentity identity) {
175175
return CredentialsInvalidationUtils.invalidateCredentialsCache(
176-
identity, credentialsCache, cachedCreds -> (AwsCredentialsIdentity) cachedCreds);
176+
identity, credentialsCache, AwsCredentialsIdentity::accessKeyId);
177177
}
178178

179179
private RefreshResult<AwsCredentials> refreshCredentials() {

core/auth/src/main/java/software/amazon/awssdk/auth/credentials/ProcessCredentialsProvider.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ public AwsCredentials resolveCredentials() {
172172
@Override
173173
public CompletableFuture<Void> invalidate(AwsCredentialsIdentity identity) {
174174
return CredentialsInvalidationUtils.invalidateCredentialsCache(
175-
identity, processCredentialCache, cachedCreds -> (AwsCredentialsIdentity) cachedCreds);
175+
identity, processCredentialCache, AwsCredentialsIdentity::accessKeyId);
176176
}
177177

178178
private RefreshResult<AwsCredentials> refreshCredentials() {

core/auth/src/main/java/software/amazon/awssdk/auth/credentials/ProfileCredentialsProvider.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ public void close() {
162162

163163
@Override
164164
public CompletableFuture<Void> invalidate(AwsCredentialsIdentity identity) {
165+
// Local copy to avoid TOCTOU race on the volatile field during concurrent profile reloads.
165166
AwsCredentialsProvider provider = credentialsProvider;
166167
if (provider != null) {
167168
return provider.invalidate(identity);

core/auth/src/main/java/software/amazon/awssdk/auth/credentials/WebIdentityTokenFileCredentialsProvider.java

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
import software.amazon.awssdk.core.SdkSystemSetting;
2828
import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId;
2929
import software.amazon.awssdk.identity.spi.AwsCredentialsIdentity;
30-
import software.amazon.awssdk.identity.spi.IdentityProvider;
3130
import software.amazon.awssdk.utils.IoUtils;
3231
import software.amazon.awssdk.utils.SdkAutoCloseable;
3332
import software.amazon.awssdk.utils.ToString;
@@ -168,11 +167,8 @@ public void close() {
168167

169168
@Override
170169
public CompletableFuture<Void> invalidate(AwsCredentialsIdentity identity) {
171-
if (credentialsProvider instanceof IdentityProvider) {
172-
@SuppressWarnings("unchecked")
173-
IdentityProvider<AwsCredentialsIdentity> provider =
174-
(IdentityProvider<AwsCredentialsIdentity>) credentialsProvider;
175-
return provider.invalidate(identity);
170+
if (credentialsProvider != null) {
171+
return credentialsProvider.invalidate(identity);
176172
}
177173
return CompletableFuture.completedFuture(null);
178174
}

core/auth/src/main/java/software/amazon/awssdk/auth/credentials/internal/CredentialsInvalidationUtils.java

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -35,38 +35,34 @@ private CredentialsInvalidationUtils() {
3535
}
3636

3737
/**
38-
* Invalidates the given cache if the rejected identity's access key ID matches the cached credential's access key ID.
38+
* Invalidates the given cache if the rejected identity's access key ID matches the access key ID of the cached value.
3939
*
4040
* <p>This method encapsulates the common pattern used by caching credential providers:
4141
* <ol>
4242
* <li>Extract the rejected access key ID from the identity</li>
43-
* <li>Compare it against the currently cached credentials</li>
43+
* <li>Use the {@code accessKeyIdExtractor} to get the access key ID from the currently cached value</li>
4444
* <li>If they match, mark the cache for mandatory refresh</li>
4545
* </ol>
4646
*
47+
* <p>For providers whose cache directly stores {@link AwsCredentialsIdentity} (or a subtype), pass
48+
* {@code AwsCredentialsIdentity::accessKeyId} as the extractor. For providers that cache a wrapper type,
49+
* provide an appropriate extraction function (e.g., {@code holder -> holder.sessionCredentials().accessKeyId()}).
50+
*
4751
* @param identity The identity that was rejected by the service.
4852
* @param cache The cached supplier to invalidate.
49-
* @param credentialsExtractor A function that extracts the {@link AwsCredentialsIdentity} from the cached value type.
50-
* For providers that cache {@code AwsCredentials} directly, use {@code Function.identity()}.
51-
* For providers that cache a holder object, provide the appropriate extraction function.
53+
* @param accessKeyIdExtractor A function that extracts the access key ID from the cached value.
5254
* @param <T> The type of value stored in the cache.
5355
* @return A {@link CompletableFuture} that completes when the invalidation check is done, or completes exceptionally
5456
* if an error occurs during the invalidation.
5557
*/
5658
public static <T> CompletableFuture<Void> invalidateCredentialsCache(
5759
AwsCredentialsIdentity identity,
5860
CachedSupplier<T> cache,
59-
Function<T, AwsCredentialsIdentity> credentialsExtractor) {
61+
Function<T, String> accessKeyIdExtractor) {
6062

6163
try {
6264
String rejectedAccessKeyId = identity.accessKeyId();
63-
cache.invalidate(cachedValue -> {
64-
AwsCredentialsIdentity cachedIdentity = credentialsExtractor.apply(cachedValue);
65-
if (cachedIdentity == null) {
66-
return false;
67-
}
68-
return rejectedAccessKeyId.equals(cachedIdentity.accessKeyId());
69-
});
65+
cache.invalidate(cachedValue -> rejectedAccessKeyId.equals(accessKeyIdExtractor.apply(cachedValue)));
7066
return CompletableFuture.completedFuture(null);
7167
} catch (Exception e) {
7268
return CompletableFutureUtils.failedFuture(e);

core/auth/src/test/java/software/amazon/awssdk/auth/credentials/IdentityProviderInvalidateTest.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,13 +66,12 @@ class IdentityProviderInvalidateTest {
6666
@BeforeEach
6767
void setup() {
6868
environmentVariableHelper.reset();
69-
System.setProperty(SdkSystemSetting.AWS_EC2_METADATA_SERVICE_ENDPOINT.property(),
70-
"http://localhost:" + wireMockServer.getPort());
69+
environmentVariableHelper.set(SdkSystemSetting.AWS_EC2_METADATA_SERVICE_ENDPOINT,
70+
"http://localhost:" + wireMockServer.getPort());
7171
}
7272

7373
@AfterAll
7474
static void teardown() {
75-
System.clearProperty(SdkSystemSetting.AWS_EC2_METADATA_SERVICE_ENDPOINT.property());
7675
environmentVariableHelper.reset();
7776
}
7877

core/aws-core/src/main/java/software/amazon/awssdk/awscore/exception/AwsServiceException.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,19 @@ public boolean isThrottlingException() {
134134
.orElse(false);
135135
}
136136

137+
/**
138+
* Checks if the exception indicates an authentication error where the credentials were rejected,
139+
* based on the AWS error code (e.g., {@code ExpiredToken}, {@code InvalidToken}, {@code AuthFailure}).
140+
*
141+
* @return true if the AWS error code indicates an authentication error, otherwise false.
142+
*/
143+
@Override
144+
public boolean isAuthenticationError() {
145+
return Optional.ofNullable(awsErrorDetails)
146+
.map(a -> AwsErrorCode.isAuthenticationErrorCode(a.errorCode()))
147+
.orElse(false);
148+
}
149+
137150
/**
138151
* @return {@link Builder} instance to construct a new {@link AwsServiceException}.
139152
*/

core/aws-core/src/main/java/software/amazon/awssdk/awscore/internal/AwsErrorCode.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ public final class AwsErrorCode {
3131
public static final Set<String> THROTTLING_ERROR_CODES;
3232
public static final Set<String> DEFINITE_CLOCK_SKEW_ERROR_CODES;
3333
public static final Set<String> POSSIBLE_CLOCK_SKEW_ERROR_CODES;
34+
public static final Set<String> AUTH_ERROR_CODES;
3435

3536
static {
3637
Set<String> throttlingErrorCodes = new HashSet<>(9);
@@ -66,6 +67,12 @@ public final class AwsErrorCode {
6667
retryableErrorCodes.add("RequestTimeoutException");
6768
retryableErrorCodes.add("InternalError");
6869
RETRYABLE_ERROR_CODES = unmodifiableSet(retryableErrorCodes);
70+
71+
Set<String> authErrorCodes = new HashSet<>(3);
72+
authErrorCodes.add("ExpiredToken");
73+
authErrorCodes.add("InvalidToken");
74+
authErrorCodes.add("AuthFailure");
75+
AUTH_ERROR_CODES = unmodifiableSet(authErrorCodes);
6976
}
7077

7178
private AwsErrorCode() {
@@ -86,4 +93,8 @@ public static boolean isPossibleClockSkewErrorCode(String errorCode) {
8693
public static boolean isRetryableErrorCode(String errorCode) {
8794
return RETRYABLE_ERROR_CODES.contains(errorCode);
8895
}
96+
97+
public static boolean isAuthenticationErrorCode(String errorCode) {
98+
return AUTH_ERROR_CODES.contains(errorCode);
99+
}
89100
}

0 commit comments

Comments
 (0)