Skip to content

Commit

Permalink
Improve SRA signer user experience, with documentation and helper met…
Browse files Browse the repository at this point in the history
…hods. (#5270)

1. Deprecate old signer interfaces, directing to new interfaces.
2. Add Javadoc for new v4 and v4a signers.
3. Add utility methods for creating ContentStreamProviders.
  • Loading branch information
millems authored Jun 6, 2024
1 parent 44a3e01 commit 5a9bd22
Show file tree
Hide file tree
Showing 24 changed files with 531 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,13 @@
* <p/>
* See <a href="https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-authenticating-requests.html">
* Amazon S3 Sigv4 documentation</a> for more detailed information.
*
* @deprecated Use {@code software.amazon.awssdk.http.auth.aws.signer.AwsV4aHttpSigner} from the 'http-auth-aws' module.
*/
@SdkPublicApi
@Immutable
@ThreadSafe
@Deprecated
public interface AwsCrtS3V4aSigner extends Signer, Presigner {

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,13 @@
* (Common RunTime) library.
* <p/>
* In CRT signing, payload signing is the default unless an override value is specified.
*
* @deprecated Use {@code software.amazon.awssdk.http.auth.aws.signer.AwsV4aHttpSigner} from the 'http-auth-aws' module.
*/
@SdkPublicApi
@Immutable
@ThreadSafe
@Deprecated
public interface AwsCrtV4aSigner extends Signer, Presigner {

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@
/**
* AWS Signature Version 4 signer that can include contents of an asynchronous request body into the signature
* calculation.
*
* @deprecated Use {@code software.amazon.awssdk.http.auth.aws.signer.AwsV4HttpSigner} from the 'http-auth-aws' module.
*/
@Deprecated
@SdkPublicApi
public final class AsyncAws4Signer extends BaseAws4Signer implements AsyncSigner {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@

/**
* Signer implementation that signs requests with the AWS4 signing protocol.
*
* @deprecated Use {@code software.amazon.awssdk.http.auth.aws.signer.AwsV4HttpSigner} from the 'http-auth-aws' module.
*/
@Deprecated
@SdkPublicApi
public final class Aws4Signer extends BaseAws4Signer {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,11 @@
* <p>
* Payloads are still signed for requests over HTTP to preserve the request
* integrity over a non-secure transport.
*
* @deprecated Use {@code software.amazon.awssdk.http.auth.aws.signer.AwsV4HttpSigner} from the 'http-auth-aws' module.
*/
@SdkPublicApi
@Deprecated
public final class Aws4UnsignedPayloadSigner extends BaseAws4Signer {

public static final String UNSIGNED_PAYLOAD = "UNSIGNED-PAYLOAD";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@

/**
* AWS4 signer implementation for AWS S3
*
* @deprecated Use {@code software.amazon.awssdk.http.auth.aws.signer.AwsV4HttpSigner} from the 'http-auth-aws' module.
*/
@Deprecated
@SdkPublicApi
public final class AwsS3V4Signer extends AbstractAwsS3V4Signer {
private AwsS3V4Signer() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import software.amazon.awssdk.annotations.SdkProtectedApi;
import software.amazon.awssdk.auth.signer.internal.BaseEventStreamAsyncAws4Signer;

@Deprecated
@SdkProtectedApi
public final class EventStreamAws4Signer extends BaseEventStreamAsyncAws4Signer {
private EventStreamAws4Signer() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
/**
* Utility class for instantiating signers only if they're available on the class path.
*/
@Deprecated
@SdkProtectedApi
public final class SignerLoader {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
* Abstract base class for concrete implementations of Aws4 signers.
*/
@SdkInternalApi
@Deprecated
public abstract class BaseAws4Signer extends AbstractAws4Signer<Aws4SignerParams, Aws4PresignerParams> {

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,11 @@

/**
* A {@link Signer} that will sign a request with Bearer token authorization.
*
* @deprecated Use {@code software.amazon.awssdk.http.auth.signer.BearerHttpSigner} from the 'http-auth' module.
*/
@SdkPublicApi
@Deprecated
public final class BearerTokenSigner implements Signer {

private static final String BEARER_LABEL = "Bearer";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,21 +29,26 @@
@SdkPublicApi
public interface AwsV4FamilyHttpSigner<T extends Identity> extends HttpSigner<T> {
/**
* The name of the AWS service. This property is required.
* The name of the AWS service. This property is required. This value can be found in the service documentation or
* on the service client itself (e.g. {@code S3Client.SERVICE_NAME}).
*/
SignerProperty<String> SERVICE_SIGNING_NAME =
SignerProperty.create(AwsV4FamilyHttpSigner.class, "ServiceSigningName");

/**
* A boolean to indicate whether to double url-encode the resource path when constructing the canonical request. This property
* defaults to true.
* <p>
* Note: S3 requires this value to be set to 'false' to prevent signature mismatch errors for certain paths.
*/
SignerProperty<Boolean> DOUBLE_URL_ENCODE =
SignerProperty.create(AwsV4FamilyHttpSigner.class, "DoubleUrlEncode");

/**
* A boolean to indicate whether the resource path should be "normalized" according to RFC3986 when constructing the canonical
* request. This property defaults to true.
* <p>
* Note: S3 requires this value to be set to 'false' to prevent signature mismatch errors for certain paths.
*/
SignerProperty<Boolean> NORMALIZE_PATH =
SignerProperty.create(AwsV4FamilyHttpSigner.class, "NormalizePath");
Expand All @@ -64,13 +69,21 @@ public interface AwsV4FamilyHttpSigner<T extends Identity> extends HttpSigner<T>
/**
* Whether to indicate that a payload is signed or not. This property defaults to true. This can be set false to disable
* payload signing.
* <p>
* When this value is true and {@link #CHUNK_ENCODING_ENABLED} is false, the whole payload must be read to generate
* the payload signature. For very large payloads, this could impact memory usage and call latency. Some services
* support this value being disabled, especially over HTTPS where SSL provides some of its own protections against
* payload tampering.
*/
SignerProperty<Boolean> PAYLOAD_SIGNING_ENABLED =
SignerProperty.create(AwsV4FamilyHttpSigner.class, "PayloadSigningEnabled");

/**
* Whether to indicate that a payload is chunk-encoded or not. This property defaults to false. This can be set true to
* enable the `aws-chunk` content-encoding
* enable the `aws-chunked` content-encoding.
* <p>
* Only some services support this value being set to true, but for those services it can prevent the need to read
* the whole payload before writing when {@link #PAYLOAD_SIGNING_ENABLED} is true.
*/
SignerProperty<Boolean> CHUNK_ENCODING_ENABLED =
SignerProperty.create(AwsV4FamilyHttpSigner.class, "ChunkEncodingEnabled");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,67 @@
import software.amazon.awssdk.identity.spi.AwsCredentialsIdentity;

/**
* An {@link HttpSigner} that will sign a request using an AWS credentials {@link AwsCredentialsIdentity}).
* An {@link HttpSigner} that will use the AWS V4 signing algorithm to sign a request using an
* {@link AwsCredentialsIdentity}).
*
* <p>
* The process for signing requests to send to AWS services is documented
* The steps performed by this signer are documented
* <a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_aws-signing.html">here</a>.
*
* <h2>Using the AwsV4HttpSigner</h2>
* <p>
* <b>Sign an HTTP request and send it to a service.</b>
* <p>
* {@snippet :
* AwsV4HttpSigner signer = AwsV4HttpSigner.create();
*
* // Specify AWS credentials. Credential providers that are used by the SDK by default are
* // available in the module "auth" (e.g. DefaultCredentialsProvider).
* AwsCredentialsIdentity credentials =
* AwsSessionCredentialsIdentity.create("skid", "akid", "stok");
*
* // Create the HTTP request to be signed
* SdkHttpRequest httpRequest =
* SdkHttpRequest.builder()
* .uri("https://s3.us-west-2.amazonaws.com/bucket/object")
* .method(SdkHttpMethod.PUT)
* .putHeader("Content-Type", "text/plain")
* .build();
*
* // Create the request payload to be signed
* ContentStreamProvider requestPayload =
* ContentStreamProvider.fromUtf8String("Hello, World!");
*
* // Sign the request. Some services require custom signing configuration properties (e.g. S3).
* // See AwsV4HttpSigner and AwsV4FamilyHttpSigner for the available signing options.
* // Note: The S3Client class below requires a dependency on the 's3' module. Alternatively, the
* // signing name can be hard-coded because it is guaranteed to not change.
* SignedRequest signedRequest =
* signer.sign(r -> r.identity(credentials)
* .request(httpRequest)
* .payload(requestPayload)
* .putProperty(AwsV4HttpSigner.SERVICE_SIGNING_NAME, S3Client.SERVICE_NAME)
* .putProperty(AwsV4HttpSigner.REGION_NAME, "us-west-2")
* .putProperty(AwsV4HttpSigner.DOUBLE_URL_ENCODE, false) // Required for S3 only
* .putProperty(AwsV4HttpSigner.NORMALIZE_PATH, false)); // Required for S3 only
*
* // Create and HTTP client and send the request. ApacheHttpClient requires the 'apache-client' module.
* try (SdkHttpClient httpClient = ApacheHttpClient.create()) {
* HttpExecuteRequest httpExecuteRequest =
* HttpExecuteRequest.builder()
* .request(signedRequest.request())
* .contentStreamProvider(signedRequest.payload().orElse(null))
* .build();
*
* HttpExecuteResponse httpResponse =
* httpClient.prepareRequest(httpExecuteRequest).call();
*
* System.out.println("HTTP Status Code: " + httpResponse.httpResponse().statusCode());
* } catch (IOException e) {
* System.err.println("HTTP Request Failed.");
* e.printStackTrace();
* }
* }
*/
@SdkPublicApi
public interface AwsV4HttpSigner extends AwsV4FamilyHttpSigner<AwsCredentialsIdentity> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,67 @@
import software.amazon.awssdk.identity.spi.AwsCredentialsIdentity;

/**
* An {@link HttpSigner} that will sign a request using an AWS credentials {@link AwsCredentialsIdentity}).
* An {@link HttpSigner} that will use the AWS V4a signing algorithm to sign a request using an
* {@link AwsCredentialsIdentity}).
*
* <p>
* The process for signing requests to send to AWS services is documented
* AWS request signing is described
* <a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_aws-signing.html">here</a>.
*
* <h2>Using the AwsV4aHttpSigner</h2>
* <p>
* <b>Sign an HTTP request and send it to a service.</b>
* <p>
* {@snippet :
* AwsV4aHttpSigner signer = AwsV4aHttpSigner.create();
*
* // Specify AWS credentials. Credential providers that are used by the SDK by default are
* // available in the module "auth" (e.g. DefaultCredentialsProvider).
* AwsCredentialsIdentity credentials =
* AwsSessionCredentialsIdentity.create("skid", "akid", "stok");
*
* // Create the HTTP request to be signed
* SdkHttpRequest httpRequest =
* SdkHttpRequest.builder()
* .uri("https://s3.us-west-2.amazonaws.com/bucket/object")
* .method(SdkHttpMethod.PUT)
* .putHeader("Content-Type", "text/plain")
* .build();
*
* // Create the request payload to be signed
* ContentStreamProvider requestPayload =
* ContentStreamProvider.fromUtf8String("Hello, World!");
*
* // Sign the request. Some services require custom signing configuration properties (e.g. S3).
* // See AwsV4aHttpSigner and AwsV4FamilyHttpSigner for the available signing options.
* // Note: The S3Client class below requires a dependency on the 's3' module. Alternatively, the
* // signing name can be hard-coded because it is guaranteed to not change.
* SignedRequest signedRequest =
* signer.sign(r -> r.identity(credentials)
* .request(httpRequest)
* .payload(requestPayload)
* .putProperty(AwsV4aHttpSigner.SERVICE_SIGNING_NAME, S3Client.SERVICE_NAME)
* .putProperty(AwsV4aHttpSigner.REGION_SET, RegionSet.create("us-west-2"))
* .putProperty(AwsV4aHttpSigner.DOUBLE_URL_ENCODE, false) // Required for S3 only
* .putProperty(AwsV4aHttpSigner.NORMALIZE_PATH, false)); // Required for S3 only
*
* // Create and HTTP client and send the request. ApacheHttpClient requires the 'apache-client' module.
* try (SdkHttpClient httpClient = ApacheHttpClient.create()) {
* HttpExecuteRequest httpExecuteRequest =
* HttpExecuteRequest.builder()
* .request(signedRequest.request())
* .contentStreamProvider(signedRequest.payload().orElse(null))
* .build();
*
* HttpExecuteResponse httpResponse =
* httpClient.prepareRequest(httpExecuteRequest).call();
*
* System.out.println("HTTP Status Code: " + httpResponse.httpResponse().statusCode());
* } catch (IOException e) {
* System.err.println("HTTP Request Failed.");
* e.printStackTrace();
* }
* }
*/
@SdkPublicApi
public interface AwsV4aHttpSigner extends AwsV4FamilyHttpSigner<AwsCredentialsIdentity> {
Expand All @@ -41,5 +98,4 @@ public interface AwsV4aHttpSigner extends AwsV4FamilyHttpSigner<AwsCredentialsId
static AwsV4aHttpSigner create() {
return getDefaultAwsCrtV4aHttpSigner();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* 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.internal.signer;

import java.util.concurrent.CompletableFuture;
import software.amazon.awssdk.annotations.SdkInternalApi;
import software.amazon.awssdk.http.auth.spi.signer.AsyncSignRequest;
import software.amazon.awssdk.http.auth.spi.signer.AsyncSignedRequest;
import software.amazon.awssdk.http.auth.spi.signer.HttpSigner;
import software.amazon.awssdk.http.auth.spi.signer.SignRequest;
import software.amazon.awssdk.http.auth.spi.signer.SignedRequest;
import software.amazon.awssdk.identity.spi.Identity;

@SdkInternalApi
public class NoOpHttpSigner<T extends Identity> implements HttpSigner<T> {
@Override
public SignedRequest sign(SignRequest<? extends T> request) {
return SignedRequest.builder()
.request(request.request())
.payload(request.payload().orElse(null))
.build();
}

@Override
public CompletableFuture<AsyncSignedRequest> signAsync(AsyncSignRequest<? extends T> request) {
return CompletableFuture.completedFuture(AsyncSignedRequest.builder()
.request(request.request())
.payload(request.payload().orElse(null))
.build());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import software.amazon.awssdk.annotations.SdkPublicApi;
import software.amazon.awssdk.http.auth.spi.internal.signer.DefaultAsyncSignRequest;
import software.amazon.awssdk.http.auth.spi.internal.signer.DefaultSignRequest;
import software.amazon.awssdk.http.auth.spi.internal.signer.NoOpHttpSigner;
import software.amazon.awssdk.identity.spi.Identity;

/**
Expand All @@ -31,14 +32,20 @@
*/
@SdkPublicApi
public interface HttpSigner<IdentityT extends Identity> {

/**
* A {@link Clock} to be used to derive the signing time. This property defaults to the system clock.
*
* <p>Note, signing time may not be relevant to some signers.
*/
SignerProperty<Clock> SIGNING_CLOCK = SignerProperty.create(HttpSigner.class, "SigningClock");

/**
* Retrieve a signer that returns the input message, without signing.
*/
default <T extends Identity> HttpSigner<T> doNotSign() {
return new NoOpHttpSigner<>();
}

/**
* Method that takes in inputs to sign a request with sync payload and returns a signed version of the request.
*
Expand Down Expand Up @@ -84,4 +91,5 @@ default SignedRequest sign(Consumer<SignRequest.Builder<IdentityT>> consumer) {
default CompletableFuture<AsyncSignedRequest> signAsync(Consumer<AsyncSignRequest.Builder<IdentityT>> consumer) {
return signAsync(DefaultAsyncSignRequest.<IdentityT>builder().applyMutation(consumer).build());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,12 @@

/**
* Interface for the signer used for signing the async requests.
*
* @deprecated Replaced by {@code software.amazon.awssdk.http.auth.spi.signer.HttpSigner} in 'http-auth-spi'.
*/
@SdkPublicApi
@FunctionalInterface
@Deprecated
public interface AsyncRequestBodySigner {
/**
* Method that takes in an signed request and async request body provider,
Expand Down
Loading

0 comments on commit 5a9bd22

Please sign in to comment.