• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.AwsCredentials;
24 import software.amazon.awssdk.auth.credentials.CredentialUtils;
25 import software.amazon.awssdk.auth.signer.Aws4Signer;
26 import software.amazon.awssdk.auth.signer.params.Aws4PresignerParams;
27 import software.amazon.awssdk.awscore.client.config.AwsClientOption;
28 import software.amazon.awssdk.core.client.config.SdkClientConfiguration;
29 import software.amazon.awssdk.http.SdkHttpFullRequest;
30 import software.amazon.awssdk.http.SdkHttpMethod;
31 import software.amazon.awssdk.identity.spi.AwsCredentialsIdentity;
32 import software.amazon.awssdk.identity.spi.IdentityProvider;
33 import software.amazon.awssdk.regions.Region;
34 import software.amazon.awssdk.services.rds.model.GenerateAuthenticationTokenRequest;
35 import software.amazon.awssdk.utils.CompletableFutureUtils;
36 import software.amazon.awssdk.utils.Logger;
37 import software.amazon.awssdk.utils.StringUtils;
38 
39 @Immutable
40 @SdkInternalApi
41 final class DefaultRdsUtilities implements RdsUtilities {
42     private static final Logger log = Logger.loggerFor(RdsUtilities.class);
43 
44     // The time the IAM token is good for. https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.IAMDBAuth.html
45     private static final Duration EXPIRATION_DURATION = Duration.ofMinutes(15);
46 
47     private final Aws4Signer signer = Aws4Signer.create();
48     private final Region region;
49     private final IdentityProvider<? extends AwsCredentialsIdentity> credentialsProvider;
50     private final Clock clock;
51 
DefaultRdsUtilities(DefaultBuilder builder)52     DefaultRdsUtilities(DefaultBuilder builder) {
53         this(builder, Clock.systemUTC());
54     }
55 
56     /**
57      * Test Only
58      */
DefaultRdsUtilities(DefaultBuilder builder, Clock clock)59     DefaultRdsUtilities(DefaultBuilder builder, Clock clock) {
60         this.credentialsProvider = builder.credentialsProvider;
61         this.region = builder.region;
62         this.clock = clock;
63     }
64 
65     /**
66      * Used by RDS low-level client's utilities() method
67      */
68     @SdkInternalApi
create(SdkClientConfiguration clientConfiguration)69     static RdsUtilities create(SdkClientConfiguration clientConfiguration) {
70         return new DefaultBuilder().clientConfiguration(clientConfiguration).build();
71     }
72 
73     @Override
generateAuthenticationToken(GenerateAuthenticationTokenRequest request)74     public String generateAuthenticationToken(GenerateAuthenticationTokenRequest request) {
75         SdkHttpFullRequest httpRequest = SdkHttpFullRequest.builder()
76                                             .method(SdkHttpMethod.GET)
77                                             .protocol("https")
78                                             .host(request.hostname())
79                                             .port(request.port())
80                                             .encodedPath("/")
81                                             .putRawQueryParameter("DBUser", request.username())
82                                             .putRawQueryParameter("Action", "connect")
83                                             .build();
84 
85         Instant expirationTime = Instant.now(clock).plus(EXPIRATION_DURATION);
86 
87         Aws4PresignerParams presignRequest = Aws4PresignerParams.builder()
88                                                 .signingClockOverride(clock)
89                                                 .expirationTime(expirationTime)
90                                                 .awsCredentials(resolveCredentials(request))
91                                                 .signingName("rds-db")
92                                                 .signingRegion(resolveRegion(request))
93                                                 .build();
94 
95         SdkHttpFullRequest fullRequest = signer.presign(httpRequest, presignRequest);
96         String signedUrl = fullRequest.getUri().toString();
97 
98         // Format should be: <hostname>>:<port>>/?Action=connect&DBUser=<username>>&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Expi...
99         // Note: This must be the real RDS hostname, not proxy or tunnels
100         String result = StringUtils.replacePrefixIgnoreCase(signedUrl, "https://", "");
101         log.debug(() -> "Generated RDS authentication token with expiration of " + expirationTime);
102         return result;
103     }
104 
resolveRegion(GenerateAuthenticationTokenRequest request)105     private Region resolveRegion(GenerateAuthenticationTokenRequest request) {
106         if (request.region() != null) {
107             return request.region();
108         }
109 
110         if (this.region != null) {
111             return this.region;
112         }
113 
114         throw new IllegalArgumentException("Region should be provided either in GenerateAuthenticationTokenRequest object " +
115                 "or RdsUtilities object");
116     }
117 
118     // TODO: update this to use AwsCredentialsIdentity when we migrate Signers to accept the new type.
resolveCredentials(GenerateAuthenticationTokenRequest request)119     private AwsCredentials resolveCredentials(GenerateAuthenticationTokenRequest request) {
120         if (request.credentialsIdentityProvider() != null) {
121             return CredentialUtils.toCredentials(
122                 CompletableFutureUtils.joinLikeSync(request.credentialsIdentityProvider().resolveIdentity()));
123         }
124 
125         if (this.credentialsProvider != null) {
126             return CredentialUtils.toCredentials(CompletableFutureUtils.joinLikeSync(this.credentialsProvider.resolveIdentity()));
127         }
128 
129         throw new IllegalArgumentException("CredentialProvider should be provided either in GenerateAuthenticationTokenRequest " +
130                 "object or RdsUtilities object");
131     }
132 
133     @SdkInternalApi
134     static final class DefaultBuilder implements Builder {
135         private Region region;
136         private IdentityProvider<? extends AwsCredentialsIdentity> credentialsProvider;
137 
DefaultBuilder()138         DefaultBuilder() {
139         }
140 
clientConfiguration(SdkClientConfiguration clientConfiguration)141         Builder clientConfiguration(SdkClientConfiguration clientConfiguration) {
142             this.credentialsProvider = clientConfiguration.option(AwsClientOption.CREDENTIALS_IDENTITY_PROVIDER);
143             this.region = clientConfiguration.option(AwsClientOption.AWS_REGION);
144 
145             return this;
146         }
147 
148         @Override
region(Region region)149         public Builder region(Region region) {
150             this.region = region;
151             return this;
152         }
153 
154         @Override
credentialsProvider(IdentityProvider<? extends AwsCredentialsIdentity> credentialsProvider)155         public Builder credentialsProvider(IdentityProvider<? extends AwsCredentialsIdentity> credentialsProvider) {
156             this.credentialsProvider = credentialsProvider;
157             return this;
158         }
159 
160         /**
161          * Construct a {@link RdsUtilities} object.
162          */
163         @Override
build()164         public RdsUtilities build() {
165             return new DefaultRdsUtilities(this);
166         }
167     }
168 }
169