diff --git a/spring-vault-core/src/main/java/org/springframework/vault/authentication/AwsIamAuthentication.java b/spring-vault-core/src/main/java/org/springframework/vault/authentication/AwsIamAuthentication.java
index a0285eda..41310f91 100644
--- a/spring-vault-core/src/main/java/org/springframework/vault/authentication/AwsIamAuthentication.java
+++ b/spring-vault-core/src/main/java/org/springframework/vault/authentication/AwsIamAuthentication.java
@@ -19,27 +19,26 @@ import java.io.ByteArrayInputStream;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
+import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
-import java.util.List;
-
-import software.amazon.awssdk.http.SdkHttpFullRequest;
-import software.amazon.awssdk.auth.signer.Aws4Signer;
-import software.amazon.awssdk.auth.signer.params.Aws4SignerParams;
-import software.amazon.awssdk.auth.credentials.AwsCredentials;
-import software.amazon.awssdk.http.SdkHttpMethod;
-import software.amazon.awssdk.regions.Region;
-import software.amazon.awssdk.regions.providers.DefaultAwsRegionProviderChain;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import software.amazon.awssdk.auth.credentials.AwsCredentials;
+import software.amazon.awssdk.auth.signer.Aws4Signer;
+import software.amazon.awssdk.auth.signer.params.Aws4SignerParams;
+import software.amazon.awssdk.http.SdkHttpFullRequest;
+import software.amazon.awssdk.http.SdkHttpMethod;
+import software.amazon.awssdk.regions.Region;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.util.Assert;
import org.springframework.util.Base64Utils;
+import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.vault.VaultException;
import org.springframework.vault.support.VaultResponse;
@@ -53,17 +52,19 @@ import org.springframework.web.client.RestOperations;
* AWS IAM authentication creates a {@link Aws4Signer signed} HTTP request that is
* executed by Vault to get the identity of the signer using AWS STS
* {@literal GetCallerIdentity}. A signature requires
- * {@link com.amazonaws.auth.AwsCredentials} to calculate the signature.
+ * {@link software.amazon.awssdk.auth.credentials.AwsCredentials} to calculate the
+ * signature.
*
* This authentication requires AWS' Java SDK to sign request parameters and calculate the
- * signature key. Using an appropriate {@link com.amazonaws.auth.AwsCredentialsProvider}
- * allows authentication within AWS-EC2 instances with an assigned profile, within ECS and
- * Lambda instances.
+ * signature key. Using an appropriate
+ * {@link software.amazon.awssdk.auth.credentials.AwsCredentialsProvider} allows
+ * authentication within AWS-EC2 instances with an assigned profile, within ECS and Lambda
+ * instances.
*
* @author Mark Paluch
* @since 1.1
* @see AwsIamAuthenticationOptions
- * @see com.amazonaws.auth.AwsCredentialsProvider
+ * @see software.amazon.awssdk.auth.credentials.AwsCredentialsProvider
* @see RestOperations
* @see Auth Backend: aws
* (IAM)
@@ -115,14 +116,15 @@ public class AwsIamAuthentication implements ClientAuthentication, Authenticatio
Assert.notNull(options, "AwsIamAuthenticationOptions must not be null");
AwsCredentials credentials = options.getCredentialsProvider().resolveCredentials();
+ Region region = options.getRegionProvider().getRegion();
- return createAuthenticationSteps(options, credentials);
+ return createAuthenticationSteps(options, credentials, region);
}
protected static AuthenticationSteps createAuthenticationSteps(AwsIamAuthenticationOptions options,
- AwsCredentials credentials) {
+ AwsCredentials credentials, Region region) {
- return AuthenticationSteps.fromSupplier(() -> createRequestBody(options, credentials)) //
+ return AuthenticationSteps.fromSupplier(() -> createRequestBody(options, credentials, region)) //
.login(AuthenticationUtil.getLoginPath(options.getPath()));
}
@@ -133,7 +135,8 @@ public class AwsIamAuthentication implements ClientAuthentication, Authenticatio
@Override
public AuthenticationSteps getAuthenticationSteps() {
- return createAuthenticationSteps(this.options, this.options.getCredentialsProvider().resolveCredentials());
+ return createAuthenticationSteps(this.options, this.options.getCredentialsProvider().resolveCredentials(),
+ this.options.getRegionProvider().getRegion());
}
@SuppressWarnings("unchecked")
@@ -174,17 +177,20 @@ public class AwsIamAuthentication implements ClientAuthentication, Authenticatio
* @return the map containing body key-value pairs.
*/
protected static Map createRequestBody(AwsIamAuthenticationOptions options) {
- return createRequestBody(options, options.getCredentialsProvider().resolveCredentials());
+ return createRequestBody(options, options.getCredentialsProvider().resolveCredentials(),
+ options.getRegionProvider().getRegion());
}
/**
* Create the request body to perform a Vault login using the AWS-IAM authentication
* method.
* @param options must not be {@literal null}.
+ * @param credentials must not be {@literal null}.
+ * @param region must not be {@literal null}.
* @return the map containing body key-value pairs.
*/
private static Map createRequestBody(AwsIamAuthenticationOptions options,
- AwsCredentials credentials) {
+ AwsCredentials credentials, Region region) {
Map login = new HashMap<>();
@@ -192,17 +198,18 @@ public class AwsIamAuthentication implements ClientAuthentication, Authenticatio
login.put("iam_request_url", Base64Utils.encodeToString(options.getEndpointUri().toString().getBytes()));
login.put("iam_request_body", REQUEST_BODY_BASE64_ENCODED);
- String headerJson = getSignedHeaders(options, credentials);
+ String headerJson = getSignedHeaders(options, credentials, region);
login.put("iam_request_headers", Base64Utils.encodeToString(headerJson.getBytes()));
- if (!StringUtils.isEmpty(options.getRole())) {
+ if (!ObjectUtils.isEmpty(options.getRole())) {
login.put("role", options.getRole());
}
return login;
}
- private static String getSignedHeaders(AwsIamAuthenticationOptions options, AwsCredentials credentials) {
+ private static String getSignedHeaders(AwsIamAuthenticationOptions options, AwsCredentials credentials,
+ Region region) {
Map> headers = createIamRequestHeaders(options);
@@ -211,7 +218,6 @@ public class AwsIamAuthentication implements ClientAuthentication, Authenticatio
.method(SdkHttpMethod.POST).uri(options.getEndpointUri());
SdkHttpFullRequest request = builder.build();
- Region region = DefaultAwsRegionProviderChain.builder().build().getRegion();
Aws4Signer signer = Aws4Signer.create();
Aws4SignerParams signerParams = Aws4SignerParams.builder().awsCredentials(credentials).signingName("sts")
.signingRegion(region).build();
diff --git a/spring-vault-core/src/main/java/org/springframework/vault/authentication/AwsIamAuthenticationOptions.java b/spring-vault-core/src/main/java/org/springframework/vault/authentication/AwsIamAuthenticationOptions.java
index 7da8d7cd..426b195f 100644
--- a/spring-vault-core/src/main/java/org/springframework/vault/authentication/AwsIamAuthenticationOptions.java
+++ b/spring-vault-core/src/main/java/org/springframework/vault/authentication/AwsIamAuthenticationOptions.java
@@ -20,6 +20,8 @@ import java.net.URI;
import software.amazon.awssdk.auth.credentials.AwsCredentials;
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
+import software.amazon.awssdk.regions.providers.AwsRegionProvider;
+import software.amazon.awssdk.regions.providers.DefaultAwsRegionProviderChain;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
@@ -51,6 +53,11 @@ public class AwsIamAuthenticationOptions {
*/
private final AwsCredentialsProvider credentialsProvider;
+ /**
+ * Region provider.
+ */
+ private final AwsRegionProvider regionProvider;
+
/**
* Name of the role against which the login is being attempted. If role is not
* specified, the friendly name (i.e., role name or username) of the IAM principal
@@ -71,11 +78,12 @@ public class AwsIamAuthenticationOptions {
*/
private final URI endpointUri;
- private AwsIamAuthenticationOptions(String path, AwsCredentialsProvider credentialsProvider, @Nullable String role,
- @Nullable String serverId, URI endpointUri) {
+ private AwsIamAuthenticationOptions(String path, AwsCredentialsProvider credentialsProvider,
+ AwsRegionProvider regionProvider, @Nullable String role, @Nullable String serverId, URI endpointUri) {
this.path = path;
this.credentialsProvider = credentialsProvider;
+ this.regionProvider = regionProvider;
this.role = role;
this.serverId = serverId;
this.endpointUri = endpointUri;
@@ -102,6 +110,15 @@ public class AwsIamAuthenticationOptions {
return this.credentialsProvider;
}
+ /**
+ * @return the region provider to obtain the AWS region to be used for computing the
+ * signature.
+ * @since 3.0
+ */
+ public AwsRegionProvider getRegionProvider() {
+ return this.regionProvider;
+ }
+
/**
* @return the role, may be {@literal null} if none.
*/
@@ -149,6 +166,8 @@ public class AwsIamAuthenticationOptions {
@Nullable
private AwsCredentialsProvider credentialsProvider;
+ private AwsRegionProvider regionProvider = DefaultAwsRegionProviderChain.builder().build();
+
@Nullable
private String role;
@@ -204,6 +223,21 @@ public class AwsIamAuthenticationOptions {
return this;
}
+ /**
+ * Configure an {@link AwsRegionProvider}, required to calculate the region to be
+ * used for computing the signature.
+ * @param regionProvider must not be {@literal null}.
+ * @return {@code this} {@link AwsIamAuthenticationOptionsBuilder}.
+ * @since 3.0
+ */
+ public AwsIamAuthenticationOptionsBuilder regionProvider(AwsRegionProvider regionProvider) {
+
+ Assert.notNull(regionProvider, "AwsRegionProvider must not be null");
+
+ this.regionProvider = regionProvider;
+ return this;
+ }
+
/**
* Configure the name of the role against which the login is being attempted. If
* role is not specified, the friendly name (i.e., role name or username) of the
@@ -267,8 +301,8 @@ public class AwsIamAuthenticationOptions {
Assert.state(this.credentialsProvider != null, "Credentials or CredentialProvider must not be null");
- return new AwsIamAuthenticationOptions(this.path, this.credentialsProvider, this.role, this.serverId,
- this.endpointUri);
+ return new AwsIamAuthenticationOptions(this.path, this.credentialsProvider, this.regionProvider, this.role,
+ this.serverId, this.endpointUri);
}
}
diff --git a/spring-vault-core/src/test/java/org/springframework/vault/authentication/AwsIamAuthenticationUnitTests.java b/spring-vault-core/src/test/java/org/springframework/vault/authentication/AwsIamAuthenticationUnitTests.java
index 35a7f6dd..17e99ed4 100644
--- a/spring-vault-core/src/test/java/org/springframework/vault/authentication/AwsIamAuthenticationUnitTests.java
+++ b/spring-vault-core/src/test/java/org/springframework/vault/authentication/AwsIamAuthenticationUnitTests.java
@@ -17,10 +17,10 @@ package org.springframework.vault.authentication;
import java.time.Duration;
-import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
-
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
+import software.amazon.awssdk.regions.Region;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
@@ -29,11 +29,9 @@ import org.springframework.vault.client.VaultClients;
import org.springframework.vault.support.VaultToken;
import org.springframework.web.client.RestTemplate;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.springframework.test.web.client.match.MockRestRequestMatchers.jsonPath;
-import static org.springframework.test.web.client.match.MockRestRequestMatchers.method;
-import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
-import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
+import static org.assertj.core.api.Assertions.*;
+import static org.springframework.test.web.client.match.MockRestRequestMatchers.*;
+import static org.springframework.test.web.client.response.MockRestResponseCreators.*;
/**
* Unit test for {@link AwsIamAuthentication}.
@@ -68,7 +66,7 @@ class AwsIamAuthenticationUnitTests {
+ "}"));
AwsIamAuthenticationOptions options = AwsIamAuthenticationOptions.builder().role("foo-role")
- .credentials(AwsBasicCredentials.create("foo", "bar")).build();
+ .regionProvider(() -> Region.US_WEST_1).credentials(AwsBasicCredentials.create("foo", "bar")).build();
AwsIamAuthentication sut = new AwsIamAuthentication(options, this.restTemplate);
VaultToken login = sut.login();
@@ -91,7 +89,7 @@ class AwsIamAuthenticationUnitTests {
+ "}"));
AwsIamAuthenticationOptions options = AwsIamAuthenticationOptions.builder().role("foo-role")
- .credentials(AwsBasicCredentials.create("foo", "bar")).build();
+ .regionProvider(() -> Region.US_WEST_1).credentials(AwsBasicCredentials.create("foo", "bar")).build();
AuthenticationSteps steps = AwsIamAuthentication.createAuthenticationSteps(options);
AuthenticationStepsExecutor executor = new AuthenticationStepsExecutor(steps, this.restTemplate);
diff --git a/spring-vault-dependencies/pom.xml b/spring-vault-dependencies/pom.xml
index 5c96d114..73851c3d 100644
--- a/spring-vault-dependencies/pom.xml
+++ b/spring-vault-dependencies/pom.xml
@@ -63,7 +63,7 @@
4.1.72.Final
3.14.9
2.13.3
- 2.17.152
+ 2.17.195
v1-rev20210226-1.31.0
1.2.2
0.22.2
diff --git a/src/main/asciidoc/new-features.adoc b/src/main/asciidoc/new-features.adoc
index ed3fedec..43bfca86 100644
--- a/src/main/asciidoc/new-features.adoc
+++ b/src/main/asciidoc/new-features.adoc
@@ -1,7 +1,12 @@
[[new-features]]
== New & Noteworthy
-[[new-features.2-4-0]]
+[[new-features.3-0-0]]
+=== What's new in Spring Vault 3.0
+
+* Upgrade to Spring Framework 6 and Java 17 baseline
+* Upgrade to AWS SDK 2.
+
=== What's new in Spring Vault 2.4
* Support for <> for Username/Password, LDAP, Okta, and RADIUS authentication.