Skip to content

Add credentialScope as credential metadata #4742

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,12 @@
import static software.amazon.awssdk.utils.StringUtils.trimToNull;

import java.util.Objects;
import java.util.Optional;
import software.amazon.awssdk.annotations.Immutable;
import software.amazon.awssdk.annotations.SdkInternalApi;
import software.amazon.awssdk.annotations.SdkPublicApi;
import software.amazon.awssdk.utils.ToString;
import software.amazon.awssdk.utils.Validate;
import software.amazon.awssdk.utils.builder.SdkBuilder;

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

private final String accessKeyId;
private final String secretAccessKey;
private final String credentialScope;

/**
* Constructs a new credentials object, with the specified AWS access key and AWS secret key.
*
* @param accessKeyId The AWS access key, used to identify the user interacting with AWS.
* @param secretAccessKey The AWS secret access key, used to authenticate the user interacting with AWS.
*/
protected AwsBasicCredentials(String accessKeyId, String secretAccessKey) {
this(accessKeyId, secretAccessKey, true);
private AwsBasicCredentials(DefaultBuilder builder) {
this.accessKeyId = trimToNull(builder.accessKeyId);
this.secretAccessKey = trimToNull(builder.secretAccessKey);
this.credentialScope = builder.credentialScope;
}

private AwsBasicCredentials(String accessKeyId, String secretAccessKey, boolean validateCredentials) {
this.accessKeyId = trimToNull(accessKeyId);
this.secretAccessKey = trimToNull(secretAccessKey);

if (validateCredentials) {
Validate.notNull(this.accessKeyId, "Access key ID cannot be blank.");
Validate.notNull(this.secretAccessKey, "Secret access key cannot be blank.");
}
/**
* Returns a builder for this object.
*/
public static Builder builder() {
return new DefaultBuilder();
}

/**
* Constructs a new credentials object, with the specified AWS access key and AWS secret key.
*
* @param accessKeyId The AWS access key, used to identify the user interacting with AWS.
* @param accessKeyId The AWS access key, used to identify the user interacting with AWS.
* @param secretAccessKey The AWS secret access key, used to authenticate the user interacting with AWS.
* */
*/
public static AwsBasicCredentials create(String accessKeyId, String secretAccessKey) {
return new AwsBasicCredentials(accessKeyId, secretAccessKey);
return builder().accessKeyId(accessKeyId)
.secretAccessKey(secretAccessKey)
.build();
}

/**
Expand All @@ -94,10 +91,19 @@ public String secretAccessKey() {
return secretAccessKey;
}

/**
* Retrieve the AWS region of the single-region account, if it exists. Otherwise, returns empty {@link Optional}.
*/
@Override
public Optional<String> credentialScope() {
return Optional.ofNullable(credentialScope);
}

@Override
public String toString() {
return ToString.builder("AwsCredentials")
.add("accessKeyId", accessKeyId)
.add("credentialScope", credentialScope)
.build();
}

Expand All @@ -111,14 +117,67 @@ public boolean equals(Object o) {
}
AwsBasicCredentials that = (AwsBasicCredentials) o;
return Objects.equals(accessKeyId, that.accessKeyId) &&
Objects.equals(secretAccessKey, that.secretAccessKey);
Objects.equals(secretAccessKey, that.secretAccessKey) &&
Objects.equals(credentialScope, that.credentialScope().orElse(null));
}

@Override
public int hashCode() {
int hashCode = 1;
hashCode = 31 * hashCode + Objects.hashCode(accessKeyId());
hashCode = 31 * hashCode + Objects.hashCode(secretAccessKey());
hashCode = 31 * hashCode + Objects.hashCode(credentialScope());
return hashCode;
}

/**
* A builder for creating an instance of {@link AwsBasicCredentials}. This can be created with the static
* {@link #builder()} method.
*/
public interface Builder extends SdkBuilder<AwsBasicCredentials.Builder, AwsBasicCredentials> {

/**
* The AWS access key, used to identify the user interacting with services.
*/
Builder accessKeyId(String accessKeyId);

/**
* The AWS secret access key, used to authenticate the user interacting with services.
*/
Builder secretAccessKey(String secretAccessKey);

/**
* The AWS region of the single-region account.
*/
Builder credentialScope(String credentialScope);
}

private static final class DefaultBuilder implements Builder {
private String accessKeyId;
private String secretAccessKey;
private String credentialScope;

@Override
public Builder accessKeyId(String accessKeyId) {
this.accessKeyId = accessKeyId;
return this;
}

@Override
public Builder secretAccessKey(String secretAccessKey) {
this.secretAccessKey = secretAccessKey;
return this;
}

@Override
public Builder credentialScope(String credentialScope) {
this.credentialScope = credentialScope;
return this;
}

@Override
public AwsBasicCredentials build() {
return new AwsBasicCredentials(this);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import software.amazon.awssdk.identity.spi.AwsSessionCredentialsIdentity;
import software.amazon.awssdk.utils.ToString;
import software.amazon.awssdk.utils.Validate;
import software.amazon.awssdk.utils.builder.SdkBuilder;

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

private final Instant expirationTime;

private AwsSessionCredentials(Builder builder) {
private AwsSessionCredentials(DefaultBuilder builder) {
this.accessKeyId = Validate.paramNotNull(builder.accessKeyId, "accessKey");
this.secretAccessKey = Validate.paramNotNull(builder.secretAccessKey, "secretKey");
this.sessionToken = Validate.paramNotNull(builder.sessionToken, "sessionToken");
this.expirationTime = builder.expirationTime;
this.credentialScope = builder.credentialScope;
}

/**
* Returns a builder for this object.
*/
public static Builder builder() {
return new Builder();
return new DefaultBuilder();
}

/**
Expand Down Expand Up @@ -81,6 +84,14 @@ public String secretAccessKey() {
return secretAccessKey;
}

/**
* Retrieve the AWS region of the single-region account, if it exists. Otherwise, returns empty {@link Optional}.
*/
@Override
public Optional<String> credentialScope() {
return Optional.ofNullable(credentialScope);
}

/**
* Retrieve the expiration time of these credentials, if it exists.
*/
Expand All @@ -93,6 +104,7 @@ public Optional<Instant> expirationTime() {
* Retrieve the AWS session token. This token is retrieved from an AWS token service, and is used for authenticating that this
* user has received temporary permission to access some resource.
*/
@Override
public String sessionToken() {
return sessionToken;
}
Expand All @@ -101,6 +113,7 @@ public String sessionToken() {
public String toString() {
return ToString.builder("AwsSessionCredentials")
.add("accessKeyId", accessKeyId())
.add("credentialScope", credentialScope)
.build();
}

Expand All @@ -117,7 +130,8 @@ public boolean equals(Object o) {
return Objects.equals(accessKeyId, that.accessKeyId) &&
Objects.equals(secretAccessKey, that.secretAccessKey) &&
Objects.equals(sessionToken, that.sessionToken) &&
Objects.equals(expirationTime, that.expirationTime().orElse(null));
Objects.equals(expirationTime, that.expirationTime().orElse(null)) &&
Objects.equals(credentialScope, that.credentialScope().orElse(null));
}

@Override
Expand All @@ -127,54 +141,82 @@ public int hashCode() {
hashCode = 31 * hashCode + Objects.hashCode(secretAccessKey());
hashCode = 31 * hashCode + Objects.hashCode(sessionToken());
hashCode = 31 * hashCode + Objects.hashCode(expirationTime);
hashCode = 31 * hashCode + Objects.hashCode(credentialScope);
return hashCode;
}

/**
* A builder for creating an instance of {@link AwsSessionCredentials}. This can be created with the static
* {@link #builder()} method.
*/
public static final class Builder {
public interface Builder extends SdkBuilder<AwsSessionCredentials.Builder, AwsSessionCredentials> {
/**
* The AWS access key, used to identify the user interacting with services. Required.
*/
Builder accessKeyId(String accessKeyId);

/**
* The AWS secret access key, used to authenticate the user interacting with services. Required
*/
Builder secretAccessKey(String secretAccessKey);

/**
* The AWS session token, retrieved from an AWS token service, used for authenticating that this user has
* received temporary permission to access some resource. Required
*/
Builder sessionToken(String sessionToken);

/**
* The time after which this identity will no longer be valid. If this is empty,
* an expiration time is not known (but the identity may still expire at some
* time in the future).
*/
Builder expirationTime(Instant expirationTime);

/**
* The AWS region of the single-region account. Optional
*/
Builder credentialScope(String credentialScope);
}

public static final class DefaultBuilder implements Builder {
private String accessKeyId;
private String secretAccessKey;
private String sessionToken;
private Instant expirationTime;
private String credentialScope;

/**
* The AWS access key, used to identify the user interacting with services. Required.
*/
@Override
public Builder accessKeyId(String accessKeyId) {
this.accessKeyId = accessKeyId;
return this;
}

/**
* The AWS secret access key, used to authenticate the user interacting with services. Required
*/
@Override
public Builder secretAccessKey(String secretAccessKey) {
this.secretAccessKey = secretAccessKey;
return this;
}

/**
* The AWS session token, retrieved from an AWS token service, used for authenticating that this user has
* received temporary permission to access some resource. Required
*/
@Override
public Builder sessionToken(String sessionToken) {
this.sessionToken = sessionToken;
return this;
}

/**
* The time after which this identity will no longer be valid. If this is empty,
* an expiration time is not known (but the identity may still expire at some
* time in the future).
*/
@Override
public Builder expirationTime(Instant expirationTime) {
this.expirationTime = expirationTime;
return this;
}

@Override
public Builder credentialScope(String credentialScope) {
this.credentialScope = credentialScope;
return this;
}

@Override
public AwsSessionCredentials build() {
return new AwsSessionCredentials(this);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,15 +71,21 @@ public static AwsCredentials toCredentials(AwsCredentialsIdentity awsCredentials
// identity-spi defines 2 known types - AwsCredentialsIdentity and a sub-type AwsSessionCredentialsIdentity
if (awsCredentialsIdentity instanceof AwsSessionCredentialsIdentity) {
AwsSessionCredentialsIdentity awsSessionCredentialsIdentity = (AwsSessionCredentialsIdentity) awsCredentialsIdentity;
return AwsSessionCredentials.create(awsSessionCredentialsIdentity.accessKeyId(),
awsSessionCredentialsIdentity.secretAccessKey(),
awsSessionCredentialsIdentity.sessionToken());
return AwsSessionCredentials.builder()
.accessKeyId(awsSessionCredentialsIdentity.accessKeyId())
.secretAccessKey(awsSessionCredentialsIdentity.secretAccessKey())
.sessionToken(awsSessionCredentialsIdentity.sessionToken())
.credentialScope(awsSessionCredentialsIdentity.credentialScope().orElse(null))
.build();
}
if (isAnonymous(awsCredentialsIdentity)) {
return AwsBasicCredentials.ANONYMOUS_CREDENTIALS;
}
return AwsBasicCredentials.create(awsCredentialsIdentity.accessKeyId(),
awsCredentialsIdentity.secretAccessKey());
return AwsBasicCredentials.builder()
.accessKeyId(awsCredentialsIdentity.accessKeyId())
.secretAccessKey(awsCredentialsIdentity.secretAccessKey())
.credentialScope(awsCredentialsIdentity.credentialScope().orElse(null))
.build();
}

/**
Expand All @@ -94,7 +100,7 @@ public static AwsCredentials toCredentials(AwsCredentialsIdentity awsCredentials
* @return The corresponding {@link AwsCredentialsProvider}
*/
public static AwsCredentialsProvider toCredentialsProvider(
IdentityProvider<? extends AwsCredentialsIdentity> identityProvider) {
IdentityProvider<? extends AwsCredentialsIdentity> identityProvider) {
if (identityProvider == null) {
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,17 +110,26 @@ public void toCredentialsProvider_null_returnsNull() {
@Test
public void toCredentialsProvider_AwsCredentialsProvider_returnsAsIs() {
IdentityProvider<AwsCredentialsIdentity> input =
StaticCredentialsProvider.create(AwsBasicCredentials.create("akid", "skid"));
StaticCredentialsProvider.create(AwsBasicCredentials.builder()
.accessKeyId("akid")
.secretAccessKey("skid")
.credentialScope("region")
.build());
AwsCredentialsProvider output = CredentialUtils.toCredentialsProvider(input);
assertThat(output).isSameAs(input);
}

@Test
public void toCredentialsProvider_IdentityProvider_converts() {
AwsCredentialsProvider credentialsProvider = CredentialUtils.toCredentialsProvider(
StaticCredentialsProvider.create(AwsBasicCredentials.create("akid", "skid")));
StaticCredentialsProvider.create(AwsBasicCredentials.builder()
.accessKeyId("akid")
.secretAccessKey("skid")
.credentialScope("region")
.build()));
AwsCredentials credentials = credentialsProvider.resolveCredentials();
assertThat(credentials.accessKeyId()).isEqualTo("akid");
assertThat(credentials.secretAccessKey()).isEqualTo("skid");
assertThat(credentials.credentialScope()).contains("region");
}
}
Loading