Skip to content

Adds accountId to STS credentials providers #4040

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
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,15 +18,16 @@
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;

/**
* Provides access to the AWS credentials used for accessing services: AWS access key ID and secret access key. These
* credentials are used to securely sign requests to services (e.g., AWS services) that use them for authentication.
* Provides access to the AWS credentials used for accessing services: AWS access key ID and secret access key. These credentials
* are used to securely sign requests to services (e.g., AWS services) that use them for authentication.
*
* <p>For more details on AWS access keys, see:
* <a href="https://docs.aws.amazon.com/general/latest/gr/aws-sec-cred-types.html#access-keys-and-secret-access-keys">
Expand All @@ -39,14 +40,26 @@
public final class AwsBasicCredentials implements AwsCredentials {
/**
* A set of AWS credentials without an access key or secret access key, indicating that anonymous access should be used.
*
* <p>
* 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().validateCredentials(false).build();

private final String accessKeyId;
private final String secretAccessKey;
private final String accountId;

private AwsBasicCredentials(Builder builder) {
this.accessKeyId = trimToNull(builder.accessKeyId);
this.secretAccessKey = trimToNull(builder.secretAccessKey);

if (builder.validateCredentials) {
Validate.notNull(this.accessKeyId, "Access key ID cannot be blank.");
Validate.notNull(this.secretAccessKey, "Secret access key cannot be blank.");
}
this.accountId = builder.accountId;
}

/**
* Constructs a new credentials object, with the specified AWS access key and AWS secret key.
Expand All @@ -55,27 +68,25 @@ public final class AwsBasicCredentials implements AwsCredentials {
* @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);
this(builder().accessKeyId(accessKeyId)
.secretAccessKey(secretAccessKey)
.validateCredentials(true));
}

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.");
}
public static Builder builder() {
return new Builder();
}

/**
* 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 +105,19 @@ public String secretAccessKey() {
return secretAccessKey;
}

/**
* Retrieve the AWS account id associated with this credentials identity, if found.
*/
@Override
public Optional<String> accountId() {
return Optional.ofNullable(accountId);
}

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

Expand All @@ -111,14 +131,59 @@ 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(accountId, that.accountId().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(accountId);
return hashCode;
}

public static final class Builder {
private String accessKeyId;
private String secretAccessKey;
private String accountId;
private boolean validateCredentials = true;

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

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

/**
* The AWS account id associated with this credentials identity.
*/
public Builder accountId(String accountId) {
this.accountId = accountId;
return this;
}

/**
* Whether this class should verify that accessKeyId and secretAccessKey are not null.
*/
Builder validateCredentials(Boolean validateCredentials) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I think this can be boolean too.

this.validateCredentials = validateCredentials;
return this;
}

public AwsBasicCredentials build() {
return new AwsBasicCredentials(this);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@

package software.amazon.awssdk.auth.credentials;

import java.time.Instant;
import java.util.Objects;
import java.util.Optional;
import software.amazon.awssdk.annotations.Immutable;
import software.amazon.awssdk.annotations.SdkPublicApi;
import software.amazon.awssdk.identity.spi.AwsSessionCredentialsIdentity;
Expand All @@ -34,11 +36,19 @@ public final class AwsSessionCredentials implements AwsCredentials, AwsSessionCr
private final String accessKeyId;
private final String secretAccessKey;
private final String sessionToken;
private final String accountId;
private final Instant expiration;

private AwsSessionCredentials(String accessKey, String secretKey, String sessionToken) {
this.accessKeyId = Validate.paramNotNull(accessKey, "accessKey");
this.secretAccessKey = Validate.paramNotNull(secretKey, "secretKey");
this.sessionToken = Validate.paramNotNull(sessionToken, "sessionToken");
private AwsSessionCredentials(Builder builder) {
this.accessKeyId = Validate.paramNotNull(builder.accessKeyId, "accessKey");
this.secretAccessKey = Validate.paramNotNull(builder.secretAccessKey, "secretKey");
this.sessionToken = Validate.paramNotNull(builder.sessionToken, "sessionToken");
this.accountId = builder.accountId;
this.expiration = builder.expiration;
}

public static Builder builder() {
return new Builder();
}

/**
Expand All @@ -50,7 +60,7 @@ private AwsSessionCredentials(String accessKey, String secretKey, String session
* received temporary permission to access some resource.
*/
public static AwsSessionCredentials create(String accessKey, String secretKey, String sessionToken) {
return new AwsSessionCredentials(accessKey, secretKey, sessionToken);
return builder().accessKeyId(accessKey).secretAccessKey(secretKey).sessionToken(sessionToken).build();
}

@Override
Expand All @@ -68,10 +78,20 @@ public String sessionToken() {
return sessionToken;
}

@Override
public Optional<String> accountId() {
return Optional.ofNullable(accountId);
}

public Optional<Instant> expiration() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this makes sense to add to this interface, why not add it to AwsSessionCredentialsIdentity and @Override here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, the top Identity interface already defines expirationTime(), which is what we should use.

return Optional.ofNullable(expiration);
}

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

Expand All @@ -87,7 +107,9 @@ public boolean equals(Object o) {
AwsSessionCredentials that = (AwsSessionCredentials) o;
return Objects.equals(accessKeyId, that.accessKeyId) &&
Objects.equals(secretAccessKey, that.secretAccessKey) &&
Objects.equals(sessionToken, that.sessionToken);
Objects.equals(sessionToken, that.sessionToken) &&
Objects.equals(accountId, that.accountId().orElse(null)) &&
Objects.equals(expiration, that.expiration().orElse(null));
}

@Override
Expand All @@ -96,6 +118,45 @@ public int hashCode() {
hashCode = 31 * hashCode + Objects.hashCode(accessKeyId());
hashCode = 31 * hashCode + Objects.hashCode(secretAccessKey());
hashCode = 31 * hashCode + Objects.hashCode(sessionToken());
hashCode = 31 * hashCode + Objects.hashCode(accountId);
hashCode = 31 * hashCode + Objects.hashCode(expiration);
return hashCode;
}

public static final class Builder {
private String accessKeyId;
private String secretAccessKey;
private String sessionToken;
private String accountId;
private Instant expiration;

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

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

public Builder sessionToken(String sessionToken) {
this.sessionToken = sessionToken;
return this;
}

public Builder accountId(String accountId) {
this.accountId = accountId;
return this;
}

public Builder expiration(Instant expiration) {
this.expiration = expiration;
return this;
}

public AwsSessionCredentials build() {
return new AwsSessionCredentials(this);
}
}
}
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())
.accountId(awsSessionCredentialsIdentity.accountId().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())
.accountId(awsCredentialsIdentity.accountId().orElse(null))
.build();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
public class StaticCredentialsProviderTest {
@Test
public void getAwsCredentials_ReturnsSameCredentials() throws Exception {
final AwsCredentials credentials = new AwsBasicCredentials("akid", "skid");
final AwsCredentials credentials = AwsBasicCredentials.create("akid", "skid");
final AwsCredentials actualCredentials =
StaticCredentialsProvider.create(credentials).resolveCredentials();
assertEquals(credentials, actualCredentials);
Expand Down
Loading