Skip to content

Commit b5512db

Browse files
committed
Adds accountId to STS credentials providers (#4040)
Adds accountId to STS credentials providers
1 parent 656fd34 commit b5512db

File tree

23 files changed

+383
-38
lines changed

23 files changed

+383
-38
lines changed

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

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ public final class AwsBasicCredentials implements AwsCredentials,
4444
ToCopyableBuilder<AwsBasicCredentials.Builder, AwsBasicCredentials> {
4545
/**
4646
* A set of AWS credentials without an access key or secret access key, indicating that anonymous access should be used.
47+
* <p>
48+
* This should be accessed via {@link AnonymousCredentialsProvider#resolveCredentials()}.
4749
*/
4850
// TODO(sra-identity-and-auth): Check if this static member can be removed after cleanup
4951
@SdkInternalApi
@@ -53,12 +55,14 @@ public final class AwsBasicCredentials implements AwsCredentials,
5355
private final String secretAccessKey;
5456
private final boolean validateCredentials;
5557
private final String providerName;
58+
private final String accountId;
5659

5760
private AwsBasicCredentials(Builder builder) {
5861
this.accessKeyId = trimToNull(builder.accessKeyId);
5962
this.secretAccessKey = trimToNull(builder.secretAccessKey);
6063
this.validateCredentials = builder.validateCredentials;
6164
this.providerName = builder.providerName;
65+
this.accountId = builder.accountId;
6266

6367
if (builder.validateCredentials) {
6468
Validate.notNull(this.accessKeyId, "Access key ID cannot be blank.");
@@ -77,6 +81,7 @@ protected AwsBasicCredentials(String accessKeyId, String secretAccessKey) {
7781
this.secretAccessKey = trimToNull(secretAccessKey);
7882
this.validateCredentials = false;
7983
this.providerName = null;
84+
this.accountId = null;
8085
}
8186

8287
public static Builder builder() {
@@ -88,7 +93,7 @@ public static Builder builder() {
8893
*
8994
* @param accessKeyId The AWS access key, used to identify the user interacting with AWS.
9095
* @param secretAccessKey The AWS secret access key, used to authenticate the user interacting with AWS.
91-
* */
96+
*/
9297
public static AwsBasicCredentials create(String accessKeyId, String secretAccessKey) {
9398
return builder().accessKeyId(accessKeyId)
9499
.secretAccessKey(secretAccessKey)
@@ -119,11 +124,20 @@ public Optional<String> providerName() {
119124
return Optional.ofNullable(providerName);
120125
}
121126

127+
/**
128+
* Retrieve the AWS account id associated with this credentials identity, if found.
129+
*/
130+
@Override
131+
public Optional<String> accountId() {
132+
return Optional.ofNullable(accountId);
133+
}
134+
122135
@Override
123136
public String toString() {
124137
return ToString.builder("AwsCredentials")
125138
.add("accessKeyId", accessKeyId)
126139
.add("providerName", providerName)
140+
.add("accountId", accountId)
127141
.build();
128142
}
129143

@@ -137,21 +151,24 @@ public boolean equals(Object o) {
137151
}
138152
AwsBasicCredentials that = (AwsBasicCredentials) o;
139153
return Objects.equals(accessKeyId, that.accessKeyId) &&
140-
Objects.equals(secretAccessKey, that.secretAccessKey);
154+
Objects.equals(secretAccessKey, that.secretAccessKey) &&
155+
Objects.equals(accountId, that.accountId().orElse(null));
141156
}
142157

143158
@Override
144159
public int hashCode() {
145160
int hashCode = 1;
146161
hashCode = 31 * hashCode + Objects.hashCode(accessKeyId());
147162
hashCode = 31 * hashCode + Objects.hashCode(secretAccessKey());
163+
hashCode = 31 * hashCode + Objects.hashCode(accountId);
148164
return hashCode;
149165
}
150166

151167
@Override
152168
public Builder toBuilder() {
153169
return builder().accessKeyId(accessKeyId)
154170
.secretAccessKey(secretAccessKey)
171+
.accountId(accountId)
155172
.validateCredentials(validateCredentials)
156173
.providerName(providerName);
157174
}
@@ -169,6 +186,7 @@ public static final class Builder implements CopyableBuilder<AwsBasicCredentials
169186
private String accessKeyId;
170187
private String secretAccessKey;
171188
private String providerName;
189+
private String accountId;
172190
private boolean validateCredentials = true;
173191

174192
private Builder() {
@@ -190,6 +208,14 @@ public Builder secretAccessKey(String secretAccessKey) {
190208
return this;
191209
}
192210

211+
/**
212+
* The AWS account id associated with this credentials identity.
213+
*/
214+
public Builder accountId(String accountId) {
215+
this.accountId = accountId;
216+
return this;
217+
}
218+
193219
/**
194220
* The name of the identity provider that created this credential identity.
195221
*/

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,15 @@ public final class AwsSessionCredentials implements AwsCredentials, AwsSessionCr
4040
private final String secretAccessKey;
4141
private final String sessionToken;
4242

43+
private final String accountId;
4344
private final Instant expirationTime;
4445
private final String providerName;
4546

4647
private AwsSessionCredentials(Builder builder) {
4748
this.accessKeyId = Validate.paramNotNull(builder.accessKeyId, "accessKey");
4849
this.secretAccessKey = Validate.paramNotNull(builder.secretAccessKey, "secretKey");
4950
this.sessionToken = Validate.paramNotNull(builder.sessionToken, "sessionToken");
51+
this.accountId = builder.accountId;
5052
this.expirationTime = builder.expirationTime;
5153
this.providerName = builder.providerName;
5254
}
@@ -111,11 +113,17 @@ public Optional<String> providerName() {
111113
return Optional.ofNullable(providerName);
112114
}
113115

116+
@Override
117+
public Optional<String> accountId() {
118+
return Optional.ofNullable(accountId);
119+
}
120+
114121
@Override
115122
public String toString() {
116123
return ToString.builder("AwsSessionCredentials")
117124
.add("accessKeyId", accessKeyId())
118125
.add("providerName", providerName)
126+
.add("accountId", accountId)
119127
.build();
120128
}
121129

@@ -132,6 +140,7 @@ public boolean equals(Object o) {
132140
return Objects.equals(accessKeyId, that.accessKeyId) &&
133141
Objects.equals(secretAccessKey, that.secretAccessKey) &&
134142
Objects.equals(sessionToken, that.sessionToken) &&
143+
Objects.equals(accountId, that.accountId().orElse(null)) &&
135144
Objects.equals(expirationTime, that.expirationTime().orElse(null));
136145
}
137146

@@ -141,6 +150,7 @@ public int hashCode() {
141150
hashCode = 31 * hashCode + Objects.hashCode(accessKeyId());
142151
hashCode = 31 * hashCode + Objects.hashCode(secretAccessKey());
143152
hashCode = 31 * hashCode + Objects.hashCode(sessionToken());
153+
hashCode = 31 * hashCode + Objects.hashCode(accountId);
144154
hashCode = 31 * hashCode + Objects.hashCode(expirationTime);
145155
return hashCode;
146156
}
@@ -150,6 +160,7 @@ public Builder toBuilder() {
150160
return builder().accessKeyId(accessKeyId)
151161
.secretAccessKey(secretAccessKey)
152162
.sessionToken(sessionToken)
163+
.accountId(accountId)
153164
.expirationTime(expirationTime)
154165
.providerName(providerName);
155166
}
@@ -167,6 +178,7 @@ public static final class Builder implements CopyableBuilder<AwsSessionCredentia
167178
private String accessKeyId;
168179
private String secretAccessKey;
169180
private String sessionToken;
181+
private String accountId;
170182
private Instant expirationTime;
171183
private String providerName;
172184

@@ -195,6 +207,16 @@ public Builder sessionToken(String sessionToken) {
195207
return this;
196208
}
197209

210+
/**
211+
* The AWS accountId
212+
* @param accountId
213+
* @return
214+
*/
215+
public Builder accountId(String accountId) {
216+
this.accountId = accountId;
217+
return this;
218+
}
219+
198220
/**
199221
* The time after which this identity will no longer be valid. If this is empty,
200222
* an expiration time is not known (but the identity may still expire at some

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

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,15 +71,21 @@ public static AwsCredentials toCredentials(AwsCredentialsIdentity awsCredentials
7171
// identity-spi defines 2 known types - AwsCredentialsIdentity and a sub-type AwsSessionCredentialsIdentity
7272
if (awsCredentialsIdentity instanceof AwsSessionCredentialsIdentity) {
7373
AwsSessionCredentialsIdentity awsSessionCredentialsIdentity = (AwsSessionCredentialsIdentity) awsCredentialsIdentity;
74-
return AwsSessionCredentials.create(awsSessionCredentialsIdentity.accessKeyId(),
75-
awsSessionCredentialsIdentity.secretAccessKey(),
76-
awsSessionCredentialsIdentity.sessionToken());
74+
return AwsSessionCredentials.builder()
75+
.accessKeyId(awsSessionCredentialsIdentity.accessKeyId())
76+
.secretAccessKey(awsSessionCredentialsIdentity.secretAccessKey())
77+
.sessionToken(awsSessionCredentialsIdentity.sessionToken())
78+
.accountId(awsSessionCredentialsIdentity.accountId().orElse(null))
79+
.build();
7780
}
7881
if (isAnonymous(awsCredentialsIdentity)) {
7982
return AwsBasicCredentials.ANONYMOUS_CREDENTIALS;
8083
}
81-
return AwsBasicCredentials.create(awsCredentialsIdentity.accessKeyId(),
82-
awsCredentialsIdentity.secretAccessKey());
84+
return AwsBasicCredentials.builder()
85+
.accessKeyId(awsCredentialsIdentity.accessKeyId())
86+
.secretAccessKey(awsCredentialsIdentity.secretAccessKey())
87+
.accountId(awsCredentialsIdentity.accountId().orElse(null))
88+
.build();
8389
}
8490

8591
/**

core/auth/src/test/java/software/amazon/awssdk/auth/credentials/internal/AwsSessionCredentialsTest.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616
package software.amazon.awssdk.auth.credentials.internal;
1717

1818
import static org.junit.jupiter.api.Assertions.assertEquals;
19+
import static org.junit.jupiter.api.Assertions.assertFalse;
1920
import static org.junit.jupiter.api.Assertions.assertThrows;
21+
import static org.junit.jupiter.api.Assertions.assertTrue;
2022

2123
import nl.jqno.equalsverifier.EqualsVerifier;
2224
import org.junit.jupiter.api.Test;
@@ -27,6 +29,7 @@ class AwsSessionCredentialsTest {
2729
private static final String ACCESS_KEY_ID = "accessKeyId";
2830
private static final String SECRET_ACCESS_KEY = "secretAccessKey";
2931
private static final String SESSION_TOKEN = "sessionToken";
32+
private static final String ACCOUNT_ID = "accountId";
3033
private static final String PROVIDER_NAME = "StaticCredentialsProvider";
3134

3235
@Test
@@ -65,6 +68,7 @@ void create_isSuccessful() {
6568
assertEquals(ACCESS_KEY_ID, identity.accessKeyId());
6669
assertEquals(SECRET_ACCESS_KEY, identity.secretAccessKey());
6770
assertEquals(SESSION_TOKEN, identity.sessionToken());
71+
assertFalse(identity.accountId().isPresent());
6872
}
6973

7074
@Test
@@ -73,10 +77,13 @@ void build_isSuccessful() {
7377
.accessKeyId(ACCESS_KEY_ID)
7478
.secretAccessKey(SECRET_ACCESS_KEY)
7579
.sessionToken(SESSION_TOKEN)
80+
.accountId(ACCOUNT_ID)
7681
.build();
7782
assertEquals(ACCESS_KEY_ID, identity.accessKeyId());
7883
assertEquals(SECRET_ACCESS_KEY, identity.secretAccessKey());
7984
assertEquals(SESSION_TOKEN, identity.sessionToken());
85+
assertTrue(identity.accountId().isPresent());
86+
assertEquals(ACCOUNT_ID, identity.accountId().get());
8087
}
8188

8289
@Test
@@ -85,11 +92,14 @@ void copy_isSuccessful() {
8592
.accessKeyId(ACCESS_KEY_ID)
8693
.secretAccessKey(SECRET_ACCESS_KEY)
8794
.sessionToken(SESSION_TOKEN)
95+
.accountId(ACCOUNT_ID)
8896
.build();
8997
AwsSessionCredentials copy = identity.copy(c -> c.providerName(PROVIDER_NAME));
9098
assertEquals(ACCESS_KEY_ID, copy.accessKeyId());
9199
assertEquals(SECRET_ACCESS_KEY, copy.secretAccessKey());
92100
assertEquals(SESSION_TOKEN, copy.sessionToken());
101+
assertTrue(identity.accountId().isPresent());
102+
assertEquals(ACCOUNT_ID, identity.accountId().get());
93103
assertEquals(PROVIDER_NAME, copy.providerName().get());
94104
}
95105
}

core/identity-spi/src/main/java/software/amazon/awssdk/identity/spi/internal/DefaultAwsSessionCredentialsIdentity.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ public boolean equals(Object o) {
9494
return Objects.equals(accessKeyId, that.accessKeyId()) &&
9595
Objects.equals(secretAccessKey, that.secretAccessKey()) &&
9696
Objects.equals(sessionToken, that.sessionToken()) &&
97-
Objects.equals(accountId, that.accountId().orElse(null));
97+
Objects.equals(accountId, that.accountId().orElse(null));
9898
}
9999

100100
@Override

services/sts/src/it/java/software/amazon/awssdk/services/sts/AssumeRoleIntegrationTest.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ public void profileCredentialsProviderCanAssumeRoles() throws InterruptedExcepti
178178

179179
assertThat(awsCredentials.accessKeyId()).isNotBlank();
180180
assertThat(awsCredentials.secretAccessKey()).isNotBlank();
181+
assertThat(awsCredentials.accountId()).isPresent();
181182
((SdkAutoCloseable) awsCredentialsProvider).close();
182183
}
183184

@@ -210,6 +211,7 @@ public void profileCredentialProviderCanAssumeRolesWithEnvironmentCredentialSour
210211

211212
assertThat(awsCredentials.accessKeyId()).isNotBlank();
212213
assertThat(awsCredentials.secretAccessKey()).isNotBlank();
214+
assertThat(awsCredentials.accountId()).isPresent();
213215
((SdkAutoCloseable) awsCredentialsProvider).close();
214216
});
215217
}
@@ -247,6 +249,7 @@ public void profileCredentialProviderWithEnvironmentCredentialSourceAndSystemPro
247249

248250
assertThat(awsCredentials.accessKeyId()).isNotBlank();
249251
assertThat(awsCredentials.secretAccessKey()).isNotBlank();
252+
assertThat(awsCredentials.accountId()).isPresent();
250253
((SdkAutoCloseable) awsCredentialsProvider).close();
251254
});
252255
} finally {
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
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.sts.auth;
17+
18+
import java.time.Instant;
19+
import software.amazon.awssdk.annotations.SdkInternalApi;
20+
import software.amazon.awssdk.annotations.ThreadSafe;
21+
import software.amazon.awssdk.auth.credentials.AwsSessionCredentials;
22+
import software.amazon.awssdk.services.sts.model.Credentials;
23+
24+
/**
25+
* Holder class used to atomically store a session with its expiration time.
26+
*/
27+
@SdkInternalApi
28+
@ThreadSafe
29+
final class SessionCredentialsHolder {
30+
private final AwsSessionCredentials sessionCredentials;
31+
private final Instant sessionCredentialsExpiration;
32+
33+
SessionCredentialsHolder(Builder b) {
34+
Credentials credentials = b.credentials;
35+
this.sessionCredentials = AwsSessionCredentials.builder()
36+
.accessKeyId(credentials.accessKeyId())
37+
.secretAccessKey(credentials.secretAccessKey())
38+
.sessionToken(credentials.sessionToken())
39+
.accountId(b.accountId)
40+
.build();
41+
this.sessionCredentialsExpiration = credentials.expiration();
42+
}
43+
44+
public static Builder builder() {
45+
return new Builder();
46+
}
47+
48+
public AwsSessionCredentials getSessionCredentials() {
49+
return sessionCredentials;
50+
}
51+
52+
public Instant getSessionCredentialsExpiration() {
53+
return sessionCredentialsExpiration;
54+
}
55+
56+
public static class Builder {
57+
private Credentials credentials;
58+
private String accountId;
59+
60+
public Builder credentials(Credentials credentials) {
61+
this.credentials = credentials;
62+
return this;
63+
}
64+
65+
public Builder accountId(String accountId) {
66+
this.accountId = accountId;
67+
return this;
68+
}
69+
70+
public SessionCredentialsHolder build() {
71+
return new SessionCredentialsHolder(this);
72+
}
73+
}
74+
}

0 commit comments

Comments
 (0)