diff --git a/.changes/next-release/feature-AWSRDS-e6fec25.json b/.changes/next-release/feature-AWSRDS-e6fec25.json
new file mode 100644
index 000000000000..46359b78d941
--- /dev/null
+++ b/.changes/next-release/feature-AWSRDS-e6fec25.json
@@ -0,0 +1,6 @@
+{
+ "type": "feature",
+ "category": "AWS RDS",
+ "contributor": "abrooksv",
+ "description": "Add the ability to generate IAM auth tokens for RDS using `RdsUtilities`"
+}
diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/model/config/customization/UtilitiesMethod.java b/codegen/src/main/java/software/amazon/awssdk/codegen/model/config/customization/UtilitiesMethod.java
index 331c3481ae8d..dbe82e57961a 100644
--- a/codegen/src/main/java/software/amazon/awssdk/codegen/model/config/customization/UtilitiesMethod.java
+++ b/codegen/src/main/java/software/amazon/awssdk/codegen/model/config/customization/UtilitiesMethod.java
@@ -29,6 +29,9 @@ public class UtilitiesMethod {
/** Fqcn of the return type of the operation */
private String returnType;
+ /** Fqcn of the instance type to be created */
+ private String instanceType;
+
/**
* The utilities method will call a protected create() method in the hand-written Utilities class.
* These the ordered list of parameters that needs to be passed to the create method.
@@ -50,4 +53,12 @@ public List getCreateMethodParams() {
public void setCreateMethodParams(List createMethodParams) {
this.createMethodParams = createMethodParams;
}
+
+ public String getInstanceType() {
+ return instanceType;
+ }
+
+ public void setInstanceType(String instanceType) {
+ this.instanceType = instanceType;
+ }
}
diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/AsyncClientClass.java b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/AsyncClientClass.java
index 4d8336d18a03..e27a17bbc74d 100644
--- a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/AsyncClientClass.java
+++ b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/AsyncClientClass.java
@@ -383,13 +383,19 @@ private TypeName eventStreamType(ShapeModel shapeModel) {
private MethodSpec utilitiesMethod() {
UtilitiesMethod config = model.getCustomizationConfig().getUtilitiesMethod();
ClassName returnType = PoetUtils.classNameFromFqcn(config.getReturnType());
+ String instanceClass = config.getInstanceType();
+ if (instanceClass == null) {
+ instanceClass = config.getReturnType();
+ }
+
+ ClassName instanceType = PoetUtils.classNameFromFqcn(instanceClass);
return MethodSpec.methodBuilder(UtilitiesMethod.METHOD_NAME)
.returns(returnType)
.addModifiers(Modifier.PUBLIC)
.addAnnotation(Override.class)
.addStatement("return $T.create($L)",
- returnType,
+ instanceType,
String.join(",", config.getCreateMethodParams()))
.build();
}
diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/SyncClientClass.java b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/SyncClientClass.java
index 95724e518af9..7fe594733993 100644
--- a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/SyncClientClass.java
+++ b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/SyncClientClass.java
@@ -310,13 +310,19 @@ private MethodSpec closeMethod() {
private MethodSpec utilitiesMethod() {
UtilitiesMethod config = model.getCustomizationConfig().getUtilitiesMethod();
ClassName returnType = PoetUtils.classNameFromFqcn(config.getReturnType());
+ String instanceClass = config.getInstanceType();
+ if (instanceClass == null) {
+ instanceClass = config.getReturnType();
+ }
+
+ ClassName instanceType = PoetUtils.classNameFromFqcn(instanceClass);
return MethodSpec.methodBuilder(UtilitiesMethod.METHOD_NAME)
.returns(returnType)
.addModifiers(Modifier.PUBLIC)
.addAnnotation(Override.class)
.addStatement("return $T.create($L)",
- returnType,
+ instanceType,
String.join(",", config.getCreateMethodParams()))
.build();
}
diff --git a/docs/LaunchChangelog.md b/docs/LaunchChangelog.md
index 36aa10162009..91bb278af7b8 100644
--- a/docs/LaunchChangelog.md
+++ b/docs/LaunchChangelog.md
@@ -449,6 +449,10 @@ The S3 client in 2.0 is drastically different from the client in 1.11, because i
1. An SQS client may no longer access SQS queues in regions different than the one with which the client was configured.
+## 4.4. RDS Changes
+
+1. The class`RdsIamAuthTokenGenerator` has been replaced with `RdsUtilities#generateAuthenticationToken`.
+
# 5. Profile File Changes
The parsing of the `~/.aws/config` and `~/.aws/credentials` has changed to more closely emulate that used by the AWS CLI.
diff --git a/services/rds/src/main/java/software/amazon/awssdk/services/rds/DefaultRdsUtilities.java b/services/rds/src/main/java/software/amazon/awssdk/services/rds/DefaultRdsUtilities.java
new file mode 100644
index 000000000000..f77082792631
--- /dev/null
+++ b/services/rds/src/main/java/software/amazon/awssdk/services/rds/DefaultRdsUtilities.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package software.amazon.awssdk.services.rds;
+
+import java.time.Clock;
+import java.time.Duration;
+import java.time.Instant;
+import software.amazon.awssdk.annotations.Immutable;
+import software.amazon.awssdk.annotations.SdkInternalApi;
+import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
+import software.amazon.awssdk.auth.signer.Aws4Signer;
+import software.amazon.awssdk.auth.signer.params.Aws4PresignerParams;
+import software.amazon.awssdk.awscore.client.config.AwsClientOption;
+import software.amazon.awssdk.core.client.config.SdkClientConfiguration;
+import software.amazon.awssdk.http.SdkHttpFullRequest;
+import software.amazon.awssdk.http.SdkHttpMethod;
+import software.amazon.awssdk.regions.Region;
+import software.amazon.awssdk.services.rds.model.GenerateAuthenticationTokenRequest;
+import software.amazon.awssdk.utils.StringUtils;
+
+@Immutable
+@SdkInternalApi
+final class DefaultRdsUtilities implements RdsUtilities {
+ // The time the IAM token is good for. https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.IAMDBAuth.html
+ private static final Duration EXPIRATION_DURATION = Duration.ofMinutes(15);
+
+ private final Aws4Signer signer = Aws4Signer.create();
+ private final Region region;
+ private final AwsCredentialsProvider credentialsProvider;
+ private final Clock clock;
+
+ DefaultRdsUtilities(DefaultBuilder builder) {
+ this(builder, Clock.systemUTC());
+ }
+
+ /**
+ * Test Only
+ */
+ DefaultRdsUtilities(DefaultBuilder builder, Clock clock) {
+ this.credentialsProvider = builder.credentialsProvider;
+ this.region = builder.region;
+ this.clock = clock;
+ }
+
+ /**
+ * Used by RDS low-level client's utilities() method
+ */
+ @SdkInternalApi
+ static RdsUtilities create(SdkClientConfiguration clientConfiguration) {
+ return new DefaultBuilder().clientConfiguration(clientConfiguration).build();
+ }
+
+ @Override
+ public String generateAuthenticationToken(GenerateAuthenticationTokenRequest request) {
+ SdkHttpFullRequest httpRequest = SdkHttpFullRequest.builder()
+ .method(SdkHttpMethod.GET)
+ .protocol("https")
+ .host(request.hostname())
+ .port(request.port())
+ .encodedPath("/")
+ .putRawQueryParameter("DBUser", request.username())
+ .putRawQueryParameter("Action", "connect")
+ .build();
+
+ Instant expirationTime = Instant.now(clock).plus(EXPIRATION_DURATION);
+ Aws4PresignerParams presignRequest = Aws4PresignerParams.builder()
+ .signingClockOverride(clock)
+ .expirationTime(expirationTime)
+ .awsCredentials(resolveCredentials(request).resolveCredentials())
+ .signingName("rds-db")
+ .signingRegion(resolveRegion(request))
+ .build();
+
+ SdkHttpFullRequest fullRequest = signer.presign(httpRequest, presignRequest);
+ String signedUrl = fullRequest.getUri().toString();
+
+ // Format should be: >:>/?Action=connect&DBUser=>&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Expi...
+ // Note: This must be the real RDS hostname, not proxy or tunnels
+ return StringUtils.replacePrefixIgnoreCase(signedUrl, "https://", "");
+ }
+
+ private Region resolveRegion(GenerateAuthenticationTokenRequest request) {
+ if (request.region() != null) {
+ return request.region();
+ }
+
+ if (this.region != null) {
+ return this.region;
+ }
+
+ throw new IllegalArgumentException("Region should be provided either in GenerateAuthenticationTokenRequest object " +
+ "or RdsUtilities object");
+ }
+
+ private AwsCredentialsProvider resolveCredentials(GenerateAuthenticationTokenRequest request) {
+ if (request.credentialsProvider() != null) {
+ return request.credentialsProvider();
+ }
+
+ if (this.credentialsProvider != null) {
+ return this.credentialsProvider;
+ }
+
+ throw new IllegalArgumentException("CredentialProvider should be provided either in GenerateAuthenticationTokenRequest " +
+ "object or RdsUtilities object");
+ }
+
+ @SdkInternalApi
+ static final class DefaultBuilder implements Builder {
+ private Region region;
+ private AwsCredentialsProvider credentialsProvider;
+
+ DefaultBuilder() {
+ }
+
+ Builder clientConfiguration(SdkClientConfiguration clientConfiguration) {
+ this.credentialsProvider = clientConfiguration.option(AwsClientOption.CREDENTIALS_PROVIDER);
+ this.region = clientConfiguration.option(AwsClientOption.AWS_REGION);
+
+ return this;
+ }
+
+ @Override
+ public Builder region(Region region) {
+ this.region = region;
+ return this;
+ }
+
+ @Override
+ public Builder credentialsProvider(AwsCredentialsProvider credentialsProvider) {
+ this.credentialsProvider = credentialsProvider;
+ return this;
+ }
+
+ /**
+ * Construct a {@link RdsUtilities} object.
+ */
+ @Override
+ public RdsUtilities build() {
+ return new DefaultRdsUtilities(this);
+ }
+ }
+}
\ No newline at end of file
diff --git a/services/rds/src/main/java/software/amazon/awssdk/services/rds/RdsUtilities.java b/services/rds/src/main/java/software/amazon/awssdk/services/rds/RdsUtilities.java
new file mode 100644
index 000000000000..d7ce5e19c06b
--- /dev/null
+++ b/services/rds/src/main/java/software/amazon/awssdk/services/rds/RdsUtilities.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package software.amazon.awssdk.services.rds;
+
+import java.util.function.Consumer;
+import software.amazon.awssdk.annotations.SdkPublicApi;
+import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
+import software.amazon.awssdk.regions.Region;
+import software.amazon.awssdk.services.rds.model.GenerateAuthenticationTokenRequest;
+
+/**
+ * Utilities for working with RDS. An instance of this class can be created by:
+ *
+ * 1) Using the low-level client {@link RdsClient#utilities()} (or {@link RdsAsyncClient#utilities()}} method. This is
+ * recommended as SDK will use the same configuration from the {@link RdsClient} object to create the {@link RdsUtilities} object.
+ *
+ *
+ * RdsClient rdsClient = RdsClient.create();
+ * RdsUtilities utilities = rdsClient.utilities();
+ *
+ *
+ *
+ *
+ * 2) Directly using the {@link #builder()} method.
+ *
+ *
+ * RdsUtilities utilities = RdsUtilities.builder()
+ * .credentialsProvider(DefaultCredentialsProvider.create())
+ * .region(Region.US_WEST_2)
+ * .build()
+ *
+ *
+ *
+ * Note: This class does not make network calls.
+ */
+@SdkPublicApi
+public interface RdsUtilities {
+ /**
+ * Create a builder that can be used to configure and create a {@link RdsUtilities}.
+ */
+ static Builder builder() {
+ return new DefaultRdsUtilities.DefaultBuilder();
+ }
+
+ /**
+ * Generates an authorization tokens for IAM authentication to an RDS database.
+ *
+ * @param request The request used to generate the auth token
+ * @return String to use as the RDS auth token
+ * @throws IllegalArgumentException if the required parameters are not valid
+ */
+ default String generateAuthenticationToken(Consumer request) {
+ return generateAuthenticationToken(GenerateAuthenticationTokenRequest.builder().applyMutation(request).build());
+ }
+
+ /**
+ * Generates an authorization tokens for IAM authentication to an RDS database.
+ *
+ * @param request The request used to generate the auth token
+ * @return String to use as the RDS auth token
+ * @throws IllegalArgumentException if the required parameters are not valid
+ */
+ default String generateAuthenticationToken(GenerateAuthenticationTokenRequest request) {
+ RdsUtilities.builder().region(Region.US_WEST_2).build();
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Builder for creating an instance of {@link RdsUtilities}. It can be configured using {@link RdsUtilities#builder()}.
+ * Once configured, the {@link RdsUtilities} can created using {@link #build()}.
+ */
+ @SdkPublicApi
+ interface Builder {
+ /**
+ * The default region to use when working with the methods in {@link RdsUtilities} class.
+ *
+ * @return This object for method chaining
+ */
+ Builder region(Region region);
+
+ /**
+ * The default credentials provider to use when working with the methods in {@link RdsUtilities} class.
+ *
+ * @return This object for method chaining
+ */
+ Builder credentialsProvider(AwsCredentialsProvider credentialsProvider);
+
+ /**
+ * Create a {@link RdsUtilities}
+ */
+ RdsUtilities build();
+ }
+}
diff --git a/services/rds/src/main/java/software/amazon/awssdk/services/rds/model/GenerateAuthenticationTokenRequest.java b/services/rds/src/main/java/software/amazon/awssdk/services/rds/model/GenerateAuthenticationTokenRequest.java
new file mode 100644
index 000000000000..2c23705ae4ce
--- /dev/null
+++ b/services/rds/src/main/java/software/amazon/awssdk/services/rds/model/GenerateAuthenticationTokenRequest.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package software.amazon.awssdk.services.rds.model;
+
+import software.amazon.awssdk.annotations.NotThreadSafe;
+import software.amazon.awssdk.annotations.SdkPublicApi;
+import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
+import software.amazon.awssdk.regions.Region;
+import software.amazon.awssdk.services.rds.RdsUtilities;
+import software.amazon.awssdk.utils.Validate;
+import software.amazon.awssdk.utils.builder.CopyableBuilder;
+import software.amazon.awssdk.utils.builder.ToCopyableBuilder;
+
+/**
+ * Input parameters for generating an auth token for IAM database authentication.
+ */
+@SdkPublicApi
+public final class GenerateAuthenticationTokenRequest implements
+ ToCopyableBuilder {
+ private final String hostname;
+ private final int port;
+ private final String username;
+ private final Region region;
+ private final AwsCredentialsProvider credentialsProvider;
+
+ private GenerateAuthenticationTokenRequest(BuilderImpl builder) {
+ this.hostname = Validate.notEmpty(builder.hostname, "hostname");
+ this.port = Validate.isPositive(builder.port, "port");
+ this.username = Validate.notEmpty(builder.username, "username");
+ this.region = builder.region;
+ this.credentialsProvider = builder.credentialsProvider;
+ }
+
+ /**
+ * @return The hostname of the database to connect to
+ */
+ public String hostname() {
+ return hostname;
+ }
+
+ /**
+ * @return The port of the database to connect to
+ */
+ public int port() {
+ return port;
+ }
+
+ /**
+ * @return The username to log in as.
+ */
+ public String username() {
+ return username;
+ }
+
+ /**
+ * @return The region the database is hosted in. If specified, takes precedence over the value specified in
+ * {@link RdsUtilities.Builder#region(Region)}
+ */
+ public Region region() {
+ return region;
+ }
+
+ /**
+ * @return The credentials provider to sign the IAM auth request with. If specified, takes precedence over the value
+ * specified in {@link RdsUtilities.Builder#credentialsProvider(AwsCredentialsProvider)}}
+ */
+ public AwsCredentialsProvider credentialsProvider() {
+ return credentialsProvider;
+ }
+
+ @Override
+ public Builder toBuilder() {
+ return new BuilderImpl(this);
+ }
+
+ /**
+ * Creates a builder for {@link RdsUtilities}.
+ */
+ public static Builder builder() {
+ return new BuilderImpl();
+ }
+
+ /**
+ * A builder for a {@link GenerateAuthenticationTokenRequest}, created with {@link #builder()}.
+ */
+ @SdkPublicApi
+ @NotThreadSafe
+ public interface Builder extends CopyableBuilder {
+ /**
+ * The hostname of the database to connect to
+ *
+ * @return This object for method chaining
+ */
+ Builder hostname(String endpoint);
+
+ /**
+ * The port number the database is listening on.
+ *
+ * @return This object for method chaining
+ */
+ Builder port(int port);
+
+ /**
+ * The username to log in as.
+ *
+ * @return This object for method chaining
+ */
+ Builder username(String userName);
+
+ /**
+ * The region the database is hosted in. If specified, takes precedence over the value specified in
+ * {@link RdsUtilities.Builder#region(Region)}
+ *
+ * @return This object for method chaining
+ */
+ Builder region(Region region);
+
+ /**
+ * The credentials provider to sign the IAM auth request with. If specified, takes precedence over the value
+ * specified in {@link RdsUtilities.Builder#credentialsProvider(AwsCredentialsProvider)}}
+ *
+ * @return This object for method chaining
+ */
+ Builder credentialsProvider(AwsCredentialsProvider credentialsProvider);
+
+ @Override
+ GenerateAuthenticationTokenRequest build();
+ }
+
+ private static final class BuilderImpl implements Builder {
+ private String hostname;
+ private int port;
+ private String username;
+ private Region region;
+ private AwsCredentialsProvider credentialsProvider;
+
+ private BuilderImpl() {
+ }
+
+ private BuilderImpl(GenerateAuthenticationTokenRequest request) {
+ this.hostname = request.hostname;
+ this.port = request.port;
+ this.username = request.username;
+ this.region = request.region;
+ this.credentialsProvider = request.credentialsProvider;
+ }
+
+ public Builder hostname(String endpoint) {
+ this.hostname = endpoint;
+ return this;
+ }
+
+ public Builder port(int port) {
+ this.port = port;
+ return this;
+ }
+
+ public Builder username(String userName) {
+ this.username = userName;
+ return this;
+ }
+
+ public Builder region(Region region) {
+ this.region = region;
+ return this;
+ }
+
+ public Builder credentialsProvider(AwsCredentialsProvider credentialsProvider) {
+ this.credentialsProvider = credentialsProvider;
+ return this;
+ }
+
+ public GenerateAuthenticationTokenRequest build() {
+ return new GenerateAuthenticationTokenRequest(this);
+ }
+ }
+}
\ No newline at end of file
diff --git a/services/rds/src/main/resources/codegen-resources/customization.config b/services/rds/src/main/resources/codegen-resources/customization.config
index 0eb1b018e172..9c878f2672a1 100644
--- a/services/rds/src/main/resources/codegen-resources/customization.config
+++ b/services/rds/src/main/resources/codegen-resources/customization.config
@@ -83,5 +83,12 @@
"describeReservedDBInstances",
"describeReservedDBInstancesOfferings",
"describeSourceRegions"
- ]
+ ],
+ "utilitiesMethod": {
+ "returnType": "software.amazon.awssdk.services.rds.RdsUtilities",
+ "instanceType": "software.amazon.awssdk.services.rds.DefaultRdsUtilities",
+ "createMethodParams": [
+ "clientConfiguration"
+ ]
+ }
}
diff --git a/services/rds/src/test/java/software/amazon/awssdk/services/rds/DefaultRdsUtilitiesTest.java b/services/rds/src/test/java/software/amazon/awssdk/services/rds/DefaultRdsUtilitiesTest.java
new file mode 100644
index 000000000000..125b899a6d4a
--- /dev/null
+++ b/services/rds/src/test/java/software/amazon/awssdk/services/rds/DefaultRdsUtilitiesTest.java
@@ -0,0 +1,128 @@
+package software.amazon.awssdk.services.rds;
+
+import org.junit.Test;
+import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
+import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
+import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
+import software.amazon.awssdk.regions.Region;
+
+import java.time.Clock;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import software.amazon.awssdk.services.rds.DefaultRdsUtilities.DefaultBuilder;
+
+import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
+import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;
+
+public class DefaultRdsUtilitiesTest {
+ private final ZoneId utcZone = ZoneId.of("UTC").normalized();
+ private final Clock fixedClock = Clock.fixed(ZonedDateTime.of(2016, 11, 7, 17, 39, 33, 0, utcZone).toInstant(), utcZone);
+
+ @Test
+ public void testTokenGenerationWithBuilderDefaults() {
+ AwsCredentialsProvider credentialsProvider = StaticCredentialsProvider.create(
+ AwsBasicCredentials.create("access_key", "secret_key")
+ );
+ DefaultBuilder utilitiesBuilder = (DefaultBuilder) RdsUtilities.builder()
+ .credentialsProvider(credentialsProvider)
+ .region(Region.US_EAST_1);
+
+ DefaultRdsUtilities rdsUtilities = new DefaultRdsUtilities(utilitiesBuilder, fixedClock);
+
+ String authenticationToken = rdsUtilities.generateAuthenticationToken(builder -> {
+ builder.username("mySQLUser")
+ .hostname("host.us-east-1.amazonaws.com")
+ .port(3306);
+ });
+
+ String expectedToken = "host.us-east-1.amazonaws.com:3306/?DBUser=mySQLUser&Action=connect&" +
+ "X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20161107T173933Z&X-Amz-SignedHeaders=host&" +
+ "X-Amz-Expires=900&X-Amz-Credential=access_key%2F20161107%2Fus-east-1%2Frds-db%2Faws4_request&" +
+ "X-Amz-Signature=87ab58107ef49f1c311a412f98b7f976b0b5152ffb559f0d36c6c9a0c5e0e362";
+ assertThat(authenticationToken).isEqualTo(expectedToken);
+ }
+
+ @Test
+ public void testTokenGenerationWithOverriddenCredentials() {
+ AwsCredentialsProvider credentialsProvider = StaticCredentialsProvider.create(
+ AwsBasicCredentials.create("foo", "bar")
+ );
+ DefaultBuilder utilitiesBuilder = (DefaultBuilder) RdsUtilities.builder()
+ .credentialsProvider(credentialsProvider)
+ .region(Region.US_EAST_1);
+ DefaultRdsUtilities rdsUtilities = new DefaultRdsUtilities(utilitiesBuilder, fixedClock);
+
+ String authenticationToken = rdsUtilities.generateAuthenticationToken(builder -> {
+ builder.username("mySQLUser")
+ .hostname("host.us-east-1.amazonaws.com")
+ .port(3306)
+ .credentialsProvider(StaticCredentialsProvider.create(
+ AwsBasicCredentials.create("access_key", "secret_key")
+ ));
+ });
+
+ String expectedToken = "host.us-east-1.amazonaws.com:3306/?DBUser=mySQLUser&Action=connect&" +
+ "X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20161107T173933Z&X-Amz-SignedHeaders=host&" +
+ "X-Amz-Expires=900&X-Amz-Credential=access_key%2F20161107%2Fus-east-1%2Frds-db%2Faws4_request&" +
+ "X-Amz-Signature=87ab58107ef49f1c311a412f98b7f976b0b5152ffb559f0d36c6c9a0c5e0e362";
+ assertThat(authenticationToken).isEqualTo(expectedToken);
+ }
+
+ @Test
+ public void testTokenGenerationWithOverriddenRegion() {
+ AwsCredentialsProvider credentialsProvider = StaticCredentialsProvider.create(
+ AwsBasicCredentials.create("access_key", "secret_key")
+ );
+ DefaultBuilder utilitiesBuilder = (DefaultBuilder) RdsUtilities.builder()
+ .credentialsProvider(credentialsProvider)
+ .region(Region.US_WEST_2);
+
+ DefaultRdsUtilities rdsUtilities = new DefaultRdsUtilities(utilitiesBuilder, fixedClock);
+
+ String authenticationToken = rdsUtilities.generateAuthenticationToken(builder -> {
+ builder.username("mySQLUser")
+ .hostname("host.us-east-1.amazonaws.com")
+ .port(3306)
+ .region(Region.US_EAST_1);
+ });
+
+ String expectedToken = "host.us-east-1.amazonaws.com:3306/?DBUser=mySQLUser&Action=connect&" +
+ "X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20161107T173933Z&X-Amz-SignedHeaders=host&" +
+ "X-Amz-Expires=900&X-Amz-Credential=access_key%2F20161107%2Fus-east-1%2Frds-db%2Faws4_request&" +
+ "X-Amz-Signature=87ab58107ef49f1c311a412f98b7f976b0b5152ffb559f0d36c6c9a0c5e0e362";
+ assertThat(authenticationToken).isEqualTo(expectedToken);
+ }
+
+ @Test
+ public void testMissingRegionThrowsException() {
+ AwsCredentialsProvider credentialsProvider = StaticCredentialsProvider.create(
+ AwsBasicCredentials.create("access_key", "secret_key")
+ );
+ DefaultBuilder utilitiesBuilder = (DefaultBuilder) RdsUtilities.builder()
+ .credentialsProvider(credentialsProvider);
+
+ DefaultRdsUtilities rdsUtilities = new DefaultRdsUtilities(utilitiesBuilder, fixedClock);
+
+ assertThatThrownBy(() -> rdsUtilities.generateAuthenticationToken(builder -> {
+ builder.username("mySQLUser")
+ .hostname("host.us-east-1.amazonaws.com")
+ .port(3306);
+ })).isInstanceOf(IllegalArgumentException.class)
+ .hasMessageContaining("Region should be provided");
+ }
+
+ @Test
+ public void testMissingCredentialsThrowsException() {
+ DefaultBuilder utilitiesBuilder = (DefaultBuilder) RdsUtilities.builder()
+ .region(Region.US_WEST_2);
+
+ DefaultRdsUtilities rdsUtilities = new DefaultRdsUtilities(utilitiesBuilder, fixedClock);
+
+ assertThatThrownBy(() -> rdsUtilities.generateAuthenticationToken(builder -> {
+ builder.username("mySQLUser")
+ .hostname("host.us-east-1.amazonaws.com")
+ .port(3306);
+ })).isInstanceOf(IllegalArgumentException.class)
+ .hasMessageContaining("CredentialProvider should be provided");
+ }
+}
\ No newline at end of file