Skip to content

Commit

Permalink
Replace Aws4Signer with AwsV4HttpSigner
Browse files Browse the repository at this point in the history
Previously the library using the deprecated Aws4Signer. This PR replaces
it with the new AwsV4HttpSigner.
  • Loading branch information
GeorgeVincentDepop committed Sep 20, 2024
1 parent 756a6e3 commit 1620f0d
Show file tree
Hide file tree
Showing 13 changed files with 199 additions and 77 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
### 2.3.2 (Next)
### 3.0.0 (Next)
* [#125](https://github.com/acm19/aws-request-signing-apache-interceptor/pull/125): Rewrite to use `AwsV4HttpSigner` - [@GeorgeVincentDepop](https://github.com/GeorgeVincentDepop)

### 2.3.1 (2023/06/11)
* [#107](https://github.com/acm19/aws-request-signing-apache-interceptor/pull/107): Automate the release fully by auto closing it - [@acm19](https://github.com/acm19).
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,15 @@ import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import io.github.acm19.aws.interceptor.http.AwsRequestSigningApacheInterceptor;
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
import software.amazon.awssdk.auth.signer.Aws4Signer;
import software.amazon.awssdk.http.auth.aws.signer.AwsV4HttpSigner;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.utils.IoUtils;

final class Example {
public static void main(String[] args) throws ClientProtocolException, IOException {
HttpRequestInterceptor interceptor = new AwsRequestSigningApacheInterceptor(
"service",
Aws4Signer.create(),
AwsV4HttpSigner.create(),
DefaultCredentialsProvider.create(),
Region.US_WEST_2
);
Expand Down Expand Up @@ -73,15 +73,15 @@ import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.http.HttpRequestInterceptor;
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
import software.amazon.awssdk.auth.signer.Aws4Signer;
import software.amazon.awssdk.http.auth.aws.signer.AwsV4HttpSigner;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.utils.IoUtils;

final class Example {
public static void main(String[] args) throws ClientProtocolException, IOException {
HttpRequestInterceptor interceptor = new AwsRequestSigningApacheV5Interceptor(
"service",
Aws4Signer.create(),
AwsV4HttpSigner.create(),
DefaultCredentialsProvider.create(),
Region.US_WEST_2
);
Expand Down
58 changes: 58 additions & 0 deletions UPGRADING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
- [Upgrading AWS Request Signing Interceptor](#upgrading-opensearch-python-client)
- [## Upgrading to >= 3.0.0](#upgrading-to->=-3.0.0)


# Upgrading AWS Request Signing Interceptor

## Upgrading to >= 3.0.0

`Aws4Signer` has been deprecated in the AWS SDK for Java 2.0 and replaced with `AwsV4HttpSigner`.

As a consequence, the `AwsRequestSigningApacheInterceptor` and `AwsRequestSigningApacheV5Interceptor` now requires an `AwsV4HttpSigner` instance instead of an `Aws4Signer`.

To upgrade, replace the `Aws4Signer` with `AwsV4HttpSigner` in your code. For example, if you are using `AwsRequestSigningApacheInterceptor`:

```java
import java.io.IOException;
import io.github.acm19.aws.interceptor.http.AwsRequestSigningApacheV5Interceptor;
import org.apache.hc.client5.http.ClientProtocolException;
import org.apache.hc.core5.http.HttpRequestInterceptor;
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
import software.amazon.awssdk.auth.signer.Aws4Signer;
import software.amazon.awssdk.regions.Region;

final class Example {
public static void main(String[] args) throws ClientProtocolException, IOException {
HttpRequestInterceptor interceptor = new AwsRequestSigningApacheV5Interceptor(
"service",
Aws4Signer.create(),
DefaultCredentialsProvider.create(),
Region.US_WEST_2
);
}
}
```

becomes:

```java
import java.io.IOException;
import io.github.acm19.aws.interceptor.http.AwsRequestSigningApacheV5Interceptor;
import org.apache.hc.client5.http.ClientProtocolException;
import org.apache.hc.core5.http.HttpRequestInterceptor;
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
import software.amazon.awssdk.auth.signer.Aws4Signer;
import software.amazon.awssdk.regions.Region;

final class Example {
public static void main(String[] args) throws ClientProtocolException, IOException {
HttpRequestInterceptor interceptor = new AwsRequestSigningApacheV5Interceptor(
"service",
AwsV4HttpSigner.create(),
DefaultCredentialsProvider.create(),
Region.US_WEST_2
);
}
}
```

2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

<groupId>io.github.acm19</groupId>
<artifactId>aws-request-signing-apache-interceptor</artifactId>
<version>2.3.2-SNAPSHOT</version>
<version>3.0.0-SNAPSHOT</version>
<packaging>jar</packaging>

<name>AWS Request Signing Apache Interceptor</name>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,16 @@
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
import software.amazon.awssdk.core.signer.Signer;
import software.amazon.awssdk.http.SdkHttpFullRequest;
import software.amazon.awssdk.http.SdkHttpMethod;
import software.amazon.awssdk.http.auth.spi.signer.HttpSigner;
import software.amazon.awssdk.http.auth.spi.signer.SignedRequest;
import software.amazon.awssdk.identity.spi.AwsCredentialsIdentity;
import software.amazon.awssdk.regions.Region;

/**
* An {@link HttpRequestInterceptor} that signs requests for any AWS service
* running in a specific region using an AWS {@link Signer} and
* running in a specific region using an AWS {@link HttpSigner} and
* {@link AwsCredentialsProvider}.
*/
public final class AwsRequestSigningApacheInterceptor implements HttpRequestInterceptor {
Expand All @@ -52,7 +54,7 @@ public final class AwsRequestSigningApacheInterceptor implements HttpRequestInte
* @param region signing region
*/
public AwsRequestSigningApacheInterceptor(String service,
Signer signer,
HttpSigner<AwsCredentialsIdentity> signer,
AwsCredentialsProvider awsCredentialsProvider,
Region region) {
this.signer = new RequestSigner(service, signer, awsCredentialsProvider, region);
Expand All @@ -69,7 +71,7 @@ public AwsRequestSigningApacheInterceptor(String service,
* @param region signing region
*/
public AwsRequestSigningApacheInterceptor(String service,
Signer signer,
HttpSigner<AwsCredentialsIdentity> signer,
AwsCredentialsProvider awsCredentialsProvider,
String region) {
this(service, signer, awsCredentialsProvider, Region.of(region));
Expand Down Expand Up @@ -108,10 +110,10 @@ public void process(HttpRequest request, HttpContext context)
// adds a hash of the request payload when signing
headers.put("x-amz-content-sha256", Collections.singletonList("required"));
requestBuilder.headers(headers);
SdkHttpFullRequest signedRequest = signer.signRequest(requestBuilder.build());
SignedRequest signedRequest = signer.signRequest(requestBuilder.build());

// copy everything back
request.setHeaders(mapToHeaderArray(signedRequest.headers()));
request.setHeaders(mapToHeaderArray(signedRequest.request().headers()));
}

private static Map<String, List<String>> headerArrayToMap(Header[] headers) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,16 @@
import org.apache.hc.core5.http.message.BasicHeader;
import org.apache.hc.core5.http.protocol.HttpContext;
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
import software.amazon.awssdk.core.signer.Signer;
import software.amazon.awssdk.http.SdkHttpFullRequest;
import software.amazon.awssdk.http.SdkHttpMethod;
import software.amazon.awssdk.http.auth.spi.signer.HttpSigner;
import software.amazon.awssdk.http.auth.spi.signer.SignedRequest;
import software.amazon.awssdk.identity.spi.AwsCredentialsIdentity;
import software.amazon.awssdk.regions.Region;

/**
* An {@link HttpRequestInterceptor} that signs requests for any AWS service
* running in a specific region using an AWS {@link Signer} and
* running in a specific region using an AWS {@link HttpSigner} and
* {@link AwsCredentialsProvider}.
*/
public final class AwsRequestSigningApacheV5Interceptor implements HttpRequestInterceptor {
Expand All @@ -55,7 +57,7 @@ public final class AwsRequestSigningApacheV5Interceptor implements HttpRequestIn
* @param region signing region
*/
public AwsRequestSigningApacheV5Interceptor(String service,
Signer signer,
HttpSigner<AwsCredentialsIdentity> signer,
AwsCredentialsProvider awsCredentialsProvider,
Region region) {
this.signer = new RequestSigner(service, signer, awsCredentialsProvider, region);
Expand All @@ -79,8 +81,9 @@ public void process(HttpRequest request, EntityDetails entityDetails, HttpContex
classicHttpRequest.getEntity().writeTo(outputStream);
if (!classicHttpRequest.getEntity().isRepeatable()) {
// copy back the entity, so it can be read again
BasicHttpEntity entity = new BasicHttpEntity(new ByteArrayInputStream(outputStream.toByteArray()),
ContentType.parse(entityDetails.getContentType()));
BasicHttpEntity entity = new BasicHttpEntity(
new ByteArrayInputStream(outputStream.toByteArray()),
ContentType.parse(entityDetails.getContentType()));
// wrap into repeatable entity to support retries
classicHttpRequest.setEntity(new BufferedHttpEntity(entity));
}
Expand All @@ -92,10 +95,10 @@ public void process(HttpRequest request, EntityDetails entityDetails, HttpContex
// adds a hash of the request payload when signing
headers.put("x-amz-content-sha256", Collections.singletonList("required"));
requestBuilder.headers(headers);
SdkHttpFullRequest signedRequest = signer.signRequest(requestBuilder.build());
SignedRequest signedRequest = signer.signRequest(requestBuilder.build());

// copy everything back
request.setHeaders(mapToHeaderArray(signedRequest.headers()));
request.setHeaders(mapToHeaderArray(signedRequest.request().headers()));
}

private static URI buildUri(HttpRequest request) throws IOException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@
import org.apache.http.protocol.HttpContext;
import org.apache.http.protocol.HttpCoreContext;
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
import software.amazon.awssdk.auth.signer.AwsSignerExecutionAttribute;
import software.amazon.awssdk.core.interceptor.ExecutionAttributes;
import software.amazon.awssdk.core.signer.Signer;
import software.amazon.awssdk.http.SdkHttpFullRequest;
import software.amazon.awssdk.http.auth.aws.signer.AwsV4HttpSigner;
import software.amazon.awssdk.http.auth.spi.signer.HttpSigner;
import software.amazon.awssdk.http.auth.spi.signer.SignedRequest;
import software.amazon.awssdk.identity.spi.AwsCredentialsIdentity;
import software.amazon.awssdk.regions.Region;

class RequestSigner {
Expand All @@ -31,7 +32,7 @@ class RequestSigner {
/**
* A signer implementation.
*/
private final Signer signer;
private final HttpSigner<AwsCredentialsIdentity> signer;
/**
* The source of AWS credentials for signing.
*/
Expand All @@ -49,7 +50,7 @@ class RequestSigner {
* @param region
*/
RequestSigner(String service,
Signer signer,
HttpSigner<AwsCredentialsIdentity> signer,
AwsCredentialsProvider awsCredentialsProvider,
Region region) {
this.service = service;
Expand All @@ -65,17 +66,18 @@ class RequestSigner {
*
* @param request to be signed
* @return signed request
* @see Signer#sign
* @see AwsV4HttpSigner#sign
*/
SdkHttpFullRequest signRequest(SdkHttpFullRequest request) {
ExecutionAttributes attributes = new ExecutionAttributes();
attributes.putAttribute(AwsSignerExecutionAttribute.AWS_CREDENTIALS,
awsCredentialsProvider.resolveCredentials());
attributes.putAttribute(AwsSignerExecutionAttribute.SERVICE_SIGNING_NAME, service);
attributes.putAttribute(AwsSignerExecutionAttribute.SIGNING_REGION, region);
SignedRequest signRequest(SdkHttpFullRequest request) {
SignedRequest signedRequest = signer.sign(r -> r.identity(awsCredentialsProvider.resolveCredentials())
.request(request)
.payload(request.contentStreamProvider().orElse(null))
.putProperty(AwsV4HttpSigner.SERVICE_SIGNING_NAME, service)
.putProperty(AwsV4HttpSigner.REGION_NAME, region.id())
.putProperty(AwsV4HttpSigner.DOUBLE_URL_ENCODE, false)
.putProperty(AwsV4HttpSigner.NORMALIZE_PATH, false));

// sign it
return signer.sign(request, attributes);
return signedRequest;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.CompletableFuture;
import java.util.zip.GZIPOutputStream;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpHeaders;
Expand All @@ -37,10 +38,14 @@
import software.amazon.awssdk.auth.credentials.AnonymousCredentialsProvider;
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.core.interceptor.ExecutionAttributes;
import software.amazon.awssdk.core.signer.Signer;
import software.amazon.awssdk.http.ContentStreamProvider;
import software.amazon.awssdk.http.SdkHttpFullRequest;
import software.amazon.awssdk.http.auth.aws.internal.signer.DefaultAwsV4HttpSigner;
import software.amazon.awssdk.http.auth.aws.signer.AwsV4HttpSigner;
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.SignRequest;
import software.amazon.awssdk.http.auth.spi.signer.SignedRequest;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.utils.IoUtils;

Expand Down Expand Up @@ -163,8 +168,7 @@ void testGzipCompressedContent() throws Exception {
gzipOutputStream.write(data.getBytes("UTF-8"));
}

ByteArrayEntity entity = new ByteArrayEntity(outputStream.toByteArray(),
ContentType.DEFAULT_BINARY);
ByteArrayEntity entity = new ByteArrayEntity(outputStream.toByteArray(), ContentType.DEFAULT_BINARY);
entity.setContentEncoding("gzip");
request.setHeader(HttpHeaders.CONTENT_ENCODING, "gzip");
request.setEntity(entity);
Expand All @@ -178,10 +182,12 @@ void testGzipCompressedContent() throws Exception {
assertEquals("required", request.getFirstHeader("x-amz-content-sha256").getValue());

assertEquals(Long.toString(entity.getContentLength()),
request.getFirstHeader("signedContentLength").getValue());
request.getFirstHeader("signedContentLength").getValue());
}

private static final class AddHeaderSigner implements Signer {
private static final class AddHeaderSigner implements AwsV4HttpSigner {
AwsV4HttpSigner signer = new DefaultAwsV4HttpSigner();

private final String name;
private final String value;

Expand All @@ -191,22 +197,38 @@ private AddHeaderSigner(final String name, final String value) {
}

@Override
public SdkHttpFullRequest sign(final SdkHttpFullRequest request, final ExecutionAttributes ea) {
SdkHttpFullRequest.Builder requestBuilder = SdkHttpFullRequest.builder()
.uri(request.getUri())
.method(request.method())
.headers(request.headers())
public SignedRequest sign(SignRequest signRequest) {
SdkHttpFullRequest.Builder request = SdkHttpFullRequest.builder()
.uri(signRequest.request().getUri())
.method(signRequest.request().method())
.headers(signRequest.request().headers())
.appendHeader(name, value)
.appendHeader("resourcePath", request.getUri().getRawPath());
.appendHeader("resourcePath", signRequest.request().getUri().getRawPath());

if (request.contentStreamProvider().isPresent()) {
ContentStreamProvider contentStreamProvider = request.contentStreamProvider().get();
requestBuilder.contentStreamProvider(contentStreamProvider);
requestBuilder.appendHeader("signedContentLength",
Long.toString(getContentLength(contentStreamProvider.newStream())));
if (signRequest.payload().isPresent()) {
ContentStreamProvider contentStreamProvider = (ContentStreamProvider) signRequest.payload().get();
InputStream payloadStream = contentStreamProvider.newStream();
ContentStreamProvider newContentStreamProvider = ContentStreamProvider.fromInputStream(payloadStream);

request.contentStreamProvider(newContentStreamProvider)
.appendHeader("signedContentLength",
Long.toString(getContentLength(newContentStreamProvider.newStream())))
.build();
}

return requestBuilder.build();
SdkHttpFullRequest requestBuilt = request.build();

SignedRequest signedRequest =
signer.sign(r -> r
.identity(AnonymousCredentialsProvider.create().resolveCredentials())
.request(requestBuilt)
.payload(requestBuilt.contentStreamProvider().orElse(null))
.putProperty(AwsV4HttpSigner.SERVICE_SIGNING_NAME, "servicename")
.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

return signedRequest;
}

private static int getContentLength(final InputStream content) {
Expand All @@ -216,6 +238,11 @@ private static int getContentLength(final InputStream content) {
return -1;
}
}

@Override
public CompletableFuture<AsyncSignedRequest> signAsync(AsyncSignRequest asyncSignRequest) {
return null;
}
}

private static class MockRequestLine implements RequestLine {
Expand Down
Loading

0 comments on commit 1620f0d

Please sign in to comment.