software.amazon.awssdk.services.s3.internal.presigner.DefaultS3Presigner Maven / Gradle / Ivy
/*
* 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.services.s3.internal.presigner;
import static java.util.stream.Collectors.toMap;
import static software.amazon.awssdk.auth.signer.AwsSignerExecutionAttribute.PRESIGNER_EXPIRATION;
import static software.amazon.awssdk.utils.CollectionUtils.mergeLists;
import static software.amazon.awssdk.utils.FunctionalUtils.invokeSafely;
import java.net.URI;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Stream;
import software.amazon.awssdk.annotations.SdkInternalApi;
import software.amazon.awssdk.auth.credentials.AwsCredentials;
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
import software.amazon.awssdk.auth.signer.AwsS3V4Signer;
import software.amazon.awssdk.auth.signer.AwsSignerExecutionAttribute;
import software.amazon.awssdk.awscore.AwsExecutionAttribute;
import software.amazon.awssdk.awscore.AwsRequestOverrideConfiguration;
import software.amazon.awssdk.awscore.client.builder.AwsDefaultClientBuilder;
import software.amazon.awssdk.awscore.endpoint.DefaultServiceEndpointBuilder;
import software.amazon.awssdk.awscore.presigner.PresignRequest;
import software.amazon.awssdk.awscore.presigner.PresignedRequest;
import software.amazon.awssdk.core.ClientType;
import software.amazon.awssdk.core.RequestOverrideConfiguration;
import software.amazon.awssdk.core.SdkBytes;
import software.amazon.awssdk.core.SdkRequest;
import software.amazon.awssdk.core.client.builder.SdkDefaultClientBuilder;
import software.amazon.awssdk.core.client.config.SdkClientConfiguration;
import software.amazon.awssdk.core.client.config.SdkClientOption;
import software.amazon.awssdk.core.http.ExecutionContext;
import software.amazon.awssdk.core.interceptor.ClasspathInterceptorChainFactory;
import software.amazon.awssdk.core.interceptor.ExecutionAttributes;
import software.amazon.awssdk.core.interceptor.ExecutionInterceptor;
import software.amazon.awssdk.core.interceptor.ExecutionInterceptorChain;
import software.amazon.awssdk.core.interceptor.InterceptorContext;
import software.amazon.awssdk.core.interceptor.SdkExecutionAttribute;
import software.amazon.awssdk.core.interceptor.SdkInternalExecutionAttribute;
import software.amazon.awssdk.core.signer.Presigner;
import software.amazon.awssdk.core.signer.Signer;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.http.ContentStreamProvider;
import software.amazon.awssdk.http.SdkHttpFullRequest;
import software.amazon.awssdk.http.SdkHttpMethod;
import software.amazon.awssdk.http.SdkHttpRequest;
import software.amazon.awssdk.protocols.xml.AwsS3ProtocolFactory;
import software.amazon.awssdk.services.s3.S3Configuration;
import software.amazon.awssdk.services.s3.model.AbortMultipartUploadRequest;
import software.amazon.awssdk.services.s3.model.CompleteMultipartUploadRequest;
import software.amazon.awssdk.services.s3.model.CreateMultipartUploadRequest;
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
import software.amazon.awssdk.services.s3.model.UploadPartRequest;
import software.amazon.awssdk.services.s3.presigner.S3Presigner;
import software.amazon.awssdk.services.s3.presigner.model.AbortMultipartUploadPresignRequest;
import software.amazon.awssdk.services.s3.presigner.model.CompleteMultipartUploadPresignRequest;
import software.amazon.awssdk.services.s3.presigner.model.CreateMultipartUploadPresignRequest;
import software.amazon.awssdk.services.s3.presigner.model.GetObjectPresignRequest;
import software.amazon.awssdk.services.s3.presigner.model.PresignedAbortMultipartUploadRequest;
import software.amazon.awssdk.services.s3.presigner.model.PresignedCompleteMultipartUploadRequest;
import software.amazon.awssdk.services.s3.presigner.model.PresignedCreateMultipartUploadRequest;
import software.amazon.awssdk.services.s3.presigner.model.PresignedGetObjectRequest;
import software.amazon.awssdk.services.s3.presigner.model.PresignedPutObjectRequest;
import software.amazon.awssdk.services.s3.presigner.model.PresignedUploadPartRequest;
import software.amazon.awssdk.services.s3.presigner.model.PutObjectPresignRequest;
import software.amazon.awssdk.services.s3.presigner.model.UploadPartPresignRequest;
import software.amazon.awssdk.services.s3.transform.AbortMultipartUploadRequestMarshaller;
import software.amazon.awssdk.services.s3.transform.CompleteMultipartUploadRequestMarshaller;
import software.amazon.awssdk.services.s3.transform.CreateMultipartUploadRequestMarshaller;
import software.amazon.awssdk.services.s3.transform.GetObjectRequestMarshaller;
import software.amazon.awssdk.services.s3.transform.PutObjectRequestMarshaller;
import software.amazon.awssdk.services.s3.transform.UploadPartRequestMarshaller;
import software.amazon.awssdk.utils.IoUtils;
import software.amazon.awssdk.utils.Validate;
/**
* The default implementation of the {@link S3Presigner} interface.
*/
@SdkInternalApi
public final class DefaultS3Presigner extends DefaultSdkPresigner implements S3Presigner {
private static final AwsS3V4Signer DEFAULT_SIGNER = AwsS3V4Signer.create();
private static final S3Configuration DEFAULT_S3_CONFIGURATION = S3Configuration.builder()
.checksumValidationEnabled(false)
.build();
private static final String SERVICE_NAME = "s3";
private static final String SIGNING_NAME = "s3";
private final S3Configuration serviceConfiguration;
private final List clientInterceptors;
private final GetObjectRequestMarshaller getObjectRequestMarshaller;
private final PutObjectRequestMarshaller putObjectRequestMarshaller;
private final CreateMultipartUploadRequestMarshaller createMultipartUploadRequestMarshaller;
private final UploadPartRequestMarshaller uploadPartRequestMarshaller;
private final CompleteMultipartUploadRequestMarshaller completeMultipartUploadRequestMarshaller;
private final AbortMultipartUploadRequestMarshaller abortMultipartUploadRequestMarshaller;
private DefaultS3Presigner(Builder b) {
super(b);
this.serviceConfiguration = b.serviceConfiguration != null ? b.serviceConfiguration : DEFAULT_S3_CONFIGURATION;
this.clientInterceptors = initializeInterceptors();
// Copied from DefaultS3Client#init
AwsS3ProtocolFactory protocolFactory = AwsS3ProtocolFactory.builder()
.clientConfiguration(createClientConfiguration())
.build();
// Copied from DefaultS3Client#getObject
this.getObjectRequestMarshaller = new GetObjectRequestMarshaller(protocolFactory);
// Copied from DefaultS3Client#putObject
this.putObjectRequestMarshaller = new PutObjectRequestMarshaller(protocolFactory);
// Copied from DefaultS3Client#createMultipartUpload
this.createMultipartUploadRequestMarshaller = new CreateMultipartUploadRequestMarshaller(protocolFactory);
// Copied from DefaultS3Client#uploadPart
this.uploadPartRequestMarshaller = new UploadPartRequestMarshaller(protocolFactory);
// Copied from DefaultS3Client#completeMultipartUpload
this.completeMultipartUploadRequestMarshaller = new CompleteMultipartUploadRequestMarshaller(protocolFactory);
// Copied from DefaultS3Client#abortMultipartUpload
this.abortMultipartUploadRequestMarshaller = new AbortMultipartUploadRequestMarshaller(protocolFactory);
}
public static S3Presigner.Builder builder() {
return new Builder();
}
/**
* Copied from {@code DefaultS3BaseClientBuilder} and {@link SdkDefaultClientBuilder}.
*/
private List initializeInterceptors() {
ClasspathInterceptorChainFactory interceptorFactory = new ClasspathInterceptorChainFactory();
List s3Interceptors =
interceptorFactory.getInterceptors("software/amazon/awssdk/services/s3/execution.interceptors");
return mergeLists(interceptorFactory.getGlobalInterceptors(), s3Interceptors);
}
/**
* Copied from {@link AwsDefaultClientBuilder}.
*/
private SdkClientConfiguration createClientConfiguration() {
if (endpointOverride() != null) {
return SdkClientConfiguration.builder()
.option(SdkClientOption.ENDPOINT, endpointOverride())
.option(SdkClientOption.ENDPOINT_OVERRIDDEN, true)
.build();
} else {
URI defaultEndpoint = new DefaultServiceEndpointBuilder(SERVICE_NAME, "https").withRegion(region())
.getServiceEndpoint();
return SdkClientConfiguration.builder()
.option(SdkClientOption.ENDPOINT, defaultEndpoint)
.build();
}
}
@Override
public PresignedGetObjectRequest presignGetObject(GetObjectPresignRequest request) {
return presign(PresignedGetObjectRequest.builder(),
request,
request.getObjectRequest(),
GetObjectRequest.class,
getObjectRequestMarshaller::marshall,
"GetObject")
.build();
}
@Override
public PresignedPutObjectRequest presignPutObject(PutObjectPresignRequest request) {
return presign(PresignedPutObjectRequest.builder(),
request,
request.putObjectRequest(),
PutObjectRequest.class,
putObjectRequestMarshaller::marshall,
"PutObject")
.build();
}
@Override
public PresignedCreateMultipartUploadRequest presignCreateMultipartUpload(CreateMultipartUploadPresignRequest request) {
return presign(PresignedCreateMultipartUploadRequest.builder(),
request,
request.createMultipartUploadRequest(),
CreateMultipartUploadRequest.class,
createMultipartUploadRequestMarshaller::marshall,
"CreateMultipartUpload")
.build();
}
@Override
public PresignedUploadPartRequest presignUploadPart(UploadPartPresignRequest request) {
return presign(PresignedUploadPartRequest.builder(),
request,
request.uploadPartRequest(),
UploadPartRequest.class,
uploadPartRequestMarshaller::marshall,
"UploadPart")
.build();
}
@Override
public PresignedCompleteMultipartUploadRequest presignCompleteMultipartUpload(CompleteMultipartUploadPresignRequest request) {
return presign(PresignedCompleteMultipartUploadRequest.builder(),
request,
request.completeMultipartUploadRequest(),
CompleteMultipartUploadRequest.class,
completeMultipartUploadRequestMarshaller::marshall,
"CompleteMultipartUpload")
.build();
}
@Override
public PresignedAbortMultipartUploadRequest presignAbortMultipartUpload(AbortMultipartUploadPresignRequest request) {
return presign(PresignedAbortMultipartUploadRequest.builder(),
request,
request.abortMultipartUploadRequest(),
AbortMultipartUploadRequest.class,
abortMultipartUploadRequestMarshaller::marshall,
"AbortMultipartUpload")
.build();
}
protected S3Configuration serviceConfiguration() {
return serviceConfiguration;
}
/**
* Generate a {@link PresignedRequest} from a {@link PresignedRequest} and {@link SdkRequest}.
*/
private T presign(T presignedRequest,
PresignRequest presignRequest,
SdkRequest requestToPresign,
Class requestToPresignType,
Function requestMarshaller,
String operationName) {
ExecutionContext execCtx = createExecutionContext(presignRequest, requestToPresign, operationName);
callBeforeExecutionHooks(execCtx);
callModifyRequestHooksAndUpdateContext(execCtx);
callBeforeMarshallingHooks(execCtx);
marshalRequestAndUpdateContext(execCtx, requestToPresignType, requestMarshaller);
callAfterMarshallingHooks(execCtx);
addRequestLevelHeadersAndQueryParameters(execCtx);
callModifyHttpRequestHooksAndUpdateContext(execCtx);
SdkHttpFullRequest httpRequest = getHttpFullRequest(execCtx);
SdkHttpFullRequest signedHttpRequest = presignRequest(execCtx, httpRequest);
initializePresignedRequest(presignedRequest, execCtx, signedHttpRequest);
return presignedRequest;
}
/**
* Creates an execution context from the provided requests information.
*/
private ExecutionContext createExecutionContext(PresignRequest presignRequest, SdkRequest sdkRequest, String operationName) {
AwsCredentialsProvider clientCredentials = credentialsProvider();
AwsCredentialsProvider credentialsProvider = sdkRequest.overrideConfiguration()
.filter(c -> c instanceof AwsRequestOverrideConfiguration)
.map(c -> (AwsRequestOverrideConfiguration) c)
.flatMap(AwsRequestOverrideConfiguration::credentialsProvider)
.orElse(clientCredentials);
Signer signer = sdkRequest.overrideConfiguration().flatMap(RequestOverrideConfiguration::signer).orElse(DEFAULT_SIGNER);
Instant signatureExpiration = Instant.now().plus(presignRequest.signatureDuration());
AwsCredentials credentials = credentialsProvider.resolveCredentials();
Validate.validState(credentials != null, "Credential providers must never return null.");
ExecutionAttributes executionAttributes = new ExecutionAttributes()
.putAttribute(AwsSignerExecutionAttribute.AWS_CREDENTIALS, credentials)
.putAttribute(AwsSignerExecutionAttribute.SERVICE_SIGNING_NAME, SIGNING_NAME)
.putAttribute(AwsExecutionAttribute.AWS_REGION, region())
.putAttribute(AwsSignerExecutionAttribute.SIGNING_REGION, region())
.putAttribute(SdkInternalExecutionAttribute.IS_FULL_DUPLEX, false)
.putAttribute(SdkExecutionAttribute.CLIENT_TYPE, ClientType.SYNC)
.putAttribute(SdkExecutionAttribute.SERVICE_NAME, SERVICE_NAME)
.putAttribute(SdkExecutionAttribute.OPERATION_NAME, operationName)
.putAttribute(AwsSignerExecutionAttribute.SERVICE_CONFIG, serviceConfiguration())
.putAttribute(PRESIGNER_EXPIRATION, signatureExpiration);
ExecutionInterceptorChain executionInterceptorChain = new ExecutionInterceptorChain(clientInterceptors);
return ExecutionContext.builder()
.interceptorChain(executionInterceptorChain)
.interceptorContext(InterceptorContext.builder()
.request(sdkRequest)
.build())
.executionAttributes(executionAttributes)
.signer(signer)
.build();
}
/**
* Call the before-execution interceptor hooks.
*/
private void callBeforeExecutionHooks(ExecutionContext execCtx) {
execCtx.interceptorChain().beforeExecution(execCtx.interceptorContext(), execCtx.executionAttributes());
}
/**
* Call the modify-request interceptor hooks and update the execution context.
*/
private void callModifyRequestHooksAndUpdateContext(ExecutionContext execCtx) {
execCtx.interceptorContext(execCtx.interceptorChain().modifyRequest(execCtx.interceptorContext(),
execCtx.executionAttributes()));
}
/**
* Call the before-marshalling interceptor hooks.
*/
private void callBeforeMarshallingHooks(ExecutionContext execCtx) {
execCtx.interceptorChain().beforeMarshalling(execCtx.interceptorContext(), execCtx.executionAttributes());
}
/**
* Marshal the request and update the execution context with the result.
*/
private void marshalRequestAndUpdateContext(ExecutionContext execCtx,
Class requestType,
Function requestMarshaller) {
T sdkRequest = Validate.isInstanceOf(requestType, execCtx.interceptorContext().request(),
"Interceptor generated unsupported type (%s) when %s was expected.",
execCtx.interceptorContext().request().getClass(), requestType);
SdkHttpFullRequest marshalledRequest = requestMarshaller.apply(sdkRequest);
// TODO: The core SDK doesn't put the request body into the interceptor context. That should be fixed.
Optional requestBody = marshalledRequest.contentStreamProvider()
.map(ContentStreamProvider::newStream)
.map(is -> invokeSafely(() -> IoUtils.toByteArray(is)))
.map(RequestBody::fromBytes);
execCtx.interceptorContext(execCtx.interceptorContext().copy(r -> r.httpRequest(marshalledRequest)
.requestBody(requestBody.orElse(null))));
}
/**
* Call the after-marshalling interceptor hooks.
*/
private void callAfterMarshallingHooks(ExecutionContext execCtx) {
execCtx.interceptorChain().afterMarshalling(execCtx.interceptorContext(), execCtx.executionAttributes());
}
/**
* Update the provided HTTP request by adding any HTTP headers or query parameters specified as part of the
* {@link SdkRequest}.
*/
private void addRequestLevelHeadersAndQueryParameters(ExecutionContext execCtx) {
SdkHttpRequest httpRequest = execCtx.interceptorContext().httpRequest();
SdkRequest sdkRequest = execCtx.interceptorContext().request();
SdkHttpRequest updatedHttpRequest =
httpRequest.toBuilder()
.applyMutation(b -> addRequestLevelHeaders(b, sdkRequest))
.applyMutation(b -> addRequestLeveQueryParameters(b, sdkRequest))
.build();
execCtx.interceptorContext(execCtx.interceptorContext().copy(c -> c.httpRequest(updatedHttpRequest)));
}
private void addRequestLevelHeaders(SdkHttpRequest.Builder builder, SdkRequest request) {
request.overrideConfiguration().ifPresent(overrideConfig -> {
if (!overrideConfig.headers().isEmpty()) {
overrideConfig.headers().forEach(builder::putHeader);
}
});
}
private void addRequestLeveQueryParameters(SdkHttpRequest.Builder builder, SdkRequest request) {
request.overrideConfiguration().ifPresent(overrideConfig -> {
if (!overrideConfig.rawQueryParameters().isEmpty()) {
overrideConfig.rawQueryParameters().forEach(builder::putRawQueryParameter);
}
});
}
/**
* Call the after-marshalling interceptor hooks and return the HTTP request that should be pre-signed.
*/
private void callModifyHttpRequestHooksAndUpdateContext(ExecutionContext execCtx) {
execCtx.interceptorContext(execCtx.interceptorChain().modifyHttpRequestAndHttpContent(execCtx.interceptorContext(),
execCtx.executionAttributes()));
}
/**
* Get the HTTP full request from the execution context.
*/
private SdkHttpFullRequest getHttpFullRequest(ExecutionContext execCtx) {
SdkHttpRequest requestFromInterceptor = execCtx.interceptorContext().httpRequest();
Optional bodyFromInterceptor = execCtx.interceptorContext().requestBody();
return SdkHttpFullRequest.builder()
.method(requestFromInterceptor.method())
.protocol(requestFromInterceptor.protocol())
.host(requestFromInterceptor.host())
.port(requestFromInterceptor.port())
.encodedPath(requestFromInterceptor.encodedPath())
.rawQueryParameters(requestFromInterceptor.rawQueryParameters())
.headers(requestFromInterceptor.headers())
.contentStreamProvider(bodyFromInterceptor.map(RequestBody::contentStreamProvider)
.orElse(null))
.build();
}
/**
* Presign the provided HTTP request.
*/
private SdkHttpFullRequest presignRequest(ExecutionContext execCtx, SdkHttpFullRequest request) {
Presigner presigner = Validate.isInstanceOf(Presigner.class, execCtx.signer(),
"Configured signer (%s) does not support presigning (must implement %s).",
execCtx.signer().getClass(), Presigner.class);
return presigner.presign(request, execCtx.executionAttributes());
}
/**
* Initialize the provided presigned request.
*/
private void initializePresignedRequest(PresignedRequest.Builder presignedRequest,
ExecutionContext execCtx,
SdkHttpFullRequest signedHttpRequest) {
SdkBytes signedPayload = signedHttpRequest.contentStreamProvider()
.map(p -> SdkBytes.fromInputStream(p.newStream()))
.orElse(null);
List signedHeadersQueryParam = signedHttpRequest.rawQueryParameters().get("X-Amz-SignedHeaders");
Validate.validState(signedHeadersQueryParam != null,
"Only SigV4 presigners are supported at this time, but the configured "
+ "presigner (%s) did not seem to generate a SigV4 signature.", execCtx.signer());
Map> signedHeaders =
signedHeadersQueryParam.stream()
.flatMap(h -> Stream.of(h.split(";")))
.collect(toMap(h -> h, h -> signedHttpRequest.firstMatchingHeader(h)
.map(Collections::singletonList)
.orElseGet(ArrayList::new)));
boolean isBrowserExecutable = signedHttpRequest.method() == SdkHttpMethod.GET &&
signedPayload == null &&
(signedHeaders.isEmpty() ||
(signedHeaders.size() == 1 && signedHeaders.containsKey("host")));
presignedRequest.expiration(execCtx.executionAttributes().getAttribute(PRESIGNER_EXPIRATION))
.isBrowserExecutable(isBrowserExecutable)
.httpRequest(signedHttpRequest)
.signedHeaders(signedHeaders)
.signedPayload(signedPayload);
}
@SdkInternalApi
public static final class Builder extends DefaultSdkPresigner.Builder
implements S3Presigner.Builder {
private S3Configuration serviceConfiguration;
private Builder() {
}
/**
* Allows providing a custom S3 serviceConfiguration by providing a {@link S3Configuration} object;
*
* Note: chunkedEncodingEnabled and checksumValidationEnabled do not apply to presigned requests.
*
* @param serviceConfiguration {@link S3Configuration}
* @return this Builder
*/
public Builder serviceConfiguration(S3Configuration serviceConfiguration) {
this.serviceConfiguration = serviceConfiguration;
return this;
}
@Override
public S3Presigner build() {
return new DefaultS3Presigner(this);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy