diff --git a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/internal/AwsExecutionContextBuilder.java b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/internal/AwsExecutionContextBuilder.java index 6cb595e0b589..d7740f722770 100644 --- a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/internal/AwsExecutionContextBuilder.java +++ b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/internal/AwsExecutionContextBuilder.java @@ -31,6 +31,7 @@ import software.amazon.awssdk.core.RequestOverrideConfiguration; import software.amazon.awssdk.core.SdkRequest; import software.amazon.awssdk.core.SdkResponse; +import software.amazon.awssdk.core.SelectedAuthScheme; import software.amazon.awssdk.core.client.config.SdkAdvancedClientOption; import software.amazon.awssdk.core.client.config.SdkClientConfiguration; import software.amazon.awssdk.core.client.config.SdkClientOption; @@ -45,6 +46,7 @@ import software.amazon.awssdk.core.internal.util.HttpChecksumResolver; import software.amazon.awssdk.core.signer.Signer; import software.amazon.awssdk.endpoints.EndpointProvider; +import software.amazon.awssdk.http.auth.scheme.NoAuthAuthScheme; import software.amazon.awssdk.http.auth.spi.scheme.AuthScheme; import software.amazon.awssdk.http.auth.spi.scheme.AuthSchemeProvider; import software.amazon.awssdk.identity.spi.IdentityProviders; @@ -152,10 +154,28 @@ private AwsExecutionContextBuilder() { /** * We will load the old (non-SRA) signer if this client seems like an old version or the customer has provided a signer * override. We assume that if there's no auth schemes defined, we're on the old code path. + *

+ * In addition, if authType=none, we don't need to use the old signer, even if overridden. */ private static boolean loadOldSigner(ExecutionAttributes attributes, SdkRequest request) { - return attributes.getAttribute(SdkInternalExecutionAttribute.AUTH_SCHEMES) == null || - SignerOverrideUtils.isSignerOverridden(request, attributes); + Map> authSchemes = attributes.getAttribute(SdkInternalExecutionAttribute.AUTH_SCHEMES); + if (authSchemes == null) { + // pre SRA case. + // We used to set IS_NONE_AUTH_TYPE_REQUEST = false when authType=none. Yes, false. + return attributes.getOptionalAttribute(SdkInternalExecutionAttribute.IS_NONE_AUTH_TYPE_REQUEST).orElse(true); + } + + // post SRA case. + // By default, SRA uses new HttpSigner, so we shouldn't use old non-SRA Signer, unless the customer has provided a signer + // override. + // But, if the operation was modeled as authTpye=None, we don't want to use the provided overridden Signer either. In + // post SRA, modeled authType=None would default to NoAuthAuthScheme. + // Note, for authType=None operation, technically, customer could override the AuthSchemeProvider and select a different + // AuthScheme (than NoAuthAuthScheme). In this case, we are choosing to use the customer's overridden Signer. + SelectedAuthScheme selectedAuthScheme = attributes.getAttribute(SdkInternalExecutionAttribute.SELECTED_AUTH_SCHEME); + return SignerOverrideUtils.isSignerOverridden(request, attributes) && + selectedAuthScheme != null && + !NoAuthAuthScheme.SCHEME_ID.equals(selectedAuthScheme.authSchemeOption().schemeId()); } private static void putAuthSchemeResolutionAttributes(ExecutionAttributes executionAttributes, diff --git a/core/aws-core/src/test/java/software/amazon/awssdk/awscore/internal/AwsExecutionContextBuilderTest.java b/core/aws-core/src/test/java/software/amazon/awssdk/awscore/internal/AwsExecutionContextBuilderTest.java index 3aee114c1b20..91a18bfc8477 100644 --- a/core/aws-core/src/test/java/software/amazon/awssdk/awscore/internal/AwsExecutionContextBuilderTest.java +++ b/core/aws-core/src/test/java/software/amazon/awssdk/awscore/internal/AwsExecutionContextBuilderTest.java @@ -17,14 +17,16 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.Optional; +import java.util.concurrent.CompletableFuture; import java.util.function.Supplier; import org.junit.Before; import org.junit.Test; @@ -32,12 +34,12 @@ import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; -import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider; import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; import software.amazon.awssdk.awscore.AwsRequestOverrideConfiguration; import software.amazon.awssdk.awscore.client.config.AwsClientOption; import software.amazon.awssdk.core.SdkRequest; import software.amazon.awssdk.core.SdkResponse; +import software.amazon.awssdk.core.SelectedAuthScheme; import software.amazon.awssdk.core.checksums.ChecksumSpecs; import software.amazon.awssdk.core.client.config.SdkAdvancedClientOption; import software.amazon.awssdk.core.client.config.SdkClientConfiguration; @@ -51,6 +53,11 @@ import software.amazon.awssdk.core.interceptor.trait.HttpChecksum; import software.amazon.awssdk.core.internal.util.HttpChecksumUtils; import software.amazon.awssdk.core.signer.Signer; +import software.amazon.awssdk.http.auth.aws.scheme.AwsV4AuthScheme; +import software.amazon.awssdk.http.auth.scheme.NoAuthAuthScheme; +import software.amazon.awssdk.http.auth.spi.scheme.AuthScheme; +import software.amazon.awssdk.http.auth.spi.scheme.AuthSchemeOption; +import software.amazon.awssdk.http.auth.spi.signer.HttpSigner; import software.amazon.awssdk.identity.spi.AwsCredentialsIdentity; import software.amazon.awssdk.identity.spi.IdentityProvider; import software.amazon.awssdk.identity.spi.IdentityProviders; @@ -65,16 +72,25 @@ public class AwsExecutionContextBuilderTest { @Mock ExecutionInterceptor interceptor; + @Mock + IdentityProvider defaultCredentialsProvider; + @Mock Signer defaultSigner; @Mock Signer clientOverrideSigner; + @Mock + Map> defaultAuthSchemes; + @Before public void setUp() throws Exception { when(sdkRequest.overrideConfiguration()).thenReturn(Optional.empty()); when(interceptor.modifyRequest(any(), any())).thenReturn(sdkRequest); + when(defaultCredentialsProvider.resolveIdentity()).thenAnswer( + invocationOnMock -> CompletableFuture.completedFuture(AwsCredentialsIdentity.create("ak", "sk"))); + } @Test @@ -109,17 +125,45 @@ public void verifyCoreExecutionAttributesTakePrecedence() { assertThat(executionContext.executionAttributes().getAttribute(SdkExecutionAttribute.SERVICE_NAME)).isEqualTo("DoNotOverrideService"); } + // pre SRA, AuthorizationStrategy would setup the signer and resolve identity. @Test - public void signing_ifNoOverrides_assignDefaultSigner() { + public void preSra_signing_ifNoOverrides_assignDefaultSigner_resolveIdentity() { ExecutionContext executionContext = AwsExecutionContextBuilder.invokeInterceptorsAndCreateExecutionContext(clientExecutionParams(), - testClientConfiguration().build()); + preSraClientConfiguration().build()); assertThat(executionContext.signer()).isEqualTo(defaultSigner); + verify(defaultCredentialsProvider, times(1)).resolveIdentity(); } + // This is post SRA case. This is asserting that AuthorizationStrategy is not used. @Test - public void signing_ifClientOverride_assignClientOverrideSigner() { + public void postSra_ifNoOverrides_doesNotResolveIdentity_doesNotAssignSigner() { + ExecutionContext executionContext = + AwsExecutionContextBuilder.invokeInterceptorsAndCreateExecutionContext(clientExecutionParams(), + testClientConfiguration().build()); + + assertThat(executionContext.signer()).isNull(); + verify(defaultCredentialsProvider, times(0)).resolveIdentity(); + } + + @Test + public void preSra_signing_ifClientOverride_assignClientOverrideSigner_resolveIdentity() { + Optional overrideConfiguration = Optional.of(AwsRequestOverrideConfiguration.builder() + .signer(clientOverrideSigner) + .build()); + when(sdkRequest.overrideConfiguration()).thenReturn(overrideConfiguration); + + ExecutionContext executionContext = + AwsExecutionContextBuilder.invokeInterceptorsAndCreateExecutionContext(clientExecutionParams(), + preSraClientConfiguration().build()); + + assertThat(executionContext.signer()).isEqualTo(clientOverrideSigner); + verify(defaultCredentialsProvider, times(1)).resolveIdentity(); + } + + @Test + public void postSra_signing_ifClientOverride_assignClientOverrideSigner_resolveIdentity() { Optional overrideConfiguration = Optional.of(AwsRequestOverrideConfiguration.builder() .signer(clientOverrideSigner) .build()); @@ -130,6 +174,102 @@ public void signing_ifClientOverride_assignClientOverrideSigner() { testClientConfiguration().build()); assertThat(executionContext.signer()).isEqualTo(clientOverrideSigner); + verify(defaultCredentialsProvider, times(1)).resolveIdentity(); + } + + @Test + public void preSra_authTypeNone_doesNotAssignSigner_doesNotResolveIdentity() { + SdkClientConfiguration.Builder clientConfig = preSraClientConfiguration(); + clientConfig.option(SdkClientOption.EXECUTION_ATTRIBUTES) + // yes, our code would put false instead of true + .putAttribute(SdkInternalExecutionAttribute.IS_NONE_AUTH_TYPE_REQUEST, false); + + ExecutionContext executionContext = + AwsExecutionContextBuilder.invokeInterceptorsAndCreateExecutionContext(clientExecutionParams(), + clientConfig.build()); + + assertThat(executionContext.signer()).isNull(); + verify(defaultCredentialsProvider, times(0)).resolveIdentity(); + } + + @Test + public void postSra_authTypeNone_doesNotAssignSigner_doesNotResolveIdentity() { + SdkClientConfiguration.Builder clientConfig = noAuthAuthSchemeClientConfiguration(); + + ExecutionContext executionContext = + AwsExecutionContextBuilder.invokeInterceptorsAndCreateExecutionContext(clientExecutionParams(), + clientConfig.build()); + + assertThat(executionContext.signer()).isNull(); + verify(defaultCredentialsProvider, times(0)).resolveIdentity(); + } + + @Test + public void preSra_authTypeNone_signerClientOverride_doesNotAssignSigner_doesNotResolveIdentity() { + SdkClientConfiguration.Builder clientConfig = preSraClientConfiguration(); + clientConfig.option(SdkClientOption.EXECUTION_ATTRIBUTES) + // yes, our code would put false instead of true + .putAttribute(SdkInternalExecutionAttribute.IS_NONE_AUTH_TYPE_REQUEST, false); + clientConfig.option(SdkAdvancedClientOption.SIGNER, this.clientOverrideSigner) + .option(SdkClientOption.SIGNER_OVERRIDDEN, true); + + ExecutionContext executionContext = + AwsExecutionContextBuilder.invokeInterceptorsAndCreateExecutionContext(clientExecutionParams(), + clientConfig.build()); + + assertThat(executionContext.signer()).isNull(); + verify(defaultCredentialsProvider, times(0)).resolveIdentity(); + } + + @Test + public void postSra_authTypeNone_signerClientOverride_doesNotAssignSigner_doesNotResolveIdentity() { + SdkClientConfiguration.Builder clientConfig = noAuthAuthSchemeClientConfiguration(); + clientConfig.option(SdkAdvancedClientOption.SIGNER, this.clientOverrideSigner) + .option(SdkClientOption.SIGNER_OVERRIDDEN, true); + + ExecutionContext executionContext = + AwsExecutionContextBuilder.invokeInterceptorsAndCreateExecutionContext(clientExecutionParams(), + clientConfig.build()); + + assertThat(executionContext.signer()).isNull(); + verify(defaultCredentialsProvider, times(0)).resolveIdentity(); + } + + @Test + public void preSra_authTypeNone_signerRequestOverride_doesNotAssignSigner_doesNotResolveIdentity() { + SdkClientConfiguration.Builder clientConfig = preSraClientConfiguration(); + clientConfig.option(SdkClientOption.EXECUTION_ATTRIBUTES) + // yes, our code would put false instead of true + .putAttribute(SdkInternalExecutionAttribute.IS_NONE_AUTH_TYPE_REQUEST, false); + + Optional overrideConfiguration = Optional.of(AwsRequestOverrideConfiguration.builder() + .signer(clientOverrideSigner) + .build()); + when(sdkRequest.overrideConfiguration()).thenReturn(overrideConfiguration); + + ExecutionContext executionContext = + AwsExecutionContextBuilder.invokeInterceptorsAndCreateExecutionContext(clientExecutionParams(), + clientConfig.build()); + + assertThat(executionContext.signer()).isNull(); + verify(defaultCredentialsProvider, times(0)).resolveIdentity(); + } + + @Test + public void postSra_authTypeNone_signerRequestOverride_doesNotAssignSigner_doesNotResolveIdentity() { + SdkClientConfiguration.Builder clientConfig = noAuthAuthSchemeClientConfiguration(); + + Optional overrideConfiguration = Optional.of(AwsRequestOverrideConfiguration.builder() + .signer(clientOverrideSigner) + .build()); + when(sdkRequest.overrideConfiguration()).thenReturn(overrideConfiguration); + + ExecutionContext executionContext = + AwsExecutionContextBuilder.invokeInterceptorsAndCreateExecutionContext(clientExecutionParams(), + clientConfig.build()); + + assertThat(executionContext.signer()).isNull(); + verify(defaultCredentialsProvider, times(0)).resolveIdentity(); } @Test @@ -255,11 +395,44 @@ private ClientExecutionParams clientExecutionParams() { } private SdkClientConfiguration.Builder testClientConfiguration() { + // In real SRA case, SelectedAuthScheme is setup as an executionAttribute by {Service}AuthSchemeInterceptor that is setup + // in EXECUTION_INTERCEPTORS. But, faking it here for unit test, by already setting SELECTED_AUTH_SCHEME into the + // executionAttributes. + SelectedAuthScheme selectedAuthScheme = new SelectedAuthScheme<>( + CompletableFuture.completedFuture(AwsCredentialsIdentity.create("ak", "sk")), + mock(HttpSigner.class), + AuthSchemeOption.builder().schemeId(AwsV4AuthScheme.SCHEME_ID).build() + ); + ExecutionAttributes executionAttributes = + ExecutionAttributes.builder() + .put(SdkInternalExecutionAttribute.SELECTED_AUTH_SCHEME, selectedAuthScheme) + .build(); + List interceptorList = Collections.singletonList(interceptor); return SdkClientConfiguration.builder() - .option(SdkClientOption.EXECUTION_INTERCEPTORS, new ArrayList<>()) .option(SdkClientOption.EXECUTION_INTERCEPTORS, interceptorList) - .option(AwsClientOption.CREDENTIALS_IDENTITY_PROVIDER, DefaultCredentialsProvider.create()) - .option(SdkAdvancedClientOption.SIGNER, this.defaultSigner); + .option(AwsClientOption.CREDENTIALS_IDENTITY_PROVIDER, defaultCredentialsProvider) + .option(SdkClientOption.AUTH_SCHEMES, defaultAuthSchemes) + .option(SdkClientOption.EXECUTION_ATTRIBUTES, executionAttributes); + } + + private SdkClientConfiguration.Builder noAuthAuthSchemeClientConfiguration() { + SdkClientConfiguration.Builder clientConfig = testClientConfiguration(); + SelectedAuthScheme selectedNoAuthScheme = new SelectedAuthScheme<>( + CompletableFuture.completedFuture(AwsCredentialsIdentity.create("ak", "sk")), + mock(HttpSigner.class), + AuthSchemeOption.builder().schemeId(NoAuthAuthScheme.SCHEME_ID).build() + ); + clientConfig.option(SdkClientOption.EXECUTION_ATTRIBUTES) + .putAttribute(SdkInternalExecutionAttribute.SELECTED_AUTH_SCHEME, selectedNoAuthScheme); + return clientConfig; + } + + private SdkClientConfiguration.Builder preSraClientConfiguration() { + SdkClientConfiguration.Builder clientConfiguration = testClientConfiguration(); + clientConfiguration.option(SdkClientOption.EXECUTION_ATTRIBUTES) + .putAttribute(SdkInternalExecutionAttribute.SELECTED_AUTH_SCHEME, null); + return clientConfiguration.option(SdkClientOption.AUTH_SCHEMES, null) + .option(SdkAdvancedClientOption.SIGNER, this.defaultSigner); } } diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/AsyncSigningStage.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/AsyncSigningStage.java index 416bcea38d40..762ffc5ca3cc 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/AsyncSigningStage.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/AsyncSigningStage.java @@ -67,19 +67,27 @@ public AsyncSigningStage(HttpClientDependencies dependencies) { @Override public CompletableFuture execute(SdkHttpFullRequest request, RequestExecutionContext context) throws Exception { - SelectedAuthScheme selectedAuthScheme = - context.executionAttributes().getAttribute(SdkInternalExecutionAttribute.SELECTED_AUTH_SCHEME); - if (shouldDoSraSigning(context, selectedAuthScheme)) { + + updateHttpRequestInInterceptorContext(request, context.executionContext()); + + // Whether pre / post SRA, if old Signer is setup in context, that's the one to use + if (context.signer() != null) { + return signRequest(request, context); + } + // else if AUTH_SCHEMES != null (implies SRA), use SelectedAuthScheme + if (context.executionAttributes().getAttribute(SdkInternalExecutionAttribute.AUTH_SCHEMES) != null) { + SelectedAuthScheme selectedAuthScheme = + context.executionAttributes().getAttribute(SdkInternalExecutionAttribute.SELECTED_AUTH_SCHEME); log.debug(() -> String.format("Using SelectedAuthScheme: %s", selectedAuthScheme.authSchemeOption().schemeId())); return sraSignRequest(request, context, selectedAuthScheme); } - return signRequest(request, context); + // else, this implies pre SRA client with authType=None, so don't need to do anything + return CompletableFuture.completedFuture(request); } private CompletableFuture sraSignRequest(SdkHttpFullRequest request, RequestExecutionContext context, SelectedAuthScheme selectedAuthScheme) { - updateHttpRequestInInterceptorContext(request, context.executionContext()); adjustForClockSkew(context.executionAttributes()); CompletableFuture identityFuture = selectedAuthScheme.identity(); return identityFuture.thenCompose(identity -> { @@ -183,15 +191,9 @@ private SdkHttpFullRequest.Builder toSdkHttpFullRequestBuilder(BaseSignedRequest */ private CompletableFuture signRequest(SdkHttpFullRequest request, RequestExecutionContext context) { - updateHttpRequestInInterceptorContext(request, context.executionContext()); - Signer signer = context.signer(); MetricCollector metricCollector = context.attemptMetricCollector(); - if (!shouldSign(context.executionAttributes(), signer)) { - return CompletableFuture.completedFuture(request); - } - adjustForClockSkew(context.executionAttributes()); AsyncSigner asyncSigner = asAsyncSigner(signer, context); @@ -216,22 +218,6 @@ private void updateHttpRequestInInterceptorContext(SdkHttpFullRequest request, E executionContext.interceptorContext(executionContext.interceptorContext().copy(b -> b.httpRequest(request))); } - /** - * We sign if it isn't auth=none. This attribute is no longer set in the SRA, so this exists only for old clients. In - * addition to this, old clients only set this to false, never true. So, we have to treat null as true. - */ - private boolean shouldSign(ExecutionAttributes attributes, Signer signer) { - return signer != null && - !Boolean.FALSE.equals(attributes.getAttribute(SdkInternalExecutionAttribute.IS_NONE_AUTH_TYPE_REQUEST)); - } - - /** - * Returns true if we should use SRA signing logic. - */ - private boolean shouldDoSraSigning(RequestExecutionContext context, SelectedAuthScheme selectedAuthScheme) { - return context.signer() == null && selectedAuthScheme != null; - } - /** * Returns the {@link Clock} used for signing that already accounts for clock skew when detected by the retryable stage. */ diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/SigningStage.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/SigningStage.java index 9bf94b1d78c8..9a99af93e7da 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/SigningStage.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/SigningStage.java @@ -67,19 +67,26 @@ public SigningStage(HttpClientDependencies dependencies) { public SdkHttpFullRequest execute(SdkHttpFullRequest request, RequestExecutionContext context) throws Exception { InterruptMonitor.checkInterrupted(); - SelectedAuthScheme selectedAuthScheme = - context.executionAttributes().getAttribute(SdkInternalExecutionAttribute.SELECTED_AUTH_SCHEME); - if (shouldDoSraSigning(context, selectedAuthScheme)) { + updateHttpRequestInInterceptorContext(request, context.executionContext()); + + // Whether pre / post SRA, if old Signer is setup in context, that's the one to use + if (context.signer() != null) { + return signRequest(request, context); + } + // else if AUTH_SCHEMES != null (implies SRA), use SelectedAuthScheme + if (context.executionAttributes().getAttribute(SdkInternalExecutionAttribute.AUTH_SCHEMES) != null) { + SelectedAuthScheme selectedAuthScheme = + context.executionAttributes().getAttribute(SdkInternalExecutionAttribute.SELECTED_AUTH_SCHEME); log.debug(() -> String.format("Using SelectedAuthScheme: %s", selectedAuthScheme.authSchemeOption().schemeId())); return sraSignRequest(request, context, selectedAuthScheme); } - return signRequest(request, context); + // else, this implies pre SRA client, with authType=None, so don't need to do anything + return request; } private SdkHttpFullRequest sraSignRequest(SdkHttpFullRequest request, RequestExecutionContext context, SelectedAuthScheme selectedAuthScheme) { - updateHttpRequestInInterceptorContext(request, context.executionContext()); adjustForClockSkew(context.executionAttributes()); CompletableFuture identityFuture = selectedAuthScheme.identity(); T identity = CompletableFutureUtils.joinLikeSync(identityFuture); @@ -128,15 +135,9 @@ private SdkHttpFullRequest toSdkHttpFullRequest(SignedRequest signedRequest) { * Sign the request if the signer if provided and credentials are present. */ private SdkHttpFullRequest signRequest(SdkHttpFullRequest request, RequestExecutionContext context) { - updateHttpRequestInInterceptorContext(request, context.executionContext()); - Signer signer = context.signer(); MetricCollector metricCollector = context.attemptMetricCollector(); - if (!shouldSign(context.executionAttributes(), signer)) { - return request; - } - adjustForClockSkew(context.executionAttributes()); Pair measuredSign = MetricUtils.measureDuration( @@ -168,22 +169,6 @@ private void updateHttpRequestInInterceptorContext(SdkHttpFullRequest request, E executionContext.interceptorContext(executionContext.interceptorContext().copy(b -> b.httpRequest(request))); } - /** - * We sign if it isn't auth=none. This attribute is no longer set in the SRA, so this exists only for old clients. In - * addition to this, old clients only set this to false, never true. So, we have to treat null as true. - */ - private boolean shouldSign(ExecutionAttributes attributes, Signer signer) { - return signer != null && - !Boolean.FALSE.equals(attributes.getAttribute(SdkInternalExecutionAttribute.IS_NONE_AUTH_TYPE_REQUEST)); - } - - /** - * Returns true if we should use SRA signing logic. - */ - private boolean shouldDoSraSigning(RequestExecutionContext context, SelectedAuthScheme selectedAuthScheme) { - return context.signer() == null && selectedAuthScheme != null; - } - /** * Returns the {@link Clock} used for signing that already accounts for clock skew when detected by the retryable stage. */ diff --git a/core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/http/pipeline/stages/AsyncSigningStageTest.java b/core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/http/pipeline/stages/AsyncSigningStageTest.java index babc64483ff8..ae9851abe1f9 100644 --- a/core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/http/pipeline/stages/AsyncSigningStageTest.java +++ b/core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/http/pipeline/stages/AsyncSigningStageTest.java @@ -31,6 +31,7 @@ import java.time.Clock; import java.time.Instant; import java.time.temporal.ChronoUnit; +import java.util.HashMap; import java.util.concurrent.CompletableFuture; import org.junit.Before; import org.junit.Test; @@ -48,6 +49,7 @@ import software.amazon.awssdk.core.http.ExecutionContext; import software.amazon.awssdk.core.interceptor.ExecutionAttributes; import software.amazon.awssdk.core.interceptor.InterceptorContext; +import software.amazon.awssdk.core.interceptor.SdkInternalExecutionAttribute; import software.amazon.awssdk.core.internal.http.HttpClientDependencies; import software.amazon.awssdk.core.internal.http.RequestExecutionContext; import software.amazon.awssdk.core.signer.AsyncRequestBodySigner; @@ -539,12 +541,15 @@ private RequestExecutionContext createContext(SelectedAuthScheme selec // .httpRequest(request) .build(); - ExecutionAttributes executionAttributes = ExecutionAttributes.builder() - .put(SELECTED_AUTH_SCHEME, selectedAuthScheme) - .build(); + ExecutionAttributes.Builder executionAttributes = ExecutionAttributes.builder() + .put(SELECTED_AUTH_SCHEME, selectedAuthScheme); + if (selectedAuthScheme != null) { + // Doesn't matter that it is empty, just needs to non-null, which implies SRA path. + executionAttributes.put(SdkInternalExecutionAttribute.AUTH_SCHEMES, new HashMap<>()); + } ExecutionContext executionContext = ExecutionContext.builder() - .executionAttributes(executionAttributes) + .executionAttributes(executionAttributes.build()) .interceptorContext(interceptorContext) .signer(oldSigner) .build(); diff --git a/core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/http/pipeline/stages/SigningStageTest.java b/core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/http/pipeline/stages/SigningStageTest.java index f6396a79f23f..865535a6298d 100644 --- a/core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/http/pipeline/stages/SigningStageTest.java +++ b/core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/http/pipeline/stages/SigningStageTest.java @@ -30,6 +30,7 @@ import java.time.Instant; import java.time.ZoneId; import java.time.temporal.ChronoUnit; +import java.util.HashMap; import java.util.concurrent.CompletableFuture; import org.junit.Before; import org.junit.Test; @@ -45,6 +46,7 @@ import software.amazon.awssdk.core.http.ExecutionContext; import software.amazon.awssdk.core.interceptor.ExecutionAttributes; import software.amazon.awssdk.core.interceptor.InterceptorContext; +import software.amazon.awssdk.core.interceptor.SdkInternalExecutionAttribute; import software.amazon.awssdk.core.internal.http.HttpClientDependencies; import software.amazon.awssdk.core.internal.http.RequestExecutionContext; import software.amazon.awssdk.core.signer.Signer; @@ -380,12 +382,15 @@ private RequestExecutionContext createContext(SelectedAuthScheme selec // .httpRequest(request) .build(); - ExecutionAttributes executionAttributes = ExecutionAttributes.builder() - .put(SELECTED_AUTH_SCHEME, selectedAuthScheme) - .build(); + ExecutionAttributes.Builder executionAttributes = ExecutionAttributes.builder() + .put(SELECTED_AUTH_SCHEME, selectedAuthScheme); + if (selectedAuthScheme != null) { + // Doesn't matter that it is empty, just needs to non-null, which implies SRA path. + executionAttributes.put(SdkInternalExecutionAttribute.AUTH_SCHEMES, new HashMap<>()); + } ExecutionContext executionContext = ExecutionContext.builder() - .executionAttributes(executionAttributes) + .executionAttributes(executionAttributes.build()) .interceptorContext(interceptorContext) .signer(oldSigner) .build(); diff --git a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/NoneAuthTypeRequestTest.java b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/NoneAuthTypeRequestTest.java index e914d348ad2e..f11901ef1fa6 100644 --- a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/NoneAuthTypeRequestTest.java +++ b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/NoneAuthTypeRequestTest.java @@ -17,6 +17,10 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import io.reactivex.Flowable; import java.io.IOException; @@ -24,9 +28,8 @@ import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; -import org.mockito.Mockito; import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; -import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; +import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; import software.amazon.awssdk.awscore.client.builder.AwsAsyncClientBuilder; import software.amazon.awssdk.awscore.client.builder.AwsClientBuilder; import software.amazon.awssdk.awscore.client.builder.AwsSyncClientBuilder; @@ -48,8 +51,10 @@ /** * Verify that the "authtype" C2J trait for request type is honored for each requests. */ +// TODO(sra-identity-auth): Verify these tests pass for useSraAuht=true public class NoneAuthTypeRequestTest { + private AwsCredentialsProvider credentialsProvider; private SdkHttpClient httpClient; private SdkAsyncHttpClient httpAsyncClient; private ProtocolRestJsonClient jsonClient; @@ -57,11 +62,14 @@ public class NoneAuthTypeRequestTest { private ProtocolRestXmlClient xmlClient; private ProtocolRestXmlAsyncClient xmlAsyncClient; - @Before public void setup() throws IOException { - httpClient = Mockito.mock(SdkHttpClient.class); - httpAsyncClient = Mockito.mock(SdkAsyncHttpClient.class); + credentialsProvider = mock(AwsCredentialsProvider.class); + when(credentialsProvider.resolveIdentity()).thenAnswer( + invocationOnMock -> CompletableFuture.completedFuture(AwsBasicCredentials.create("123", "12344"))); + + httpClient = mock(SdkHttpClient.class); + httpAsyncClient = mock(SdkAsyncHttpClient.class); jsonClient = initializeSync(ProtocolRestJsonClient.builder()).build(); jsonAsyncClient = initializeAsync(ProtocolRestJsonAsyncClient.builder()).build(); xmlClient = initializeSync(ProtocolRestXmlClient.builder()).build(); @@ -72,13 +80,13 @@ public void setup() throws IOException { .putHeader("Content-Length", "0") .build(); - ExecutableHttpRequest request = Mockito.mock(ExecutableHttpRequest.class); + ExecutableHttpRequest request = mock(ExecutableHttpRequest.class); - Mockito.when(request.call()).thenReturn(HttpExecuteResponse.builder() + when(request.call()).thenReturn(HttpExecuteResponse.builder() .response(successfulHttpResponse) .build()); - Mockito.when(httpClient.prepareRequest(any())).thenReturn(request); - Mockito.when(httpAsyncClient.execute(any())).thenAnswer(invocation -> { + when(httpClient.prepareRequest(any())).thenReturn(request); + when(httpAsyncClient.execute(any())).thenAnswer(invocation -> { AsyncExecuteRequest asyncExecuteRequest = invocation.getArgument(0, AsyncExecuteRequest.class); asyncExecuteRequest.responseHandler().onHeaders(successfulHttpResponse); asyncExecuteRequest.responseHandler().onStream(Flowable.empty()); @@ -90,68 +98,76 @@ public void setup() throws IOException { public void sync_json_authorization_is_absent_for_noneAuthType() { jsonClient.operationWithNoneAuthType(o -> o.booleanMember(true)); assertThat(getSyncRequest().firstMatchingHeader("Authorization")).isNotPresent(); + verify(credentialsProvider, times(0)).resolveIdentity(); } @Test public void sync_json_authorization_is_present_for_defaultAuth() { jsonClient.jsonValuesOperation(); assertThat(getSyncRequest().firstMatchingHeader("Authorization")).isPresent(); + verify(credentialsProvider, times(1)).resolveIdentity(); } @Test public void async_json_authorization_is_absent_for_noneAuthType() { jsonAsyncClient.operationWithNoneAuthType(o -> o.booleanMember(true)); assertThat(getAsyncRequest().firstMatchingHeader("Authorization")).isNotPresent(); + verify(credentialsProvider, times(0)).resolveIdentity(); } @Test public void async_json_authorization_is_present_for_defaultAuth() { jsonAsyncClient.jsonValuesOperation(); assertThat(getAsyncRequest().firstMatchingHeader("Authorization")).isPresent(); + verify(credentialsProvider, times(1)).resolveIdentity(); } @Test public void sync_xml_authorization_is_absent_for_noneAuthType() { xmlClient.operationWithNoneAuthType(o -> o.booleanMember(true)); assertThat(getSyncRequest().firstMatchingHeader("Authorization")).isNotPresent(); + verify(credentialsProvider, times(0)).resolveIdentity(); } @Test public void sync_xml_authorization_is_present_for_defaultAuth() { xmlClient.jsonValuesOperation(json -> json.jsonValueMember("one")); assertThat(getSyncRequest().firstMatchingHeader("Authorization")).isPresent(); + verify(credentialsProvider, times(1)).resolveIdentity(); } @Test public void async_xml_authorization_is_absent_for_noneAuthType() { xmlAsyncClient.operationWithNoneAuthType(o -> o.booleanMember(true)); assertThat(getAsyncRequest().firstMatchingHeader("Authorization")).isNotPresent(); + verify(credentialsProvider, times(0)).resolveIdentity(); } @Test public void async_xml_authorization_is_present_for_defaultAuth() { xmlAsyncClient.jsonValuesOperation(json -> json.jsonValueMember("one")); assertThat(getAsyncRequest().firstMatchingHeader("Authorization")).isPresent(); + verify(credentialsProvider, times(1)).resolveIdentity(); } private SdkHttpRequest getSyncRequest() { ArgumentCaptor captor = ArgumentCaptor.forClass(HttpExecuteRequest.class); - Mockito.verify(httpClient).prepareRequest(captor.capture()); + verify(httpClient).prepareRequest(captor.capture()); return captor.getValue().httpRequest(); } private SdkHttpRequest getAsyncRequest() { ArgumentCaptor captor = ArgumentCaptor.forClass(AsyncExecuteRequest.class); - Mockito.verify(httpAsyncClient).execute(captor.capture()); + verify(httpAsyncClient).execute(captor.capture()); return captor.getValue().request(); } private & AwsClientBuilder> T initializeSync(T syncClientBuilder) { - return initialize(syncClientBuilder.httpClient(httpClient).credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create("123", "12344")))); + return initialize(syncClientBuilder.httpClient(httpClient).credentialsProvider(credentialsProvider)); } private & AwsClientBuilder> T initializeAsync(T asyncClientBuilder) { - return initialize(asyncClientBuilder.httpClient(httpAsyncClient)); + return initialize(asyncClientBuilder.httpClient(httpAsyncClient).credentialsProvider(credentialsProvider)); } private > T initialize(T clientBuilder) {