-
Notifications
You must be signed in to change notification settings - Fork 916
Set the signing clock property #4341
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
eb9c850
18272b9
8cf4b10
814f51e
0defcdb
b5078dc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
/* | ||
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"). | ||
* You may not use this file except in compliance with the License. | ||
* A copy of the License is located at | ||
* | ||
* http://aws.amazon.com/apache2.0 | ||
* | ||
* or in the "license" file accompanying this file. This file is distributed | ||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either | ||
* express or implied. See the License for the specific language governing | ||
* permissions and limitations under the License. | ||
*/ | ||
|
||
package software.amazon.awssdk.http.auth.spi; | ||
|
||
import java.time.Clock; | ||
import software.amazon.awssdk.annotations.SdkPublicApi; | ||
|
||
/** | ||
* Container class for the clock signer property that is common to some signing schemes. | ||
*/ | ||
@SdkPublicApi | ||
public final class SharedSignerProperty { | ||
/** | ||
* A {@link Clock} to be used at the time of signing. This property defaults to the time at which signing occurs. | ||
*/ | ||
public static final SignerProperty<Clock> SIGNING_CLOCK = SignerProperty.create(Clock.class, "SigningClock"); | ||
|
||
private SharedSignerProperty() { | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,6 +16,7 @@ | |
package software.amazon.awssdk.core.internal.http.pipeline.stages; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
import static org.assertj.core.api.Assertions.within; | ||
import static org.mockito.ArgumentMatchers.any; | ||
import static org.mockito.ArgumentMatchers.eq; | ||
import static org.mockito.Mockito.mock; | ||
|
@@ -28,6 +29,8 @@ | |
import static software.amazon.awssdk.core.metrics.CoreMetric.SIGNING_DURATION; | ||
|
||
import java.nio.ByteBuffer; | ||
import java.time.Instant; | ||
import java.time.temporal.ChronoUnit; | ||
import java.util.concurrent.CompletableFuture; | ||
import org.junit.Before; | ||
import org.junit.Test; | ||
|
@@ -58,6 +61,7 @@ | |
import software.amazon.awssdk.http.auth.spi.AsyncSignedRequest; | ||
import software.amazon.awssdk.http.auth.spi.AuthSchemeOption; | ||
import software.amazon.awssdk.http.auth.spi.HttpSigner; | ||
import software.amazon.awssdk.http.auth.spi.SharedSignerProperty; | ||
import software.amazon.awssdk.http.auth.spi.SignerProperty; | ||
import software.amazon.awssdk.http.auth.spi.SyncSignRequest; | ||
import software.amazon.awssdk.http.auth.spi.SyncSignedRequest; | ||
|
@@ -67,7 +71,7 @@ | |
|
||
@RunWith(MockitoJUnitRunner.class) | ||
public class AsyncSigningStageTest { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we add a test (sync and async both) to assert that if the selected auth scheme has a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. good call about that use case! |
||
|
||
private static final int TEST_TIME_OFFSET = 17; | ||
private static final SignerProperty<String> SIGNER_PROPERTY = SignerProperty.create(String.class, "key"); | ||
|
||
@Mock | ||
|
@@ -134,6 +138,59 @@ public void execute_selectedAuthScheme_doesSraSign() throws Exception { | |
assertThat(signRequest.request()).isSameAs(request); | ||
assertThat(signRequest.property(SIGNER_PROPERTY)).isEqualTo("value"); | ||
|
||
// Assert that the time offset set was zero | ||
assertThat(signRequest.property(SharedSignerProperty.SIGNING_CLOCK)).isNotNull(); | ||
assertThat(signRequest.property(SharedSignerProperty.SIGNING_CLOCK).instant()) | ||
.isCloseTo(Instant.now(), within(10, ChronoUnit.MILLIS)); | ||
|
||
// assert that metrics are collected | ||
verify(metricCollector).reportMetric(eq(SIGNING_DURATION), any()); | ||
|
||
verifyNoInteractions(oldSigner); | ||
} | ||
|
||
|
||
@Test | ||
public void execute_selectedAuthSchemeAndTimeOffsetSet_doesSraSignAndAdjustTheSigningClock() throws Exception { | ||
// Set up a scheme with a signer property | ||
SelectedAuthScheme<Identity> selectedAuthScheme = new SelectedAuthScheme<>( | ||
CompletableFuture.completedFuture(identity), | ||
signer, | ||
AuthSchemeOption.builder() | ||
.schemeId("my.auth#myAuth") | ||
.putSignerProperty(SIGNER_PROPERTY, "value") | ||
.build()); | ||
RequestExecutionContext context = createContext(selectedAuthScheme); | ||
|
||
// Setup the timeoffset to test that the clock is setup properly. | ||
httpClientDependencies.updateTimeOffset(TEST_TIME_OFFSET); | ||
|
||
SdkHttpRequest signedRequest = ValidSdkObjects.sdkHttpFullRequest().build(); | ||
when(signer.sign(Mockito.<SyncSignRequest<? extends Identity>>any())) | ||
.thenReturn(SyncSignedRequest.builder() | ||
.request(signedRequest) | ||
.build()); | ||
|
||
SdkHttpFullRequest request = ValidSdkObjects.sdkHttpFullRequest().build(); | ||
SdkHttpFullRequest result = stage.execute(request, context).join(); | ||
|
||
assertThat(result).isSameAs(signedRequest); | ||
// assert that interceptor context is updated with result | ||
assertThat(context.executionContext().interceptorContext().httpRequest()).isSameAs(result); | ||
|
||
// assert that the input to the signer is as expected, including that signer properties are set | ||
verify(signer).sign(syncSignRequestCaptor.capture()); | ||
SyncSignRequest<? extends Identity> signRequest = syncSignRequestCaptor.getValue(); | ||
assertThat(signRequest.identity()).isSameAs(identity); | ||
assertThat(signRequest.request()).isSameAs(request); | ||
assertThat(signRequest.property(SIGNER_PROPERTY)).isEqualTo("value"); | ||
|
||
// Assert that the signing clock is setup properly | ||
assertThat(signRequest.property(SharedSignerProperty.SIGNING_CLOCK)).isNotNull(); | ||
assertThat(signRequest.property(SharedSignerProperty.SIGNING_CLOCK).instant()) | ||
.isCloseTo(Instant.now().minusSeconds(17) | ||
, within(10, ChronoUnit.MILLIS)); | ||
|
||
// assert that metrics are collected | ||
verify(metricCollector).reportMetric(eq(SIGNING_DURATION), any()); | ||
|
||
|
@@ -177,8 +234,13 @@ public void execute_selectedAuthScheme_asyncRequestBody_doesSraSignAsync() throw | |
AsyncSignRequest<? extends Identity> signRequest = asyncSignRequestCaptor.getValue(); | ||
assertThat(signRequest.identity()).isSameAs(identity); | ||
assertThat(signRequest.request()).isSameAs(request); | ||
assertThat(signRequest.property(SIGNER_PROPERTY)).isEqualTo("value"); | ||
assertThat(signRequest.payload().get()).isSameAs(asyncPayload); | ||
assertThat(signRequest.property(SIGNER_PROPERTY)).isEqualTo("value"); | ||
|
||
// Assert that the time offset set was zero | ||
assertThat(signRequest.property(SharedSignerProperty.SIGNING_CLOCK)).isNotNull(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think rather not set the property if offset is 0 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That will introduce a branch that is not needed the way it's written now, so I think is better just to have a single code path. Can you please help me understand why you think is better not to set it? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thought it is better since with current approach the test is more fragile and can fail sometimes if for some reason Instant.now is actually beyond the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The other test is still fragile like that. I think we can have a package-private constructor that takes in a Clock (instead of Clock.systemUTC), so that unit test can use a mock Clock and test exactly, instead of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That would address my main concern - test fragility. We could still chose to not set or set the clock property if offset is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd rather tackle the problem when and if arises, I'm know that what you describing is possible but I don't think is likely, for that to happen it would mean that the machine running the tests will need to park the thread for more than 100ms (~ 300 Million instructions). Were the previous tests brittle like actually failing every so often? |
||
assertThat(signRequest.property(SharedSignerProperty.SIGNING_CLOCK).instant()) | ||
.isCloseTo(Instant.now(), within(10, ChronoUnit.MILLIS)); | ||
|
||
// assert that metrics are collected | ||
verify(metricCollector).reportMetric(eq(SIGNING_DURATION), any()); | ||
|
@@ -225,6 +287,11 @@ public void execute_selectedNoAuthAuthScheme_doesSraSignAsync() throws Exception | |
assertThat(signRequest.property(SIGNER_PROPERTY)).isEqualTo("value"); | ||
assertThat(signRequest.payload().get()).isSameAs(asyncPayload); | ||
|
||
// Assert that the time offset set was zero | ||
assertThat(signRequest.property(SharedSignerProperty.SIGNING_CLOCK)).isNotNull(); | ||
assertThat(signRequest.property(SharedSignerProperty.SIGNING_CLOCK).instant()) | ||
.isCloseTo(Instant.now(), within(10, ChronoUnit.MILLIS)); | ||
|
||
// assert that metrics are collected | ||
verify(metricCollector).reportMetric(eq(SIGNING_DURATION), any()); | ||
|
||
|
Uh oh!
There was an error while loading. Please reload this page.