Skip to content

Commit 32af5c6

Browse files
authored
Add credentialScope as credential metadata (#4742)
* Add credentialScope as credential metadata * Refactor tests * Address comments
1 parent cb17af8 commit 32af5c6

File tree

14 files changed

+273
-73
lines changed

14 files changed

+273
-73
lines changed

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

Lines changed: 81 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,12 @@
1818
import static software.amazon.awssdk.utils.StringUtils.trimToNull;
1919

2020
import java.util.Objects;
21+
import java.util.Optional;
2122
import software.amazon.awssdk.annotations.Immutable;
2223
import software.amazon.awssdk.annotations.SdkInternalApi;
2324
import software.amazon.awssdk.annotations.SdkPublicApi;
2425
import software.amazon.awssdk.utils.ToString;
25-
import software.amazon.awssdk.utils.Validate;
26+
import software.amazon.awssdk.utils.builder.SdkBuilder;
2627

2728
/**
2829
* Provides access to the AWS credentials used for accessing services: AWS access key ID and secret access key. These
@@ -43,39 +44,35 @@ public final class AwsBasicCredentials implements AwsCredentials {
4344
* This should be accessed via {@link AnonymousCredentialsProvider#resolveCredentials()}.
4445
*/
4546
@SdkInternalApi
46-
static final AwsBasicCredentials ANONYMOUS_CREDENTIALS = new AwsBasicCredentials(null, null, false);
47+
static final AwsBasicCredentials ANONYMOUS_CREDENTIALS = builder().build();
4748

4849
private final String accessKeyId;
4950
private final String secretAccessKey;
51+
private final String credentialScope;
5052

51-
/**
52-
* Constructs a new credentials object, with the specified AWS access key and AWS secret key.
53-
*
54-
* @param accessKeyId The AWS access key, used to identify the user interacting with AWS.
55-
* @param secretAccessKey The AWS secret access key, used to authenticate the user interacting with AWS.
56-
*/
57-
protected AwsBasicCredentials(String accessKeyId, String secretAccessKey) {
58-
this(accessKeyId, secretAccessKey, true);
53+
private AwsBasicCredentials(DefaultBuilder builder) {
54+
this.accessKeyId = trimToNull(builder.accessKeyId);
55+
this.secretAccessKey = trimToNull(builder.secretAccessKey);
56+
this.credentialScope = builder.credentialScope;
5957
}
6058

61-
private AwsBasicCredentials(String accessKeyId, String secretAccessKey, boolean validateCredentials) {
62-
this.accessKeyId = trimToNull(accessKeyId);
63-
this.secretAccessKey = trimToNull(secretAccessKey);
64-
65-
if (validateCredentials) {
66-
Validate.notNull(this.accessKeyId, "Access key ID cannot be blank.");
67-
Validate.notNull(this.secretAccessKey, "Secret access key cannot be blank.");
68-
}
59+
/**
60+
* Returns a builder for this object.
61+
*/
62+
public static Builder builder() {
63+
return new DefaultBuilder();
6964
}
7065

7166
/**
7267
* Constructs a new credentials object, with the specified AWS access key and AWS secret key.
7368
*
74-
* @param accessKeyId The AWS access key, used to identify the user interacting with AWS.
69+
* @param accessKeyId The AWS access key, used to identify the user interacting with AWS.
7570
* @param secretAccessKey The AWS secret access key, used to authenticate the user interacting with AWS.
76-
* */
71+
*/
7772
public static AwsBasicCredentials create(String accessKeyId, String secretAccessKey) {
78-
return new AwsBasicCredentials(accessKeyId, secretAccessKey);
73+
return builder().accessKeyId(accessKeyId)
74+
.secretAccessKey(secretAccessKey)
75+
.build();
7976
}
8077

8178
/**
@@ -94,10 +91,19 @@ public String secretAccessKey() {
9491
return secretAccessKey;
9592
}
9693

94+
/**
95+
* Retrieve the AWS region of the single-region account, if it exists. Otherwise, returns empty {@link Optional}.
96+
*/
97+
@Override
98+
public Optional<String> credentialScope() {
99+
return Optional.ofNullable(credentialScope);
100+
}
101+
97102
@Override
98103
public String toString() {
99104
return ToString.builder("AwsCredentials")
100105
.add("accessKeyId", accessKeyId)
106+
.add("credentialScope", credentialScope)
101107
.build();
102108
}
103109

@@ -111,14 +117,67 @@ public boolean equals(Object o) {
111117
}
112118
AwsBasicCredentials that = (AwsBasicCredentials) o;
113119
return Objects.equals(accessKeyId, that.accessKeyId) &&
114-
Objects.equals(secretAccessKey, that.secretAccessKey);
120+
Objects.equals(secretAccessKey, that.secretAccessKey) &&
121+
Objects.equals(credentialScope, that.credentialScope().orElse(null));
115122
}
116123

117124
@Override
118125
public int hashCode() {
119126
int hashCode = 1;
120127
hashCode = 31 * hashCode + Objects.hashCode(accessKeyId());
121128
hashCode = 31 * hashCode + Objects.hashCode(secretAccessKey());
129+
hashCode = 31 * hashCode + Objects.hashCode(credentialScope());
122130
return hashCode;
123131
}
132+
133+
/**
134+
* A builder for creating an instance of {@link AwsBasicCredentials}. This can be created with the static
135+
* {@link #builder()} method.
136+
*/
137+
public interface Builder extends SdkBuilder<AwsBasicCredentials.Builder, AwsBasicCredentials> {
138+
139+
/**
140+
* The AWS access key, used to identify the user interacting with services.
141+
*/
142+
Builder accessKeyId(String accessKeyId);
143+
144+
/**
145+
* The AWS secret access key, used to authenticate the user interacting with services.
146+
*/
147+
Builder secretAccessKey(String secretAccessKey);
148+
149+
/**
150+
* The AWS region of the single-region account.
151+
*/
152+
Builder credentialScope(String credentialScope);
153+
}
154+
155+
private static final class DefaultBuilder implements Builder {
156+
private String accessKeyId;
157+
private String secretAccessKey;
158+
private String credentialScope;
159+
160+
@Override
161+
public Builder accessKeyId(String accessKeyId) {
162+
this.accessKeyId = accessKeyId;
163+
return this;
164+
}
165+
166+
@Override
167+
public Builder secretAccessKey(String secretAccessKey) {
168+
this.secretAccessKey = secretAccessKey;
169+
return this;
170+
}
171+
172+
@Override
173+
public Builder credentialScope(String credentialScope) {
174+
this.credentialScope = credentialScope;
175+
return this;
176+
}
177+
178+
@Override
179+
public AwsBasicCredentials build() {
180+
return new AwsBasicCredentials(this);
181+
}
182+
}
124183
}

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

Lines changed: 61 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import software.amazon.awssdk.identity.spi.AwsSessionCredentialsIdentity;
2424
import software.amazon.awssdk.utils.ToString;
2525
import software.amazon.awssdk.utils.Validate;
26+
import software.amazon.awssdk.utils.builder.SdkBuilder;
2627

2728
/**
2829
* A special type of {@link AwsCredentials} that provides a session token to be used in service authentication. Session
@@ -36,21 +37,23 @@ public final class AwsSessionCredentials implements AwsCredentials, AwsSessionCr
3637
private final String accessKeyId;
3738
private final String secretAccessKey;
3839
private final String sessionToken;
40+
private final String credentialScope;
3941

4042
private final Instant expirationTime;
4143

42-
private AwsSessionCredentials(Builder builder) {
44+
private AwsSessionCredentials(DefaultBuilder builder) {
4345
this.accessKeyId = Validate.paramNotNull(builder.accessKeyId, "accessKey");
4446
this.secretAccessKey = Validate.paramNotNull(builder.secretAccessKey, "secretKey");
4547
this.sessionToken = Validate.paramNotNull(builder.sessionToken, "sessionToken");
4648
this.expirationTime = builder.expirationTime;
49+
this.credentialScope = builder.credentialScope;
4750
}
4851

4952
/**
5053
* Returns a builder for this object.
5154
*/
5255
public static Builder builder() {
53-
return new Builder();
56+
return new DefaultBuilder();
5457
}
5558

5659
/**
@@ -81,6 +84,14 @@ public String secretAccessKey() {
8184
return secretAccessKey;
8285
}
8386

87+
/**
88+
* Retrieve the AWS region of the single-region account, if it exists. Otherwise, returns empty {@link Optional}.
89+
*/
90+
@Override
91+
public Optional<String> credentialScope() {
92+
return Optional.ofNullable(credentialScope);
93+
}
94+
8495
/**
8596
* Retrieve the expiration time of these credentials, if it exists.
8697
*/
@@ -93,6 +104,7 @@ public Optional<Instant> expirationTime() {
93104
* Retrieve the AWS session token. This token is retrieved from an AWS token service, and is used for authenticating that this
94105
* user has received temporary permission to access some resource.
95106
*/
107+
@Override
96108
public String sessionToken() {
97109
return sessionToken;
98110
}
@@ -101,6 +113,7 @@ public String sessionToken() {
101113
public String toString() {
102114
return ToString.builder("AwsSessionCredentials")
103115
.add("accessKeyId", accessKeyId())
116+
.add("credentialScope", credentialScope)
104117
.build();
105118
}
106119

@@ -117,7 +130,8 @@ public boolean equals(Object o) {
117130
return Objects.equals(accessKeyId, that.accessKeyId) &&
118131
Objects.equals(secretAccessKey, that.secretAccessKey) &&
119132
Objects.equals(sessionToken, that.sessionToken) &&
120-
Objects.equals(expirationTime, that.expirationTime().orElse(null));
133+
Objects.equals(expirationTime, that.expirationTime().orElse(null)) &&
134+
Objects.equals(credentialScope, that.credentialScope().orElse(null));
121135
}
122136

123137
@Override
@@ -127,54 +141,82 @@ public int hashCode() {
127141
hashCode = 31 * hashCode + Objects.hashCode(secretAccessKey());
128142
hashCode = 31 * hashCode + Objects.hashCode(sessionToken());
129143
hashCode = 31 * hashCode + Objects.hashCode(expirationTime);
144+
hashCode = 31 * hashCode + Objects.hashCode(credentialScope);
130145
return hashCode;
131146
}
132147

133148
/**
134149
* A builder for creating an instance of {@link AwsSessionCredentials}. This can be created with the static
135150
* {@link #builder()} method.
136151
*/
137-
public static final class Builder {
152+
public interface Builder extends SdkBuilder<AwsSessionCredentials.Builder, AwsSessionCredentials> {
153+
/**
154+
* The AWS access key, used to identify the user interacting with services. Required.
155+
*/
156+
Builder accessKeyId(String accessKeyId);
157+
158+
/**
159+
* The AWS secret access key, used to authenticate the user interacting with services. Required
160+
*/
161+
Builder secretAccessKey(String secretAccessKey);
162+
163+
/**
164+
* The AWS session token, retrieved from an AWS token service, used for authenticating that this user has
165+
* received temporary permission to access some resource. Required
166+
*/
167+
Builder sessionToken(String sessionToken);
168+
169+
/**
170+
* The time after which this identity will no longer be valid. If this is empty,
171+
* an expiration time is not known (but the identity may still expire at some
172+
* time in the future).
173+
*/
174+
Builder expirationTime(Instant expirationTime);
175+
176+
/**
177+
* The AWS region of the single-region account. Optional
178+
*/
179+
Builder credentialScope(String credentialScope);
180+
}
181+
182+
public static final class DefaultBuilder implements Builder {
138183
private String accessKeyId;
139184
private String secretAccessKey;
140185
private String sessionToken;
141186
private Instant expirationTime;
187+
private String credentialScope;
142188

143-
/**
144-
* The AWS access key, used to identify the user interacting with services. Required.
145-
*/
189+
@Override
146190
public Builder accessKeyId(String accessKeyId) {
147191
this.accessKeyId = accessKeyId;
148192
return this;
149193
}
150194

151-
/**
152-
* The AWS secret access key, used to authenticate the user interacting with services. Required
153-
*/
195+
@Override
154196
public Builder secretAccessKey(String secretAccessKey) {
155197
this.secretAccessKey = secretAccessKey;
156198
return this;
157199
}
158200

159-
/**
160-
* The AWS session token, retrieved from an AWS token service, used for authenticating that this user has
161-
* received temporary permission to access some resource. Required
162-
*/
201+
@Override
163202
public Builder sessionToken(String sessionToken) {
164203
this.sessionToken = sessionToken;
165204
return this;
166205
}
167206

168-
/**
169-
* The time after which this identity will no longer be valid. If this is empty,
170-
* an expiration time is not known (but the identity may still expire at some
171-
* time in the future).
172-
*/
207+
@Override
173208
public Builder expirationTime(Instant expirationTime) {
174209
this.expirationTime = expirationTime;
175210
return this;
176211
}
177212

213+
@Override
214+
public Builder credentialScope(String credentialScope) {
215+
this.credentialScope = credentialScope;
216+
return this;
217+
}
218+
219+
@Override
178220
public AwsSessionCredentials build() {
179221
return new AwsSessionCredentials(this);
180222
}

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

Lines changed: 12 additions & 6 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+
.credentialScope(awsSessionCredentialsIdentity.credentialScope().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+
.credentialScope(awsCredentialsIdentity.credentialScope().orElse(null))
88+
.build();
8389
}
8490

8591
/**
@@ -94,7 +100,7 @@ public static AwsCredentials toCredentials(AwsCredentialsIdentity awsCredentials
94100
* @return The corresponding {@link AwsCredentialsProvider}
95101
*/
96102
public static AwsCredentialsProvider toCredentialsProvider(
97-
IdentityProvider<? extends AwsCredentialsIdentity> identityProvider) {
103+
IdentityProvider<? extends AwsCredentialsIdentity> identityProvider) {
98104
if (identityProvider == null) {
99105
return null;
100106
}

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

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,17 +110,26 @@ public void toCredentialsProvider_null_returnsNull() {
110110
@Test
111111
public void toCredentialsProvider_AwsCredentialsProvider_returnsAsIs() {
112112
IdentityProvider<AwsCredentialsIdentity> input =
113-
StaticCredentialsProvider.create(AwsBasicCredentials.create("akid", "skid"));
113+
StaticCredentialsProvider.create(AwsBasicCredentials.builder()
114+
.accessKeyId("akid")
115+
.secretAccessKey("skid")
116+
.credentialScope("region")
117+
.build());
114118
AwsCredentialsProvider output = CredentialUtils.toCredentialsProvider(input);
115119
assertThat(output).isSameAs(input);
116120
}
117121

118122
@Test
119123
public void toCredentialsProvider_IdentityProvider_converts() {
120124
AwsCredentialsProvider credentialsProvider = CredentialUtils.toCredentialsProvider(
121-
StaticCredentialsProvider.create(AwsBasicCredentials.create("akid", "skid")));
125+
StaticCredentialsProvider.create(AwsBasicCredentials.builder()
126+
.accessKeyId("akid")
127+
.secretAccessKey("skid")
128+
.credentialScope("region")
129+
.build()));
122130
AwsCredentials credentials = credentialsProvider.resolveCredentials();
123131
assertThat(credentials.accessKeyId()).isEqualTo("akid");
124132
assertThat(credentials.secretAccessKey()).isEqualTo("skid");
133+
assertThat(credentials.credentialScope()).contains("region");
125134
}
126135
}

0 commit comments

Comments
 (0)