Skip to content

Commit caab8b9

Browse files
Austin Brookszoewangg
Austin Brooks
authored andcommitted
Add a RdsUtilities class with the ability to generate an IAM auth token
1 parent b0bdb2d commit caab8b9

File tree

10 files changed

+623
-3
lines changed

10 files changed

+623
-3
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"type": "feature",
3+
"category": "AWS RDS",
4+
"contributor": "abrooksv",
5+
"description": "Add the ability to generate IAM auth tokens for RDS using `RdsUtilities`"
6+
}

codegen/src/main/java/software/amazon/awssdk/codegen/model/config/customization/UtilitiesMethod.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ public class UtilitiesMethod {
2929
/** Fqcn of the return type of the operation */
3030
private String returnType;
3131

32+
/** Fqcn of the instance type to be created */
33+
private String instanceType;
34+
3235
/**
3336
* The utilities method will call a protected create() method in the hand-written Utilities class.
3437
* These the ordered list of parameters that needs to be passed to the create method.
@@ -50,4 +53,12 @@ public List<String> getCreateMethodParams() {
5053
public void setCreateMethodParams(List<String> createMethodParams) {
5154
this.createMethodParams = createMethodParams;
5255
}
56+
57+
public String getInstanceType() {
58+
return instanceType;
59+
}
60+
61+
public void setInstanceType(String instanceType) {
62+
this.instanceType = instanceType;
63+
}
5364
}

codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/AsyncClientClass.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -383,13 +383,19 @@ private TypeName eventStreamType(ShapeModel shapeModel) {
383383
private MethodSpec utilitiesMethod() {
384384
UtilitiesMethod config = model.getCustomizationConfig().getUtilitiesMethod();
385385
ClassName returnType = PoetUtils.classNameFromFqcn(config.getReturnType());
386+
String instanceClass = config.getInstanceType();
387+
if (instanceClass == null) {
388+
instanceClass = config.getReturnType();
389+
}
390+
391+
ClassName instanceType = PoetUtils.classNameFromFqcn(instanceClass);
386392

387393
return MethodSpec.methodBuilder(UtilitiesMethod.METHOD_NAME)
388394
.returns(returnType)
389395
.addModifiers(Modifier.PUBLIC)
390396
.addAnnotation(Override.class)
391397
.addStatement("return $T.create($L)",
392-
returnType,
398+
instanceType,
393399
String.join(",", config.getCreateMethodParams()))
394400
.build();
395401
}

codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/SyncClientClass.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -310,13 +310,19 @@ private MethodSpec closeMethod() {
310310
private MethodSpec utilitiesMethod() {
311311
UtilitiesMethod config = model.getCustomizationConfig().getUtilitiesMethod();
312312
ClassName returnType = PoetUtils.classNameFromFqcn(config.getReturnType());
313+
String instanceClass = config.getInstanceType();
314+
if (instanceClass == null) {
315+
instanceClass = config.getReturnType();
316+
}
317+
318+
ClassName instanceType = PoetUtils.classNameFromFqcn(instanceClass);
313319

314320
return MethodSpec.methodBuilder(UtilitiesMethod.METHOD_NAME)
315321
.returns(returnType)
316322
.addModifiers(Modifier.PUBLIC)
317323
.addAnnotation(Override.class)
318324
.addStatement("return $T.create($L)",
319-
returnType,
325+
instanceType,
320326
String.join(",", config.getCreateMethodParams()))
321327
.build();
322328
}

docs/LaunchChangelog.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,10 @@ The S3 client in 2.0 is drastically different from the client in 1.11, because i
449449

450450
1. An SQS client may no longer access SQS queues in regions different than the one with which the client was configured.
451451

452+
## 4.4. RDS Changes
453+
454+
1. The class`RdsIamAuthTokenGenerator` has been replaced with `RdsUtilities#generateAuthenticationToken`.
455+
452456
# 5. Profile File Changes
453457

454458
The parsing of the `~/.aws/config` and `~/.aws/credentials` has changed to more closely emulate that used by the AWS CLI.
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package software.amazon.awssdk.services.rds;
17+
18+
import java.time.Clock;
19+
import java.time.Duration;
20+
import java.time.Instant;
21+
import software.amazon.awssdk.annotations.Immutable;
22+
import software.amazon.awssdk.annotations.SdkInternalApi;
23+
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
24+
import software.amazon.awssdk.auth.signer.Aws4Signer;
25+
import software.amazon.awssdk.auth.signer.params.Aws4PresignerParams;
26+
import software.amazon.awssdk.awscore.client.config.AwsClientOption;
27+
import software.amazon.awssdk.core.client.config.SdkClientConfiguration;
28+
import software.amazon.awssdk.http.SdkHttpFullRequest;
29+
import software.amazon.awssdk.http.SdkHttpMethod;
30+
import software.amazon.awssdk.regions.Region;
31+
import software.amazon.awssdk.services.rds.model.GenerateAuthenticationTokenRequest;
32+
import software.amazon.awssdk.utils.StringUtils;
33+
34+
@Immutable
35+
@SdkInternalApi
36+
final class DefaultRdsUtilities implements RdsUtilities {
37+
// The time the IAM token is good for. https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.IAMDBAuth.html
38+
private static final Duration EXPIRATION_DURATION = Duration.ofMinutes(15);
39+
40+
private final Aws4Signer signer = Aws4Signer.create();
41+
private final Region region;
42+
private final AwsCredentialsProvider credentialsProvider;
43+
private final Clock clock;
44+
45+
DefaultRdsUtilities(DefaultBuilder builder) {
46+
this(builder, Clock.systemUTC());
47+
}
48+
49+
/**
50+
* Test Only
51+
*/
52+
DefaultRdsUtilities(DefaultBuilder builder, Clock clock) {
53+
this.credentialsProvider = builder.credentialsProvider;
54+
this.region = builder.region;
55+
this.clock = clock;
56+
}
57+
58+
/**
59+
* Used by RDS low-level client's utilities() method
60+
*/
61+
@SdkInternalApi
62+
static RdsUtilities create(SdkClientConfiguration clientConfiguration) {
63+
return new DefaultBuilder().clientConfiguration(clientConfiguration).build();
64+
}
65+
66+
@Override
67+
public String generateAuthenticationToken(GenerateAuthenticationTokenRequest request) {
68+
SdkHttpFullRequest httpRequest = SdkHttpFullRequest.builder()
69+
.method(SdkHttpMethod.GET)
70+
.protocol("https")
71+
.host(request.hostname())
72+
.port(request.port())
73+
.encodedPath("/")
74+
.putRawQueryParameter("DBUser", request.username())
75+
.putRawQueryParameter("Action", "connect")
76+
.build();
77+
78+
Instant expirationTime = Instant.now(clock).plus(EXPIRATION_DURATION);
79+
Aws4PresignerParams presignRequest = Aws4PresignerParams.builder()
80+
.signingClockOverride(clock)
81+
.expirationTime(expirationTime)
82+
.awsCredentials(resolveCredentials(request).resolveCredentials())
83+
.signingName("rds-db")
84+
.signingRegion(resolveRegion(request))
85+
.build();
86+
87+
SdkHttpFullRequest fullRequest = signer.presign(httpRequest, presignRequest);
88+
String signedUrl = fullRequest.getUri().toString();
89+
90+
// Format should be: <hostname>>:<port>>/?Action=connect&DBUser=<username>>&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Expi...
91+
// Note: This must be the real RDS hostname, not proxy or tunnels
92+
return StringUtils.replacePrefixIgnoreCase(signedUrl, "https://", "");
93+
}
94+
95+
private Region resolveRegion(GenerateAuthenticationTokenRequest request) {
96+
if (request.region() != null) {
97+
return request.region();
98+
}
99+
100+
if (this.region != null) {
101+
return this.region;
102+
}
103+
104+
throw new IllegalArgumentException("Region should be provided either in GenerateAuthenticationTokenRequest object " +
105+
"or RdsUtilities object");
106+
}
107+
108+
private AwsCredentialsProvider resolveCredentials(GenerateAuthenticationTokenRequest request) {
109+
if (request.credentialsProvider() != null) {
110+
return request.credentialsProvider();
111+
}
112+
113+
if (this.credentialsProvider != null) {
114+
return this.credentialsProvider;
115+
}
116+
117+
throw new IllegalArgumentException("CredentialProvider should be provided either in GenerateAuthenticationTokenRequest " +
118+
"object or RdsUtilities object");
119+
}
120+
121+
@SdkInternalApi
122+
static final class DefaultBuilder implements Builder {
123+
private Region region;
124+
private AwsCredentialsProvider credentialsProvider;
125+
126+
DefaultBuilder() {
127+
}
128+
129+
Builder clientConfiguration(SdkClientConfiguration clientConfiguration) {
130+
this.credentialsProvider = clientConfiguration.option(AwsClientOption.CREDENTIALS_PROVIDER);
131+
this.region = clientConfiguration.option(AwsClientOption.AWS_REGION);
132+
133+
return this;
134+
}
135+
136+
@Override
137+
public Builder region(Region region) {
138+
this.region = region;
139+
return this;
140+
}
141+
142+
@Override
143+
public Builder credentialsProvider(AwsCredentialsProvider credentialsProvider) {
144+
this.credentialsProvider = credentialsProvider;
145+
return this;
146+
}
147+
148+
/**
149+
* Construct a {@link RdsUtilities} object.
150+
*/
151+
@Override
152+
public RdsUtilities build() {
153+
return new DefaultRdsUtilities(this);
154+
}
155+
}
156+
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package software.amazon.awssdk.services.rds;
17+
18+
import java.util.function.Consumer;
19+
import software.amazon.awssdk.annotations.SdkPublicApi;
20+
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
21+
import software.amazon.awssdk.regions.Region;
22+
import software.amazon.awssdk.services.rds.model.GenerateAuthenticationTokenRequest;
23+
24+
/**
25+
* Utilities for working with RDS. An instance of this class can be created by:
26+
* <p>
27+
* 1) Using the low-level client {@link RdsClient#utilities()} (or {@link RdsAsyncClient#utilities()}} method. This is
28+
* recommended as SDK will use the same configuration from the {@link RdsClient} object to create the {@link RdsUtilities} object.
29+
*
30+
* <pre>
31+
* RdsClient rdsClient = RdsClient.create();
32+
* RdsUtilities utilities = rdsClient.utilities();
33+
* </pre>
34+
* </p>
35+
*
36+
* <p>
37+
* 2) Directly using the {@link #builder()} method.
38+
*
39+
* <pre>
40+
* RdsUtilities utilities = RdsUtilities.builder()
41+
* .credentialsProvider(DefaultCredentialsProvider.create())
42+
* .region(Region.US_WEST_2)
43+
* .build()
44+
* </pre>
45+
* </p>
46+
*
47+
* Note: This class does not make network calls.
48+
*/
49+
@SdkPublicApi
50+
public interface RdsUtilities {
51+
/**
52+
* Create a builder that can be used to configure and create a {@link RdsUtilities}.
53+
*/
54+
static Builder builder() {
55+
return new DefaultRdsUtilities.DefaultBuilder();
56+
}
57+
58+
/**
59+
* Generates an authorization tokens for IAM authentication to an RDS database.
60+
*
61+
* @param request The request used to generate the auth token
62+
* @return String to use as the RDS auth token
63+
* @throws IllegalArgumentException if the required parameters are not valid
64+
*/
65+
default String generateAuthenticationToken(Consumer<GenerateAuthenticationTokenRequest.Builder> request) {
66+
return generateAuthenticationToken(GenerateAuthenticationTokenRequest.builder().applyMutation(request).build());
67+
}
68+
69+
/**
70+
* Generates an authorization tokens for IAM authentication to an RDS database.
71+
*
72+
* @param request The request used to generate the auth token
73+
* @return String to use as the RDS auth token
74+
* @throws IllegalArgumentException if the required parameters are not valid
75+
*/
76+
default String generateAuthenticationToken(GenerateAuthenticationTokenRequest request) {
77+
RdsUtilities.builder().region(Region.US_WEST_2).build();
78+
throw new UnsupportedOperationException();
79+
}
80+
81+
/**
82+
* Builder for creating an instance of {@link RdsUtilities}. It can be configured using {@link RdsUtilities#builder()}.
83+
* Once configured, the {@link RdsUtilities} can created using {@link #build()}.
84+
*/
85+
@SdkPublicApi
86+
interface Builder {
87+
/**
88+
* The default region to use when working with the methods in {@link RdsUtilities} class.
89+
*
90+
* @return This object for method chaining
91+
*/
92+
Builder region(Region region);
93+
94+
/**
95+
* The default credentials provider to use when working with the methods in {@link RdsUtilities} class.
96+
*
97+
* @return This object for method chaining
98+
*/
99+
Builder credentialsProvider(AwsCredentialsProvider credentialsProvider);
100+
101+
/**
102+
* Create a {@link RdsUtilities}
103+
*/
104+
RdsUtilities build();
105+
}
106+
}

0 commit comments

Comments
 (0)