diff --git a/aws-advanced-jdbc-wrapper-bundle/build.gradle.kts b/aws-advanced-jdbc-wrapper-bundle/build.gradle.kts index 6e290c1b2..74cba7996 100644 --- a/aws-advanced-jdbc-wrapper-bundle/build.gradle.kts +++ b/aws-advanced-jdbc-wrapper-bundle/build.gradle.kts @@ -25,8 +25,8 @@ repositories { dependencies { implementation("org.apache.httpcomponents:httpclient:4.5.14") - implementation("software.amazon.awssdk:rds:2.29.6") - implementation("software.amazon.awssdk:sts:2.27.22") + implementation("software.amazon.awssdk:rds:2.29.23") + implementation("software.amazon.awssdk:sts:2.29.23") implementation(project(":aws-advanced-jdbc-wrapper")) } diff --git a/examples/AWSDriverExample/build.gradle.kts b/examples/AWSDriverExample/build.gradle.kts index af8a41272..50e226410 100644 --- a/examples/AWSDriverExample/build.gradle.kts +++ b/examples/AWSDriverExample/build.gradle.kts @@ -18,9 +18,9 @@ dependencies { implementation("org.springframework.boot:spring-boot-starter-jdbc:2.7.13") // 2.7.13 is the last version compatible with Java 8 implementation("org.postgresql:postgresql:42.7.4") implementation("mysql:mysql-connector-java:8.0.33") - implementation("software.amazon.awssdk:rds:2.29.6") - implementation("software.amazon.awssdk:secretsmanager:2.28.11") - implementation("software.amazon.awssdk:sts:2.27.22") + implementation("software.amazon.awssdk:rds:2.29.23") + implementation("software.amazon.awssdk:secretsmanager:2.29.23") + implementation("software.amazon.awssdk:sts:2.29.23") implementation("com.fasterxml.jackson.core:jackson-databind:2.17.1") implementation(project(":aws-advanced-jdbc-wrapper")) implementation("io.opentelemetry:opentelemetry-api:1.42.1") diff --git a/examples/DBCPExample/build.gradle.kts b/examples/DBCPExample/build.gradle.kts index 115a0579b..c7a35529a 100644 --- a/examples/DBCPExample/build.gradle.kts +++ b/examples/DBCPExample/build.gradle.kts @@ -19,5 +19,5 @@ dependencies { implementation("mysql:mysql-connector-java:8.0.33") implementation(project(":aws-advanced-jdbc-wrapper")) implementation("org.apache.commons:commons-dbcp2:2.12.0") - implementation("software.amazon.awssdk:rds:2.29.6") + implementation("software.amazon.awssdk:rds:2.29.23") } diff --git a/examples/SpringHibernateBalancedReaderOneDataSourceExample/build.gradle.kts b/examples/SpringHibernateBalancedReaderOneDataSourceExample/build.gradle.kts index 1eff2de23..c7f76ce6c 100644 --- a/examples/SpringHibernateBalancedReaderOneDataSourceExample/build.gradle.kts +++ b/examples/SpringHibernateBalancedReaderOneDataSourceExample/build.gradle.kts @@ -23,6 +23,6 @@ dependencies { implementation("org.springframework.boot:spring-boot-starter-data-jpa") implementation("org.springframework.retry:spring-retry") implementation("org.postgresql:postgresql:42.7.4") - implementation("software.amazon.awssdk:rds:2.29.6") + implementation("software.amazon.awssdk:rds:2.29.23") implementation(project(":aws-advanced-jdbc-wrapper")) } diff --git a/examples/SpringHibernateBalancedReaderTwoDataSourceExample/build.gradle.kts b/examples/SpringHibernateBalancedReaderTwoDataSourceExample/build.gradle.kts index 1eff2de23..c7f76ce6c 100644 --- a/examples/SpringHibernateBalancedReaderTwoDataSourceExample/build.gradle.kts +++ b/examples/SpringHibernateBalancedReaderTwoDataSourceExample/build.gradle.kts @@ -23,6 +23,6 @@ dependencies { implementation("org.springframework.boot:spring-boot-starter-data-jpa") implementation("org.springframework.retry:spring-retry") implementation("org.postgresql:postgresql:42.7.4") - implementation("software.amazon.awssdk:rds:2.29.6") + implementation("software.amazon.awssdk:rds:2.29.23") implementation(project(":aws-advanced-jdbc-wrapper")) } diff --git a/examples/SpringHibernateExample/build.gradle.kts b/examples/SpringHibernateExample/build.gradle.kts index f09f0d913..69aa20461 100644 --- a/examples/SpringHibernateExample/build.gradle.kts +++ b/examples/SpringHibernateExample/build.gradle.kts @@ -23,6 +23,6 @@ dependencies { implementation("org.springframework.boot:spring-boot-starter-data-jpa") implementation("org.springframework.boot:spring-boot-starter-web") implementation("org.postgresql:postgresql:42.7.4") - implementation("software.amazon.awssdk:rds:2.29.6") + implementation("software.amazon.awssdk:rds:2.29.23") implementation(project(":aws-advanced-jdbc-wrapper")) } diff --git a/examples/SpringTxFailoverExample/README.md b/examples/SpringTxFailoverExample/README.md index 57f35d544..8aa160eec 100644 --- a/examples/SpringTxFailoverExample/README.md +++ b/examples/SpringTxFailoverExample/README.md @@ -102,7 +102,7 @@ dependencies { implementation("org.springframework.retry:spring-retry:1.3.4") implementation("org.springframework:spring-aspects:5.3.29") implementation("org.postgresql:postgresql:42.5.4") - implementation("software.amazon.awssdk:rds:2.25.2") + implementation("software.amazon.awssdk:rds:2.29.23") implementation("software.amazon.jdbc:aws-advanced-jdbc-wrapper:latest") } ``` @@ -145,7 +145,7 @@ import java.util.List; import java.util.Map; public interface ExampleDao { - public List> getAll(); + List> getAll(); } ``` diff --git a/examples/SpringWildflyExample/spring/build.gradle.kts b/examples/SpringWildflyExample/spring/build.gradle.kts index 24b8b2b4f..5060fb31f 100644 --- a/examples/SpringWildflyExample/spring/build.gradle.kts +++ b/examples/SpringWildflyExample/spring/build.gradle.kts @@ -24,6 +24,6 @@ dependencies { implementation("org.springframework.boot:spring-boot-starter-web") runtimeOnly("org.springframework.boot:spring-boot-devtools") implementation("org.postgresql:postgresql:42.7.4") - implementation("software.amazon.awssdk:rds:2.29.6") + implementation("software.amazon.awssdk:rds:2.29.23") implementation(project(":aws-advanced-jdbc-wrapper")) } diff --git a/wrapper/build.gradle.kts b/wrapper/build.gradle.kts index 138b98ca3..107d279df 100644 --- a/wrapper/build.gradle.kts +++ b/wrapper/build.gradle.kts @@ -29,12 +29,12 @@ plugins { dependencies { implementation("org.checkerframework:checker-qual:3.48.1") compileOnly("org.apache.httpcomponents:httpclient:4.5.14") - compileOnly("software.amazon.awssdk:rds:2.29.6") - compileOnly("software.amazon.awssdk:auth:2.28.2") // Required for IAM (light implementation) - compileOnly("software.amazon.awssdk:http-client-spi:2.27.8") // Required for IAM (light implementation) - compileOnly("software.amazon.awssdk:sts:2.27.22") + compileOnly("software.amazon.awssdk:rds:2.29.23") + compileOnly("software.amazon.awssdk:auth:2.29.23") // Required for IAM (light implementation) + compileOnly("software.amazon.awssdk:http-client-spi:2.29.23") // Required for IAM (light implementation) + compileOnly("software.amazon.awssdk:sts:2.29.23") compileOnly("com.zaxxer:HikariCP:4.0.3") // Version 4.+ is compatible with Java 8 - compileOnly("software.amazon.awssdk:secretsmanager:2.28.11") + compileOnly("software.amazon.awssdk:secretsmanager:2.29.23") compileOnly("com.fasterxml.jackson.core:jackson-databind:2.17.1") compileOnly("mysql:mysql-connector-java:8.0.33") compileOnly("org.postgresql:postgresql:42.7.4") @@ -63,12 +63,12 @@ dependencies { testImplementation("com.zaxxer:HikariCP:4.0.3") // Version 4.+ is compatible with Java 8 testImplementation("org.springframework.boot:spring-boot-starter-jdbc:2.7.13") // 2.7.13 is the last version compatible with Java 8 testImplementation("org.mockito:mockito-inline:4.11.0") // 4.11.0 is the last version compatible with Java 8 - testImplementation("software.amazon.awssdk:rds:2.29.6") - testImplementation("software.amazon.awssdk:auth:2.28.2") // Required for IAM (light implementation) - testImplementation("software.amazon.awssdk:http-client-spi:2.27.8") // Required for IAM (light implementation) - testImplementation("software.amazon.awssdk:ec2:2.29.9") - testImplementation("software.amazon.awssdk:secretsmanager:2.28.11") - testImplementation("software.amazon.awssdk:sts:2.27.22") + testImplementation("software.amazon.awssdk:rds:2.29.23") + testImplementation("software.amazon.awssdk:auth:2.29.23") // Required for IAM (light implementation) + testImplementation("software.amazon.awssdk:http-client-spi:2.29.23") // Required for IAM (light implementation) + testImplementation("software.amazon.awssdk:ec2:2.29.23") + testImplementation("software.amazon.awssdk:secretsmanager:2.29.23") + testImplementation("software.amazon.awssdk:sts:2.29.23") testImplementation("org.testcontainers:testcontainers:1.20.1") testImplementation("org.testcontainers:mysql:1.20.1") testImplementation("org.testcontainers:postgresql:1.20.3") diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/iam/LightRdsUtility.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/iam/LightRdsUtility.java index 6859e1232..6606f47a8 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/iam/LightRdsUtility.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/iam/LightRdsUtility.java @@ -19,6 +19,7 @@ import java.time.Clock; import java.time.Duration; import java.time.Instant; +import java.time.ZoneId; import java.util.logging.Logger; import org.checkerframework.checker.nullness.qual.NonNull; import software.amazon.awssdk.auth.credentials.AwsCredentials; @@ -31,7 +32,6 @@ import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.utils.CompletableFutureUtils; import software.amazon.awssdk.utils.StringUtils; -import software.amazon.jdbc.util.Messages; public class LightRdsUtility implements IamTokenUtility { @@ -40,6 +40,17 @@ public class LightRdsUtility implements IamTokenUtility { // 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 Clock clock; + + public LightRdsUtility() { + this.clock = Clock.systemDefaultZone(); + } + + // For testing only + public LightRdsUtility(final Instant fixedInstant) { + this.clock = Clock.fixed(fixedInstant, ZoneId.systemDefault()); + } + @Override public String generateAuthenticationToken( final @NonNull AwsCredentialsProvider credentialsProvider, @@ -52,7 +63,6 @@ public String generateAuthenticationToken( // method generateAuthenticationToken(GenerateAuthenticationTokenRequest request). // Update this code when the original method changes. - final Clock clock = Clock.systemUTC(); final Aws4Signer signer = Aws4Signer.create(); final SdkHttpFullRequest httpRequest = SdkHttpFullRequest.builder() @@ -65,13 +75,13 @@ public String generateAuthenticationToken( .putRawQueryParameter("Action", "connect") .build(); - final Instant expirationTime = Instant.now(clock).plus(EXPIRATION_DURATION); + final Instant expirationTime = Instant.now(this.clock).plus(EXPIRATION_DURATION); final AwsCredentials credentials = CredentialUtils.toCredentials( CompletableFutureUtils.joinLikeSync(credentialsProvider.resolveIdentity())); final Aws4PresignerParams presignRequest = Aws4PresignerParams.builder() - .signingClockOverride(clock) + .signingClockOverride(this.clock) .expirationTime(expirationTime) .awsCredentials(credentials) .signingName("rds-db") diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/iam/RegularRdsUtility.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/iam/RegularRdsUtility.java index e5d44ffd4..cc098caf2 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/iam/RegularRdsUtility.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/iam/RegularRdsUtility.java @@ -23,6 +23,15 @@ public class RegularRdsUtility implements IamTokenUtility { + private RdsUtilities utilities = null; + + public RegularRdsUtility() {} + + // For testing only + public RegularRdsUtility(final RdsUtilities utilities) { + this.utilities = utilities; + } + @Override public String generateAuthenticationToken( final @NonNull AwsCredentialsProvider credentialsProvider, @@ -31,11 +40,13 @@ public String generateAuthenticationToken( final int port, final @NonNull String username) { - final RdsUtilities utilities = RdsUtilities.builder() - .credentialsProvider(credentialsProvider) - .region(region) - .build(); - return utilities.generateAuthenticationToken((builder) -> + if (this.utilities == null) { + this.utilities = RdsUtilities.builder() + .credentialsProvider(credentialsProvider) + .region(region) + .build(); + } + return this.utilities.generateAuthenticationToken((builder) -> builder .hostname(hostname) .port(port) diff --git a/wrapper/src/test/build.gradle.kts b/wrapper/src/test/build.gradle.kts index 9563d3830..9f677d615 100644 --- a/wrapper/src/test/build.gradle.kts +++ b/wrapper/src/test/build.gradle.kts @@ -41,9 +41,9 @@ dependencies { testImplementation("com.zaxxer:HikariCP:4.+") // version 4.+ is compatible with Java 8 testImplementation("org.springframework.boot:spring-boot-starter-jdbc:2.7.13") // 2.7.13 is the last version compatible with Java 8 testImplementation("org.mockito:mockito-inline:4.11.0") // 4.11.0 is the last version compatible with Java 8 - testImplementation("software.amazon.awssdk:ec2:2.25.2") - testImplementation("software.amazon.awssdk:rds:2.25.2") - testImplementation("software.amazon.awssdk:sts:2.25.2") + testImplementation("software.amazon.awssdk:ec2:2.29.23") + testImplementation("software.amazon.awssdk:rds:2.29.23") + testImplementation("software.amazon.awssdk:sts:2.29.23") testImplementation("org.testcontainers:testcontainers:1.17.+") testImplementation("org.testcontainers:mysql:1.17.+") testImplementation("org.testcontainers:postgresql:1.17.+") diff --git a/wrapper/src/test/java/integration/container/tests/AwsIamIntegrationTest.java b/wrapper/src/test/java/integration/container/tests/AwsIamIntegrationTest.java index 5b9e5dd69..96ddcfbd3 100644 --- a/wrapper/src/test/java/integration/container/tests/AwsIamIntegrationTest.java +++ b/wrapper/src/test/java/integration/container/tests/AwsIamIntegrationTest.java @@ -33,18 +33,22 @@ import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; +import java.time.Instant; import java.util.Properties; import java.util.logging.Logger; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; import org.junit.jupiter.api.TestTemplate; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mockito; import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.rds.RdsUtilities; +import software.amazon.awssdk.services.rds.TestDefaultRdsUtilities; import software.amazon.jdbc.HostSpec; import software.amazon.jdbc.HostSpecBuilder; import software.amazon.jdbc.PropertyDefinition; @@ -251,30 +255,46 @@ void test_AwsIam_UserAndPasswordPropertiesArePreserved() throws SQLException { @TestTemplate void test_TokenGenerators() { + // This test could be a simple unit test since it requires AWS credentials. + + final String clusterEndpoint = "test-cluster.cluster-xyz.us-west-2.rds.amazonaws.com"; + final int clusterEndpointPort = 5432; + final String region = "us-west-2"; + final String iamUsername = "jane_doe"; + final HostAvailabilityStrategy mockHostAvailabilityStrategy = Mockito.mock(HostAvailabilityStrategy.class); - final Properties awsIamProp = - initAwsIamProps(TestEnvironment.getCurrent().getInfo().getIamUsername(), ""); + final Properties awsIamProp = new Properties(); + awsIamProp.setProperty(IamAuthConnectionPlugin.IAM_REGION.name, region); + awsIamProp.setProperty(PropertyDefinition.USER.name, ""); final HostSpec hostSpec = new HostSpecBuilder(mockHostAvailabilityStrategy) - .host(TestEnvironment.getCurrent().getInfo().getDatabaseInfo().getClusterEndpoint()) + .host(clusterEndpoint) .build(); final AwsCredentialsProvider credentialsProvider = AwsCredentialsManager.getProvider(hostSpec, awsIamProp); - final String regularToken = new RegularRdsUtility().generateAuthenticationToken( + final Instant fixedInstant = Instant.parse("2024-10-20T00:00:00Z"); + + final RdsUtilities rdsUtilities = TestDefaultRdsUtilities.getDefaultRdsUtilities( + credentialsProvider, + Region.of(region), + fixedInstant); + final RegularRdsUtility regularRdsUtility = new RegularRdsUtility(rdsUtilities); + + final String regularToken = regularRdsUtility.generateAuthenticationToken( credentialsProvider, - Region.of(TestEnvironment.getCurrent().getInfo().getRegion()), - TestEnvironment.getCurrent().getInfo().getDatabaseInfo().getClusterEndpoint(), - TestEnvironment.getCurrent().getInfo().getDatabaseInfo().getClusterReadOnlyEndpointPort(), - TestEnvironment.getCurrent().getInfo().getIamUsername()); + Region.of(region), + clusterEndpoint, + clusterEndpointPort, + iamUsername); - final String lightToken = new LightRdsUtility().generateAuthenticationToken( + final String lightToken = new LightRdsUtility(fixedInstant).generateAuthenticationToken( credentialsProvider, - Region.of(TestEnvironment.getCurrent().getInfo().getRegion()), - TestEnvironment.getCurrent().getInfo().getDatabaseInfo().getClusterEndpoint(), - TestEnvironment.getCurrent().getInfo().getDatabaseInfo().getClusterReadOnlyEndpointPort(), - TestEnvironment.getCurrent().getInfo().getIamUsername()); + Region.of(region), + clusterEndpoint, + clusterEndpointPort, + iamUsername); assertEquals(regularToken, lightToken); } diff --git a/wrapper/src/test/java/software/amazon/awssdk/services/rds/TestDefaultRdsUtilities.java b/wrapper/src/test/java/software/amazon/awssdk/services/rds/TestDefaultRdsUtilities.java new file mode 100644 index 000000000..7d1a98234 --- /dev/null +++ b/wrapper/src/test/java/software/amazon/awssdk/services/rds/TestDefaultRdsUtilities.java @@ -0,0 +1,38 @@ +/* + * 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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.Instant; +import java.time.ZoneId; +import org.checkerframework.checker.nullness.qual.NonNull; +import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.rds.DefaultRdsUtilities.DefaultBuilder; + +public class TestDefaultRdsUtilities { + + public static DefaultRdsUtilities getDefaultRdsUtilities( + final @NonNull AwsCredentialsProvider credentialsProvider, + final @NonNull Region region, + Instant fixedInstant) { + final DefaultBuilder builder = (DefaultBuilder) new DefaultBuilder() + .credentialsProvider(credentialsProvider) + .region(region); + return new DefaultRdsUtilities(builder, Clock.fixed(fixedInstant, ZoneId.systemDefault())); + } +} diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/iam/IamAuthConnectionPluginTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/iam/IamAuthConnectionPluginTest.java index 00bc00c2f..a1b5fdab9 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/iam/IamAuthConnectionPluginTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/iam/IamAuthConnectionPluginTest.java @@ -44,6 +44,8 @@ import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider; import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.rds.RdsUtilities; +import software.amazon.awssdk.services.rds.TestDefaultRdsUtilities; import software.amazon.jdbc.Driver; import software.amazon.jdbc.HostSpec; import software.amazon.jdbc.HostSpecBuilder; @@ -52,6 +54,7 @@ import software.amazon.jdbc.PropertyDefinition; import software.amazon.jdbc.authentication.AwsCredentialsManager; import software.amazon.jdbc.dialect.Dialect; +import software.amazon.jdbc.hostavailability.HostAvailabilityStrategy; import software.amazon.jdbc.hostavailability.SimpleHostAvailabilityStrategy; import software.amazon.jdbc.plugin.TokenInfo; import software.amazon.jdbc.util.RdsUtils;