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.auth.signer; 17 18 import static software.amazon.awssdk.utils.CompletableFutureUtils.joinLikeSync; 19 20 import java.time.Clock; 21 import java.time.Instant; 22 import java.util.concurrent.CompletableFuture; 23 import software.amazon.awssdk.annotations.SdkProtectedApi; 24 import software.amazon.awssdk.auth.credentials.AwsCredentials; 25 import software.amazon.awssdk.auth.credentials.CredentialUtils; 26 import software.amazon.awssdk.auth.signer.params.Aws4SignerParams; 27 import software.amazon.awssdk.core.SelectedAuthScheme; 28 import software.amazon.awssdk.core.interceptor.ExecutionAttribute; 29 import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; 30 import software.amazon.awssdk.core.interceptor.SdkExecutionAttribute; 31 import software.amazon.awssdk.core.interceptor.SdkInternalExecutionAttribute; 32 import software.amazon.awssdk.core.signer.Signer; 33 import software.amazon.awssdk.http.auth.aws.scheme.AwsV4AuthScheme; 34 import software.amazon.awssdk.http.auth.aws.signer.AwsV4FamilyHttpSigner; 35 import software.amazon.awssdk.http.auth.aws.signer.AwsV4HttpSigner; 36 import software.amazon.awssdk.http.auth.aws.signer.AwsV4aHttpSigner; 37 import software.amazon.awssdk.http.auth.aws.signer.RegionSet; 38 import software.amazon.awssdk.http.auth.spi.scheme.AuthSchemeOption; 39 import software.amazon.awssdk.http.auth.spi.signer.AsyncSignRequest; 40 import software.amazon.awssdk.http.auth.spi.signer.AsyncSignedRequest; 41 import software.amazon.awssdk.http.auth.spi.signer.HttpSigner; 42 import software.amazon.awssdk.http.auth.spi.signer.SignRequest; 43 import software.amazon.awssdk.http.auth.spi.signer.SignedRequest; 44 import software.amazon.awssdk.identity.spi.AwsCredentialsIdentity; 45 import software.amazon.awssdk.identity.spi.Identity; 46 import software.amazon.awssdk.regions.Region; 47 import software.amazon.awssdk.regions.RegionScope; 48 import software.amazon.awssdk.utils.CompletableFutureUtils; 49 50 /** 51 * AWS-specific signing attributes attached to the execution. This information is available to {@link ExecutionInterceptor}s and 52 * {@link Signer}s. 53 * 54 * @deprecated Signer execution attributes have been deprecated in favor of signer properties, set on the auth scheme's signer 55 * option. 56 */ 57 @Deprecated 58 @SdkProtectedApi 59 public final class AwsSignerExecutionAttribute extends SdkExecutionAttribute { 60 /** 61 * The key under which the request credentials are set. 62 * 63 * @deprecated This is a protected class that is internal to the SDK, so you shouldn't be using it. If you are using it 64 * from execution interceptors, you should instead be overriding the credential provider via the {@code SdkRequest}'s 65 * {@code overrideConfiguration.credentialsProvider}. If you're using it to call the SDK's signers, you should migrate to a 66 * subtype of {@code HttpSigner}. 67 */ 68 @Deprecated 69 public static final ExecutionAttribute<AwsCredentials> AWS_CREDENTIALS = 70 ExecutionAttribute.derivedBuilder("AwsCredentials", 71 AwsCredentials.class, 72 SdkInternalExecutionAttribute.SELECTED_AUTH_SCHEME) 73 .readMapping(AwsSignerExecutionAttribute::awsCredentialsReadMapping) 74 .writeMapping(AwsSignerExecutionAttribute::awsCredentialsWriteMapping) 75 .build(); 76 77 /** 78 * The AWS {@link Region} that is used for signing a request. This is not always same as the region configured on the client 79 * for global services like IAM. 80 * 81 * @deprecated This is a protected class that is internal to the SDK, so you shouldn't be using it. If you are using it 82 * from execution interceptors, you should instead be overriding the signing region via the {@code AuthSchemeProvider} that 83 * is configured on the SDK client builder. If you're using it to call the SDK's signers, you should migrate to a 84 * subtype of {@code HttpSigner}. 85 */ 86 @Deprecated 87 public static final ExecutionAttribute<Region> SIGNING_REGION = 88 ExecutionAttribute.derivedBuilder("SigningRegion", 89 Region.class, 90 SdkInternalExecutionAttribute.SELECTED_AUTH_SCHEME) 91 .readMapping(AwsSignerExecutionAttribute::signingRegionReadMapping) 92 .writeMapping(AwsSignerExecutionAttribute::signingRegionWriteMapping) 93 .build(); 94 95 /** 96 * The AWS {@link Region} that is used for signing a request. This is not always same as the region configured on the client 97 * for global services like IAM. 98 * 99 * @deprecated This is a protected class that is internal to the SDK, so you shouldn't be using it. If you are using it 100 * from execution interceptors, you should instead be overriding the signing region scope via the {@code AuthSchemeProvider} 101 * that is configured on the SDK client builder. If you're using it to call the SDK's signers, you should migrate to a 102 * subtype of {@code HttpSigner}. 103 */ 104 @Deprecated 105 public static final ExecutionAttribute<RegionScope> SIGNING_REGION_SCOPE = 106 ExecutionAttribute.derivedBuilder("SigningRegionScope", 107 RegionScope.class, 108 SdkInternalExecutionAttribute.SELECTED_AUTH_SCHEME) 109 .readMapping(AwsSignerExecutionAttribute::signingRegionScopeReadMapping) 110 .writeMapping(AwsSignerExecutionAttribute::signingRegionScopeWriteMapping) 111 .build(); 112 113 /** 114 * The signing name of the service to be using in SigV4 signing 115 * 116 * @deprecated This is a protected class that is internal to the SDK, so you shouldn't be using it. If you are using it 117 * from execution interceptors, you should instead be overriding the signing region name via the {@code AuthSchemeProvider} 118 * that is configured on the SDK client builder. If you're using it to call the SDK's signers, you should migrate to a 119 * subtype of {@code HttpSigner}. 120 */ 121 @Deprecated 122 public static final ExecutionAttribute<String> SERVICE_SIGNING_NAME = 123 ExecutionAttribute.derivedBuilder("ServiceSigningName", 124 String.class, 125 SdkInternalExecutionAttribute.SELECTED_AUTH_SCHEME) 126 .readMapping(AwsSignerExecutionAttribute::serviceSigningNameReadMapping) 127 .writeMapping(AwsSignerExecutionAttribute::serviceSigningNameWriteMapping) 128 .build(); 129 130 /** 131 * The key to specify whether to use double url encoding during signing. 132 * 133 * @deprecated This is a protected class that is internal to the SDK, so you shouldn't be using it. If you are using it 134 * from execution interceptors, you should instead be overriding the double-url-encode setting via the {@code 135 * AuthSchemeProvider} that is configured on the SDK client builder. If you're using it to call the SDK's signers, you 136 * should migrate to a subtype of {@code HttpSigner}. 137 */ 138 @Deprecated 139 public static final ExecutionAttribute<Boolean> SIGNER_DOUBLE_URL_ENCODE = 140 ExecutionAttribute.derivedBuilder("DoubleUrlEncode", 141 Boolean.class, 142 SdkInternalExecutionAttribute.SELECTED_AUTH_SCHEME) 143 .readMapping(AwsSignerExecutionAttribute::signerDoubleUrlEncodeReadMapping) 144 .writeMapping(AwsSignerExecutionAttribute::signerDoubleUrlEncodeWriteMapping) 145 .build(); 146 147 /** 148 * The key to specify whether to normalize the resource path during signing. 149 * 150 * @deprecated This is a protected class that is internal to the SDK, so you shouldn't be using it. If you are using it 151 * from execution interceptors, you should instead be overriding the normalize-path setting via the {@code 152 * AuthSchemeProvider} that is configured on the SDK client builder. If you're using it to call the SDK's signers, you 153 * should migrate to a subtype of {@code HttpSigner}. 154 */ 155 @Deprecated 156 public static final ExecutionAttribute<Boolean> SIGNER_NORMALIZE_PATH = 157 ExecutionAttribute.derivedBuilder("NormalizePath", 158 Boolean.class, 159 SdkInternalExecutionAttribute.SELECTED_AUTH_SCHEME) 160 .readMapping(AwsSignerExecutionAttribute::signerNormalizePathReadMapping) 161 .writeMapping(AwsSignerExecutionAttribute::signerNormalizePathWriteMapping) 162 .build(); 163 164 165 /** 166 * An override clock to use during signing. 167 * @see Aws4SignerParams.Builder#signingClockOverride(Clock) 168 * 169 * @deprecated This is a protected class that is internal to the SDK, so you shouldn't be using it. If you are using it 170 * from execution interceptors, you should instead be overriding the clock setting via the {@code 171 * AuthSchemeProvider} that is configured on the SDK client builder. If you're using it to call the SDK's signers, you 172 * should migrate to a subtype of {@code HttpSigner}. 173 */ 174 @Deprecated 175 public static final ExecutionAttribute<Clock> SIGNING_CLOCK = 176 ExecutionAttribute.derivedBuilder("Clock", 177 Clock.class, 178 SdkInternalExecutionAttribute.SELECTED_AUTH_SCHEME) 179 .readMapping(AwsSignerExecutionAttribute::signingClockReadMapping) 180 .writeMapping(AwsSignerExecutionAttribute::signingClockWriteMapping) 181 .build(); 182 183 /** 184 * The key to specify the expiration time when pre-signing aws requests. 185 * 186 * @deprecated This is a protected class that is internal to the SDK, so you shouldn't be using it. If you are using it 187 * from execution interceptors, you should instead be overriding the expiration via the {@code AuthSchemeProvider} that is 188 * configured on the SDK client builder. If you're using it to call the SDK's signers, you should migrate to a subtype of 189 * {@code HttpSigner}. 190 */ 191 @Deprecated 192 public static final ExecutionAttribute<Instant> PRESIGNER_EXPIRATION = new ExecutionAttribute("PresignerExpiration"); 193 AwsSignerExecutionAttribute()194 private AwsSignerExecutionAttribute() { 195 } 196 awsCredentialsReadMapping(SelectedAuthScheme<?> authScheme)197 private static AwsCredentials awsCredentialsReadMapping(SelectedAuthScheme<?> authScheme) { 198 if (authScheme == null) { 199 return null; 200 } 201 Identity identity = joinLikeSync(authScheme.identity()); 202 if (!(identity instanceof AwsCredentialsIdentity)) { 203 return null; 204 } 205 return CredentialUtils.toCredentials((AwsCredentialsIdentity) identity); 206 } 207 awsCredentialsWriteMapping(SelectedAuthScheme<T> authScheme, AwsCredentials awsCredentials)208 private static <T extends Identity> SelectedAuthScheme<?> awsCredentialsWriteMapping(SelectedAuthScheme<T> authScheme, 209 AwsCredentials awsCredentials) { 210 if (authScheme == null) { 211 // This is an unusual use-case. 212 // Let's assume they're setting the credentials so that they can call the signer directly. If that's true, then it 213 // doesn't really matter what we store other than the credentials. 214 return new SelectedAuthScheme<>(CompletableFuture.completedFuture(awsCredentials), 215 AwsV4HttpSigner.create(), 216 AuthSchemeOption.builder() 217 .schemeId(AwsV4AuthScheme.SCHEME_ID) 218 .build()); 219 } 220 221 return new SelectedAuthScheme<>(CompletableFuture.completedFuture((T) awsCredentials), 222 authScheme.signer(), 223 authScheme.authSchemeOption()); 224 } 225 signingRegionReadMapping(SelectedAuthScheme<?> authScheme)226 private static Region signingRegionReadMapping(SelectedAuthScheme<?> authScheme) { 227 if (authScheme == null) { 228 return null; 229 } 230 String regionName = authScheme.authSchemeOption().signerProperty(AwsV4HttpSigner.REGION_NAME); 231 if (regionName == null) { 232 return null; 233 } 234 return Region.of(regionName); 235 } 236 signingRegionWriteMapping(SelectedAuthScheme<T> authScheme, Region region)237 private static <T extends Identity> SelectedAuthScheme<?> signingRegionWriteMapping(SelectedAuthScheme<T> authScheme, 238 Region region) { 239 String regionString = region == null ? null : region.id(); 240 241 if (authScheme == null) { 242 // This is an unusual use-case. 243 // Let's assume they're setting the region so that they can call the signer directly. If that's true, then it 244 // doesn't really matter what we store other than the region. 245 return new SelectedAuthScheme<>(CompletableFuture.completedFuture(new UnsetIdentity()), 246 new UnsetHttpSigner(), 247 AuthSchemeOption.builder() 248 .schemeId("unset") 249 .putSignerProperty(AwsV4HttpSigner.REGION_NAME, 250 regionString) 251 .build()); 252 } 253 254 return new SelectedAuthScheme<>(authScheme.identity(), 255 authScheme.signer(), 256 authScheme.authSchemeOption().copy(o -> o.putSignerProperty(AwsV4HttpSigner.REGION_NAME, 257 regionString))); 258 } 259 signingRegionScopeReadMapping(SelectedAuthScheme<?> authScheme)260 private static RegionScope signingRegionScopeReadMapping(SelectedAuthScheme<?> authScheme) { 261 if (authScheme == null) { 262 return null; 263 } 264 RegionSet regionSet = authScheme.authSchemeOption().signerProperty(AwsV4aHttpSigner.REGION_SET); 265 if (regionSet == null || regionSet.asString().isEmpty()) { 266 return null; 267 } 268 269 return RegionScope.create(regionSet.asString()); 270 } 271 signingRegionScopeWriteMapping(SelectedAuthScheme<T> authScheme, RegionScope regionScope)272 private static <T extends Identity> SelectedAuthScheme<?> signingRegionScopeWriteMapping(SelectedAuthScheme<T> authScheme, 273 RegionScope regionScope) { 274 RegionSet regionSet = regionScope != null ? RegionSet.create(regionScope.id()) : null; 275 276 if (authScheme == null) { 277 // This is an unusual use-case. 278 // Let's assume they're setting the region scope so that they can call the signer directly. If that's true, then it 279 // doesn't really matter what we store other than the region scope. 280 return new SelectedAuthScheme<>(CompletableFuture.completedFuture(new UnsetIdentity()), 281 new UnsetHttpSigner(), 282 AuthSchemeOption.builder() 283 .schemeId("unset") 284 .putSignerProperty(AwsV4aHttpSigner.REGION_SET, regionSet) 285 .build()); 286 } 287 288 return new SelectedAuthScheme<>(authScheme.identity(), 289 authScheme.signer(), 290 authScheme.authSchemeOption().copy(o -> o.putSignerProperty(AwsV4aHttpSigner.REGION_SET, 291 regionSet))); 292 } 293 serviceSigningNameReadMapping(SelectedAuthScheme<?> authScheme)294 private static String serviceSigningNameReadMapping(SelectedAuthScheme<?> authScheme) { 295 if (authScheme == null) { 296 return null; 297 } 298 return authScheme.authSchemeOption().signerProperty(AwsV4FamilyHttpSigner.SERVICE_SIGNING_NAME); 299 } 300 serviceSigningNameWriteMapping(SelectedAuthScheme<T> authScheme, String signingName)301 private static <T extends Identity> SelectedAuthScheme<?> serviceSigningNameWriteMapping(SelectedAuthScheme<T> authScheme, 302 String signingName) { 303 if (authScheme == null) { 304 // This is an unusual use-case. 305 // Let's assume they're setting the signing name so that they can call the signer directly. If that's true, then it 306 // doesn't really matter what we store other than the signing name. 307 return new SelectedAuthScheme<>(CompletableFuture.completedFuture(new UnsetIdentity()), 308 new UnsetHttpSigner(), 309 AuthSchemeOption.builder() 310 .schemeId("unset") 311 .putSignerProperty(AwsV4FamilyHttpSigner.SERVICE_SIGNING_NAME, 312 signingName) 313 .build()); 314 } 315 316 return new SelectedAuthScheme<>(authScheme.identity(), 317 authScheme.signer(), 318 authScheme.authSchemeOption() 319 .copy(o -> o.putSignerProperty(AwsV4FamilyHttpSigner.SERVICE_SIGNING_NAME, 320 signingName))); 321 } 322 signerDoubleUrlEncodeReadMapping(SelectedAuthScheme<?> authScheme)323 private static Boolean signerDoubleUrlEncodeReadMapping(SelectedAuthScheme<?> authScheme) { 324 if (authScheme == null) { 325 return null; 326 } 327 AuthSchemeOption authOption = authScheme.authSchemeOption(); 328 return authOption.signerProperty(AwsV4FamilyHttpSigner.DOUBLE_URL_ENCODE); 329 } 330 signerDoubleUrlEncodeWriteMapping(SelectedAuthScheme<T> authScheme, Boolean doubleUrlEncode)331 private static <T extends Identity> SelectedAuthScheme<?> signerDoubleUrlEncodeWriteMapping(SelectedAuthScheme<T> authScheme, 332 Boolean doubleUrlEncode) { 333 if (authScheme == null) { 334 // This is an unusual use-case. 335 // Let's assume they're setting double-url-encode so that they can call the signer directly. If that's true, then it 336 // doesn't really matter what we store other than double-url-encode. 337 return new SelectedAuthScheme<>(CompletableFuture.completedFuture(new UnsetIdentity()), 338 new UnsetHttpSigner(), 339 AuthSchemeOption.builder() 340 .schemeId("unset") 341 .putSignerProperty(AwsV4FamilyHttpSigner.DOUBLE_URL_ENCODE, 342 doubleUrlEncode) 343 .build()); 344 } 345 346 return new SelectedAuthScheme<>(authScheme.identity(), 347 authScheme.signer(), 348 authScheme.authSchemeOption() 349 .copy(o -> o.putSignerProperty(AwsV4FamilyHttpSigner.DOUBLE_URL_ENCODE, 350 doubleUrlEncode))); 351 } 352 signerNormalizePathReadMapping(SelectedAuthScheme<?> authScheme)353 private static Boolean signerNormalizePathReadMapping(SelectedAuthScheme<?> authScheme) { 354 if (authScheme == null) { 355 return null; 356 } 357 AuthSchemeOption authOption = authScheme.authSchemeOption(); 358 return authOption.signerProperty(AwsV4FamilyHttpSigner.NORMALIZE_PATH); 359 } 360 signerNormalizePathWriteMapping(SelectedAuthScheme<T> authScheme, Boolean normalizePath)361 private static <T extends Identity> SelectedAuthScheme<?> signerNormalizePathWriteMapping(SelectedAuthScheme<T> authScheme, 362 Boolean normalizePath) { 363 if (authScheme == null) { 364 // This is an unusual use-case. 365 // Let's assume they're setting normalize-path so that they can call the signer directly. If that's true, then it 366 // doesn't really matter what we store other than normalize-path. 367 return new SelectedAuthScheme<>(CompletableFuture.completedFuture(new UnsetIdentity()), 368 new UnsetHttpSigner(), 369 AuthSchemeOption.builder() 370 .schemeId("unset") 371 .putSignerProperty(AwsV4FamilyHttpSigner.NORMALIZE_PATH, 372 normalizePath) 373 .build()); 374 } 375 376 return new SelectedAuthScheme<>(authScheme.identity(), 377 authScheme.signer(), 378 authScheme.authSchemeOption() 379 .copy(o -> o.putSignerProperty(AwsV4FamilyHttpSigner.NORMALIZE_PATH, 380 normalizePath))); 381 } 382 signingClockReadMapping(SelectedAuthScheme<?> authScheme)383 private static Clock signingClockReadMapping(SelectedAuthScheme<?> authScheme) { 384 if (authScheme == null) { 385 return null; 386 } 387 return authScheme.authSchemeOption().signerProperty(HttpSigner.SIGNING_CLOCK); 388 } 389 signingClockWriteMapping(SelectedAuthScheme<T> authScheme, Clock clock)390 private static <T extends Identity> SelectedAuthScheme<?> signingClockWriteMapping(SelectedAuthScheme<T> authScheme, 391 Clock clock) { 392 if (authScheme == null) { 393 // This is an unusual use-case. 394 // Let's assume they're setting signing clock so that they can call the signer directly. If that's true, then it 395 // doesn't really matter what we store other than the signing clock. 396 return new SelectedAuthScheme<>(CompletableFuture.completedFuture(new UnsetIdentity()), 397 new UnsetHttpSigner(), 398 AuthSchemeOption.builder() 399 .schemeId("unset") 400 .putSignerProperty(HttpSigner.SIGNING_CLOCK, clock) 401 .build()); 402 } 403 404 return new SelectedAuthScheme<>(authScheme.identity(), 405 authScheme.signer(), 406 authScheme.authSchemeOption() 407 .copy(o -> o.putSignerProperty(HttpSigner.SIGNING_CLOCK, clock))); 408 } 409 410 private static class UnsetIdentity implements Identity { 411 } 412 413 private static class UnsetHttpSigner implements HttpSigner<UnsetIdentity> { 414 @Override sign(SignRequest<? extends UnsetIdentity> request)415 public SignedRequest sign(SignRequest<? extends UnsetIdentity> request) { 416 throw new IllegalStateException("A signer was not configured."); 417 } 418 419 @Override signAsync(AsyncSignRequest<? extends UnsetIdentity> request)420 public CompletableFuture<AsyncSignedRequest> signAsync(AsyncSignRequest<? extends UnsetIdentity> request) { 421 return CompletableFutureUtils.failedFuture(new IllegalStateException("A signer was not configured.")); 422 } 423 } 424 } 425