Skip to content

Commit 6ab9802

Browse files
committed
Preserve interceptor-set signer properties across auth scheme and endpoint resolution
1 parent 413b425 commit 6ab9802

6 files changed

Lines changed: 113 additions & 5 deletions

File tree

core/aws-core/src/main/java/software/amazon/awssdk/awscore/internal/AwsExecutionContextBuilder.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,9 @@ private AwsExecutionContextBuilder() {
161161
ExecutionInterceptorChain executionInterceptorChain =
162162
new ExecutionInterceptorChain(clientConfig.option(SdkClientOption.EXECUTION_INTERCEPTORS));
163163

164+
executionAttributes.putAttribute(SdkInternalExecutionAttribute.AUTH_SCHEME_BEFORE_INTERCEPTORS,
165+
executionAttributes.getAttribute(SdkInternalExecutionAttribute.SELECTED_AUTH_SCHEME));
166+
164167
InterceptorContext interceptorContext = InterceptorContext.builder()
165168
.request(originalRequest)
166169
.asyncRequestBody(executionParams.getAsyncRequestBody())

core/sdk-core/src/main/java/software/amazon/awssdk/core/http/auth/AuthSchemeResolver.java

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.util.ArrayList;
2020
import java.util.List;
2121
import java.util.Map;
22+
import java.util.Objects;
2223
import java.util.concurrent.CompletableFuture;
2324
import java.util.function.Supplier;
2425
import java.util.stream.Collectors;
@@ -35,6 +36,7 @@
3536
import software.amazon.awssdk.http.auth.spi.scheme.AuthScheme;
3637
import software.amazon.awssdk.http.auth.spi.scheme.AuthSchemeOption;
3738
import software.amazon.awssdk.http.auth.spi.signer.HttpSigner;
39+
import software.amazon.awssdk.http.auth.spi.signer.SignerProperty;
3840
import software.amazon.awssdk.identity.spi.AwsCredentialsIdentity;
3941
import software.amazon.awssdk.identity.spi.Identity;
4042
import software.amazon.awssdk.identity.spi.IdentityProvider;
@@ -137,9 +139,23 @@ public static <T extends Identity> SelectedAuthScheme<T> mergePreExistingAuthSch
137139
return selectedAuthScheme;
138140
}
139141

142+
SelectedAuthScheme<?> beforeInterceptors =
143+
executionAttributes.getAttribute(SdkInternalExecutionAttribute.AUTH_SCHEME_BEFORE_INTERCEPTORS);
144+
140145
AuthSchemeOption.Builder mergedOption = selectedAuthScheme.authSchemeOption().toBuilder();
146+
147+
existingAuthScheme.authSchemeOption().forEachSignerProperty(new AuthSchemeOption.SignerPropertyConsumer() {
148+
@Override
149+
public <S> void accept(SignerProperty<S> key, S value) {
150+
if (wasModifiedByInterceptor(beforeInterceptors, key, value)) {
151+
mergedOption.putSignerProperty(key, value);
152+
} else {
153+
mergedOption.putSignerPropertyIfAbsent(key, value);
154+
}
155+
}
156+
});
157+
141158
existingAuthScheme.authSchemeOption().forEachIdentityProperty(mergedOption::putIdentityPropertyIfAbsent);
142-
existingAuthScheme.authSchemeOption().forEachSignerProperty(mergedOption::putSignerPropertyIfAbsent);
143159

144160
return new SelectedAuthScheme<>(
145161
selectedAuthScheme.identity(),
@@ -148,6 +164,57 @@ public static <T extends Identity> SelectedAuthScheme<T> mergePreExistingAuthSch
148164
);
149165
}
150166

167+
private static <T> boolean wasModifiedByInterceptor(SelectedAuthScheme<?> beforeInterceptors,
168+
SignerProperty<T> key, T currentValue) {
169+
if (beforeInterceptors == null) {
170+
return true;
171+
}
172+
T originalValue = beforeInterceptors.authSchemeOption().signerProperty(key);
173+
return !Objects.equals(originalValue, currentValue);
174+
}
175+
176+
/**
177+
* Re-applies interceptor-modified signer properties onto the current auth scheme.
178+
* Called after endpoint resolution, which may have overwritten properties that interceptors set.
179+
*/
180+
public static void applyInterceptorModifiedProperties(SelectedAuthScheme<?> currentScheme,
181+
SelectedAuthScheme<?> beforeInterceptors,
182+
SelectedAuthScheme<?> afterInterceptors,
183+
ExecutionAttributes attrs) {
184+
if (afterInterceptors == null) {
185+
return;
186+
}
187+
doApplyInterceptorModifiedProperties(currentScheme, beforeInterceptors, afterInterceptors, attrs);
188+
}
189+
190+
@SuppressWarnings("unchecked")
191+
private static <T extends Identity> void doApplyInterceptorModifiedProperties(
192+
SelectedAuthScheme<T> currentScheme,
193+
SelectedAuthScheme<?> beforeInterceptors,
194+
SelectedAuthScheme<?> afterInterceptors,
195+
ExecutionAttributes attrs) {
196+
197+
AuthSchemeOption.Builder mergedOption = currentScheme.authSchemeOption().toBuilder();
198+
boolean[] changed = {false};
199+
200+
afterInterceptors.authSchemeOption().forEachSignerProperty(new AuthSchemeOption.SignerPropertyConsumer() {
201+
@Override
202+
public <S> void accept(SignerProperty<S> key, S value) {
203+
if (wasModifiedByInterceptor(beforeInterceptors, key, value)) {
204+
mergedOption.putSignerProperty(key, value);
205+
changed[0] = true;
206+
}
207+
}
208+
});
209+
210+
if (changed[0]) {
211+
attrs.putAttribute(SdkInternalExecutionAttribute.SELECTED_AUTH_SCHEME,
212+
new SelectedAuthScheme<>(currentScheme.identity(),
213+
currentScheme.signer(),
214+
mergedOption.build()));
215+
}
216+
}
217+
151218
private static <T extends Identity> SelectedAuthScheme<T> trySelectAuthScheme(
152219
AuthSchemeOption authOption,
153220
AuthScheme<T> authScheme,

core/sdk-core/src/main/java/software/amazon/awssdk/core/interceptor/SdkInternalExecutionAttribute.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ public final class SdkInternalExecutionAttribute extends SdkExecutionAttribute {
177177
public static final ExecutionAttribute<IdentityProviderUpdater> IDENTITY_PROVIDER_UPDATER =
178178
new ExecutionAttribute<>("IdentityProviderUpdater");
179179

180+
/**
180181
/**
181182
* Callback to resolve auth scheme options from the (possibly modified) request.
182183
* Called by AuthSchemeResolutionStage after interceptors have run.
@@ -204,6 +205,22 @@ public final class SdkInternalExecutionAttribute extends SdkExecutionAttribute {
204205
public static final ExecutionAttribute<SelectedAuthScheme<?>> SELECTED_AUTH_SCHEME =
205206
new ExecutionAttribute<>("SelectedAuthScheme");
206207

208+
/**
209+
* Snapshot of {@link #SELECTED_AUTH_SCHEME} taken before execution interceptors run.
210+
* Used by {@code AuthSchemeResolver#mergePreExistingAuthSchemeProperties} to detect which signer properties
211+
* were explicitly modified by interceptors (and should therefore override the freshly-resolved values).
212+
*/
213+
public static final ExecutionAttribute<SelectedAuthScheme<?>> AUTH_SCHEME_BEFORE_INTERCEPTORS =
214+
new ExecutionAttribute<>("AuthSchemeBeforeInterceptors");
215+
216+
/**
217+
* Snapshot of {@link #SELECTED_AUTH_SCHEME} taken after interceptors run but before auth scheme resolution.
218+
* Together with {@link #AUTH_SCHEME_BEFORE_INTERCEPTORS}, this allows detecting which signer properties
219+
* were explicitly modified by interceptors so they can be re-applied after endpoint resolution.
220+
*/
221+
public static final ExecutionAttribute<SelectedAuthScheme<?>> AUTH_SCHEME_AFTER_INTERCEPTORS =
222+
new ExecutionAttribute<>("AuthSchemeAfterInterceptors");
223+
207224
/**
208225
* The supported compression algorithms for an operation, and whether the operation is streaming or not.
209226
*/

core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/AuthSchemeResolutionStage.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@ public SdkHttpFullRequest.Builder execute(SdkHttpFullRequest.Builder request, Re
7676
SelectedAuthScheme<? extends Identity> selectedAuthScheme =
7777
AuthSchemeResolver.selectAuthScheme(authOptions, authSchemes, identityProviders, metricCollector);
7878

79+
executionAttributes.putAttribute(SdkInternalExecutionAttribute.AUTH_SCHEME_AFTER_INTERCEPTORS,
80+
executionAttributes.getAttribute(SdkInternalExecutionAttribute.SELECTED_AUTH_SCHEME));
81+
7982
selectedAuthScheme = AuthSchemeResolver.mergePreExistingAuthSchemeProperties(selectedAuthScheme, executionAttributes);
8083

8184
executionAttributes.putAttribute(SdkInternalExecutionAttribute.SELECTED_AUTH_SCHEME, selectedAuthScheme);

core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/EndpointResolutionStage.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
import software.amazon.awssdk.annotations.SdkInternalApi;
2323
import software.amazon.awssdk.core.ClientEndpointProvider;
2424
import software.amazon.awssdk.core.SdkRequest;
25+
import software.amazon.awssdk.core.SelectedAuthScheme;
26+
import software.amazon.awssdk.core.http.auth.AuthSchemeResolver;
2527
import software.amazon.awssdk.core.interceptor.ExecutionAttributes;
2628
import software.amazon.awssdk.core.interceptor.SdkExecutionAttribute;
2729
import software.amazon.awssdk.core.interceptor.SdkInternalExecutionAttribute;
@@ -70,6 +72,8 @@ public SdkHttpFullRequest.Builder execute(SdkHttpFullRequest.Builder request, Re
7072
Endpoint endpoint = resolver.resolve(sdkRequest, attrs);
7173
Duration resolveEndpointDuration = Duration.ofNanos(System.nanoTime() - resolveEndpointStart);
7274

75+
reapplyInterceptorModifiedAuthProperties(attrs);
76+
7377
MetricCollector metricCollector = attrs.getAttribute(SdkExecutionAttribute.API_CALL_METRIC_COLLECTOR);
7478
if (metricCollector != null) {
7579
metricCollector.reportMetric(CoreMetric.ENDPOINT_RESOLVE_DURATION, resolveEndpointDuration);
@@ -140,4 +144,21 @@ private static String combinePath(String clientEndpointPath, String requestPath,
140144
String requestPathWithClientPathRemoved = StringUtils.replaceOnce(requestPath, clientEndpointPath, "");
141145
return SdkHttpUtils.appendUri(resolvedUriPath, requestPathWithClientPathRemoved);
142146
}
147+
148+
private static void reapplyInterceptorModifiedAuthProperties(ExecutionAttributes attrs) {
149+
SelectedAuthScheme<?> currentScheme = attrs.getAttribute(SdkInternalExecutionAttribute.SELECTED_AUTH_SCHEME);
150+
if (currentScheme == null) {
151+
return;
152+
}
153+
SelectedAuthScheme<?> beforeInterceptors =
154+
attrs.getAttribute(SdkInternalExecutionAttribute.AUTH_SCHEME_BEFORE_INTERCEPTORS);
155+
156+
SelectedAuthScheme<?> afterInterceptors =
157+
attrs.getAttribute(SdkInternalExecutionAttribute.AUTH_SCHEME_AFTER_INTERCEPTORS);
158+
if (afterInterceptors == null) {
159+
return;
160+
}
161+
162+
AuthSchemeResolver.applyInterceptorModifiedProperties(currentScheme, beforeInterceptors, afterInterceptors, attrs);
163+
}
143164
}

services/s3/src/test/java/software/amazon/awssdk/services/s3/ExecutionAttributeBackwardsCompatibilityTest.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,7 @@ public void canSetSignerExecutionAttributes_beforeExecution() {
7272
public void beforeExecution(Context.BeforeExecution context, ExecutionAttributes executionAttributes) {
7373
attributeModifications.accept(executionAttributes);
7474
}
75-
},
76-
AwsSignerExecutionAttribute.SERVICE_SIGNING_NAME, // Endpoint rules override signing name
77-
AwsSignerExecutionAttribute.SIGNING_REGION, // Endpoint rules override signing region
78-
AwsSignerExecutionAttribute.SIGNER_DOUBLE_URL_ENCODE); // Endpoint rules override double-url-encode
75+
});
7976
}
8077

8178
@Test

0 commit comments

Comments
 (0)