15
15
16
16
package software .amazon .awssdk .services .docdb .internal ;
17
17
18
- import static software .amazon .awssdk .auth .signer .AwsSignerExecutionAttribute .AWS_CREDENTIALS ;
19
18
import static software .amazon .awssdk .core .interceptor .SdkInternalExecutionAttribute .SELECTED_AUTH_SCHEME ;
20
19
21
20
import java .net .URI ;
22
21
import java .time .Clock ;
22
+ import java .time .Duration ;
23
+ import java .time .Instant ;
24
+ import java .time .ZoneOffset ;
25
+ import java .util .concurrent .CompletableFuture ;
23
26
import software .amazon .awssdk .annotations .SdkInternalApi ;
24
- import software .amazon .awssdk .auth .credentials .AwsCredentials ;
25
- import software .amazon .awssdk .auth .credentials .CredentialUtils ;
26
- import software .amazon .awssdk .auth .signer .Aws4Signer ;
27
- import software .amazon .awssdk .auth .signer .AwsSignerExecutionAttribute ;
28
- import software .amazon .awssdk .auth .signer .params .Aws4PresignerParams ;
29
27
import software .amazon .awssdk .awscore .AwsExecutionAttribute ;
30
28
import software .amazon .awssdk .awscore .endpoint .DefaultServiceEndpointBuilder ;
31
29
import software .amazon .awssdk .core .Protocol ;
32
30
import software .amazon .awssdk .core .SdkRequest ;
31
+ import software .amazon .awssdk .core .SelectedAuthScheme ;
33
32
import software .amazon .awssdk .core .client .config .SdkClientConfiguration ;
34
33
import software .amazon .awssdk .core .client .config .SdkClientOption ;
35
34
import software .amazon .awssdk .core .exception .SdkClientException ;
40
39
import software .amazon .awssdk .http .SdkHttpFullRequest ;
41
40
import software .amazon .awssdk .http .SdkHttpMethod ;
42
41
import software .amazon .awssdk .http .SdkHttpRequest ;
43
- import software .amazon .awssdk .identity .spi .AwsCredentialsIdentity ;
42
+ import software .amazon .awssdk .http .auth .aws .signer .AwsV4FamilyHttpSigner ;
43
+ import software .amazon .awssdk .http .auth .aws .signer .AwsV4HttpSigner ;
44
+ import software .amazon .awssdk .http .auth .spi .scheme .AuthSchemeOption ;
45
+ import software .amazon .awssdk .http .auth .spi .signer .HttpSigner ;
46
+ import software .amazon .awssdk .http .auth .spi .signer .SignRequest ;
47
+ import software .amazon .awssdk .http .auth .spi .signer .SignedRequest ;
48
+ import software .amazon .awssdk .identity .spi .Identity ;
44
49
import software .amazon .awssdk .protocols .query .AwsQueryProtocolFactory ;
45
50
import software .amazon .awssdk .regions .Region ;
46
51
import software .amazon .awssdk .services .docdb .model .DocDbRequest ;
@@ -79,49 +84,39 @@ public interface PresignableRequest {
79
84
80
85
private final Class <T > requestClassToPreSign ;
81
86
82
- private final Clock signingOverrideClock ;
87
+ private final Clock signingClockOverride ;
83
88
84
89
protected RdsPresignInterceptor (Class <T > requestClassToPreSign ) {
85
90
this (requestClassToPreSign , null );
86
91
}
87
92
88
- protected RdsPresignInterceptor (Class <T > requestClassToPreSign , Clock signingOverrideClock ) {
93
+ protected RdsPresignInterceptor (Class <T > requestClassToPreSign , Clock signingClockOverride ) {
89
94
this .requestClassToPreSign = requestClassToPreSign ;
90
- this .signingOverrideClock = signingOverrideClock ;
95
+ this .signingClockOverride = signingClockOverride ;
91
96
}
92
97
93
98
@ Override
94
99
public final SdkHttpRequest modifyHttpRequest (Context .ModifyHttpRequest context ,
95
- ExecutionAttributes executionAttributes ) {
100
+ ExecutionAttributes executionAttributes ) {
96
101
SdkHttpRequest request = context .httpRequest ();
97
- SdkRequest originalRequest = context .request ();
98
- if (!requestClassToPreSign .isInstance (originalRequest )) {
99
- return request ;
100
- }
101
-
102
- if (request .firstMatchingRawQueryParameter (PARAM_PRESIGNED_URL ).isPresent ()) {
103
- return request ;
102
+ PresignableRequest presignableRequest = toPresignableRequest (request , context );
103
+ if (presignableRequest == null ) {
104
+ return request .toBuilder ().removeQueryParameter (PARAM_SOURCE_REGION ).build ();
104
105
}
105
106
106
- PresignableRequest presignableRequest = adaptRequest (requestClassToPreSign .cast (originalRequest ));
107
-
107
+ SelectedAuthScheme <?> selectedAuthScheme = executionAttributes .getAttribute (SELECTED_AUTH_SCHEME );
108
108
String sourceRegion = presignableRequest .getSourceRegion ();
109
- if (sourceRegion == null ) {
110
- return request ;
111
- }
112
-
113
- String destinationRegion = executionAttributes .getAttribute (AwsSignerExecutionAttribute .SIGNING_REGION ).id ();
114
-
109
+ String destinationRegion = selectedAuthScheme .authSchemeOption ().signerProperty (AwsV4HttpSigner .REGION_NAME );
115
110
URI endpoint = createEndpoint (sourceRegion , SERVICE_NAME , executionAttributes );
116
111
SdkHttpFullRequest .Builder marshalledRequest = presignableRequest .marshall ().toBuilder ().uri (endpoint );
117
112
118
113
SdkHttpFullRequest requestToPresign =
119
- marshalledRequest .method (SdkHttpMethod .GET )
120
- .putRawQueryParameter (PARAM_DESTINATION_REGION , destinationRegion )
121
- .removeQueryParameter (PARAM_SOURCE_REGION )
122
- .build ();
114
+ marshalledRequest .method (SdkHttpMethod .GET )
115
+ .putRawQueryParameter (PARAM_DESTINATION_REGION , destinationRegion )
116
+ .removeQueryParameter (PARAM_SOURCE_REGION )
117
+ .build ();
123
118
124
- requestToPresign = presignRequest ( requestToPresign , executionAttributes , sourceRegion );
119
+ requestToPresign = sraPresignRequest ( executionAttributes , requestToPresign , sourceRegion );
125
120
126
121
String presignedUrl = requestToPresign .getUri ().toString ();
127
122
@@ -140,39 +135,93 @@ public final SdkHttpRequest modifyHttpRequest(Context.ModifyHttpRequest context,
140
135
*/
141
136
protected abstract PresignableRequest adaptRequest (T originalRequest );
142
137
143
- private SdkHttpFullRequest presignRequest (SdkHttpFullRequest request ,
144
- ExecutionAttributes attributes ,
145
- String signingRegion ) {
138
+ /**
139
+ * Converts the request to a PresignableRequest if possible.
140
+ */
141
+ private PresignableRequest toPresignableRequest (SdkHttpRequest request , Context .ModifyHttpRequest context ) {
142
+ SdkRequest originalRequest = context .request ();
143
+ if (!requestClassToPreSign .isInstance (originalRequest )) {
144
+ return null ;
145
+ }
146
+ if (request .firstMatchingRawQueryParameter (PARAM_PRESIGNED_URL ).isPresent ()) {
147
+ return null ;
148
+ }
149
+ PresignableRequest presignableRequest = adaptRequest (requestClassToPreSign .cast (originalRequest ));
150
+ String sourceRegion = presignableRequest .getSourceRegion ();
151
+ if (sourceRegion == null ) {
152
+ return null ;
153
+ }
154
+ return presignableRequest ;
155
+ }
156
+
157
+ /**
158
+ * Presign the provided HTTP request using SRA HttpSigner
159
+ */
160
+ private SdkHttpFullRequest sraPresignRequest (ExecutionAttributes executionAttributes , SdkHttpFullRequest request ,
161
+ String signingRegion ) {
162
+ SelectedAuthScheme <?> selectedAuthScheme = executionAttributes .getAttribute (SELECTED_AUTH_SCHEME );
146
163
147
- Aws4Signer signer = Aws4Signer .create ();
148
- Aws4PresignerParams presignerParams = Aws4PresignerParams .builder ()
149
- .signingRegion (Region .of (signingRegion ))
150
- .signingName (SERVICE_NAME )
151
- .signingClockOverride (signingOverrideClock )
152
- .awsCredentials (resolveCredentials (attributes ))
153
- .build ();
154
164
155
- return signer .presign (request , presignerParams );
165
+ Instant signingInstant ;
166
+ if (signingClockOverride != null ) {
167
+ signingInstant = signingClockOverride .instant ();
168
+ } else {
169
+ signingInstant = Instant .now ();
170
+ }
171
+ // A fixed signing clock is used so that the current time used by the signing logic, as well as to
172
+ // determine expiration are the same.
173
+ Clock signingClock = Clock .fixed (signingInstant , ZoneOffset .UTC );
174
+ Duration expirationDuration = Duration .ofDays (7 );
175
+ return doSraPresign (request , selectedAuthScheme , signingRegion , signingClock , expirationDuration );
176
+ }
177
+
178
+ private <T extends Identity > SdkHttpFullRequest doSraPresign (SdkHttpFullRequest request ,
179
+ SelectedAuthScheme <T > selectedAuthScheme ,
180
+ String signingRegion ,
181
+ Clock signingClock ,
182
+ Duration expirationDuration ) {
183
+ CompletableFuture <? extends T > identityFuture = selectedAuthScheme .identity ();
184
+ T identity = CompletableFutureUtils .joinLikeSync (identityFuture );
185
+
186
+ // Pre-signed URL puts auth info in query string, does not sign the payload, and has an expiry.
187
+ SignRequest .Builder <T > signRequestBuilder = SignRequest
188
+ .builder (identity )
189
+ .putProperty (AwsV4FamilyHttpSigner .AUTH_LOCATION , AwsV4FamilyHttpSigner .AuthLocation .QUERY_STRING )
190
+ .putProperty (AwsV4FamilyHttpSigner .EXPIRATION_DURATION , expirationDuration )
191
+ .putProperty (HttpSigner .SIGNING_CLOCK , signingClock )
192
+ .request (request )
193
+ .payload (request .contentStreamProvider ().orElse (null ));
194
+ AuthSchemeOption authSchemeOption = selectedAuthScheme .authSchemeOption ();
195
+ authSchemeOption .forEachSignerProperty (signRequestBuilder ::putProperty );
196
+ // Override the region
197
+ signRequestBuilder .putProperty (AwsV4HttpSigner .REGION_NAME , signingRegion );
198
+ HttpSigner <T > signer = selectedAuthScheme .signer ();
199
+ SignedRequest signedRequest = signer .sign (signRequestBuilder .build ());
200
+ return toSdkHttpFullRequest (signedRequest );
156
201
}
157
202
158
- private AwsCredentials resolveCredentials (ExecutionAttributes attributes ) {
159
- return attributes .getOptionalAttribute (SELECTED_AUTH_SCHEME )
160
- .map (selectedAuthScheme -> selectedAuthScheme .identity ())
161
- .map (identityFuture -> CompletableFutureUtils .joinLikeSync (identityFuture ))
162
- .filter (identity -> identity instanceof AwsCredentialsIdentity )
163
- .map (identity -> {
164
- AwsCredentialsIdentity awsCredentialsIdentity = (AwsCredentialsIdentity ) identity ;
165
- return CredentialUtils .toCredentials (awsCredentialsIdentity );
166
- }).orElse (attributes .getAttribute (AWS_CREDENTIALS ));
203
+ private SdkHttpFullRequest toSdkHttpFullRequest (SignedRequest signedRequest ) {
204
+ SdkHttpRequest request = signedRequest .request ();
205
+
206
+ return SdkHttpFullRequest .builder ()
207
+ .contentStreamProvider (signedRequest .payload ().orElse (null ))
208
+ .protocol (request .protocol ())
209
+ .method (request .method ())
210
+ .host (request .host ())
211
+ .port (request .port ())
212
+ .encodedPath (request .encodedPath ())
213
+ .applyMutation (r -> request .forEachHeader (r ::putHeader ))
214
+ .applyMutation (r -> request .forEachRawQueryParameter (r ::putRawQueryParameter ))
215
+ .removeQueryParameter (PARAM_SOURCE_REGION )
216
+ .build ();
167
217
}
168
218
169
219
private URI createEndpoint (String regionName , String serviceName , ExecutionAttributes attributes ) {
170
220
Region region = Region .of (regionName );
171
-
172
221
if (region == null ) {
173
222
throw SdkClientException .builder ()
174
223
.message ("{" + serviceName + ", " + regionName + "} was not "
175
- + "found in region metadata. Update to latest version of SDK and try again." )
224
+ + "found in region metadata. Update to latest version of SDK and try again." )
176
225
.build ();
177
226
}
178
227
0 commit comments