Skip to content

HttpSigner interface changes #4018

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

Merged
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions core/http-auth-spi/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@
<artifactId>equalsverifier</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ interface SignerPropertyConsumer {
}

interface Builder extends SdkBuilder<Builder, HttpAuthOption> {
<T> Builder schemeId(String schemeId);
Builder schemeId(String schemeId);

<T> Builder putIdentityProperty(IdentityProperty<T> key, T value);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,5 +58,5 @@ public interface HttpAuthScheme<T extends Identity> {
* Retrieve the signer associated with this authentication scheme. This signer is guaranteed to support the identity
* generated by the identity provider in this authentication scheme.
*/
HttpSigner signer();
HttpSigner<T> signer();
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,26 @@
import software.amazon.awssdk.annotations.ThreadSafe;
import software.amazon.awssdk.http.SdkHttpRequest;
import software.amazon.awssdk.http.auth.spi.internal.DefaultHttpSignRequest;
import software.amazon.awssdk.identity.spi.Identity;
import software.amazon.awssdk.utils.builder.SdkBuilder;

/**
* Represents a request to be signed by {@link HttpSigner}.
* Input parameters to sign a request using {@link HttpSigner}.
*
* @param <PayloadT> The type of payload of this request.
* @param <PayloadT> The type of payload of the request.
* @param <IdentityT> The type of the identity.
*/
@SdkPublicApi
@Immutable
@ThreadSafe
public interface HttpSignRequest<PayloadT> {
public interface HttpSignRequest<PayloadT, IdentityT extends Identity> {
Copy link
Contributor

@millems millems May 18, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Imo, this should implement ToCopyableBuilder and its builder should implement CopyableBuilder. That prevents some issues here, like needing to remember to override every parent method in the child class.

Unfortunately it makes the parameters on this type even crazier.

To make the room, we could change IdentityT identity() to Identity identity() and Optional<PayloadT> payload() to Optional<? extends Object> payload(), and then override them in the child types to return the exact types. Even better, we might just remove the getters from here entirely and rely on the subtype. I don't expect it to be a major problem to need to use the subtype to get access to the payload or identity.

We'd also need to remove the payload and identity setters from this base builder, but that's probably okay - we can just put them in the child types. It should be very rare that someone needs to set the identity or payload using the base builder type...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That prevents some issues here, like needing to remember to override every parent method in the child class.

Without using To/CopyableBuilder, I was able to remove the overriding of methods in the builder sub-interfaces to return the sub-type by adding a generic type parameter for the builder, in these commits de704e7 and 3e24cc4.

I'm not sure if we'll really need to mutate these objects, so I think we can defer making these To/CopyableBuilder until needed?


/**
* Get a new builder for creating a {@link HttpSignRequest}.
*/
static <T> Builder<T> builder(Class<T> payloadType) {
return new DefaultHttpSignRequest.BuilderImpl(payloadType);
static <PayloadT, IdentityT extends Identity> Builder<PayloadT, IdentityT> builder(Class<PayloadT> payloadType,
Class<IdentityT> identityType) {
return new DefaultHttpSignRequest.BuilderImpl<>(payloadType);
}

/**
Expand All @@ -55,29 +58,37 @@ static <T> Builder<T> builder(Class<T> payloadType) {
*/
Optional<PayloadT> payload();

IdentityT identity();

/**
* Returns the property that the {@link HttpSigner} can use during signing.
* Returns the value of a property that the {@link HttpSigner} can use during signing.
*/
<T> T property(SignerProperty<T> property);

/**
* A builder for a {@link HttpSignRequest}.
*/
interface Builder<PayloadT> extends SdkBuilder<Builder<PayloadT>, HttpSignRequest> {
interface Builder<PayloadT, IdentityT extends Identity>
extends SdkBuilder<Builder<PayloadT, IdentityT>, HttpSignRequest<PayloadT, IdentityT>> {

/**
* Set the HTTP request object, without the request body payload.
*/
Builder request(SdkHttpRequest request);
Builder<PayloadT, IdentityT> request(SdkHttpRequest request);

/**
* Set the body payload of the request. A payload is optional. By default, the payload will be empty.
*/
Builder<PayloadT, IdentityT> payload(PayloadT payload);

/**
* Set the body payload of the request. A payload is optional. By default, the payload will be empty.
*/
Builder payload(PayloadT payload);
Builder<PayloadT, IdentityT> identity(IdentityT identity);

/**
* Set a property that the {@link HttpSigner} can use during signing.
*/
<T> Builder putProperty(SignerProperty<T> key, T value);
<T> Builder<PayloadT, IdentityT> putProperty(SignerProperty<T> key, T value);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,30 +16,65 @@
package software.amazon.awssdk.http.auth.spi;

import java.nio.ByteBuffer;
import java.util.function.Consumer;
import org.reactivestreams.Publisher;
import software.amazon.awssdk.annotations.SdkPublicApi;
import software.amazon.awssdk.http.ContentStreamProvider;
import software.amazon.awssdk.http.auth.spi.internal.DefaultHttpSignRequest;
import software.amazon.awssdk.identity.spi.Identity;

/**
* Interface for the process of modifying a request destined for a service so that the service can authenticate the SDK
* customer’s identity
* customer’s identity.
*
* @param <IdentityT> The type of the identity.
*/
@SdkPublicApi
public interface HttpSigner {
public interface HttpSigner<IdentityT extends Identity> {

/**
* Method that takes in inputs to sign a request with sync payload and returns a signed version of the request.
*
* @param request The inputs to sign a request.
* @return A signed version of the request.
*/
SignedHttpRequest<ContentStreamProvider> sign(HttpSignRequest<ContentStreamProvider, IdentityT> request);

/**
* Method that takes in inputs to sign a request with sync payload and returns a signed version of the request.
* <p>
* Similar to {@link #sign(HttpSignRequest)}, but takes a lambda to configure a new {@link HttpSignRequest.Builder}. This
* removes the need to call {@link HttpSignRequest#builder(Class, Class)}} and {@link HttpSignRequest.Builder#build()}.
*
* @param consumer A {@link Consumer} to which an empty {@link HttpSignRequest.Builder} will be given.
* @return A signed version of the request.
*/
default SignedHttpRequest<ContentStreamProvider> sign(
Consumer<HttpSignRequest.Builder<ContentStreamProvider, IdentityT>> consumer) {
return sign(new DefaultHttpSignRequest.BuilderImpl<ContentStreamProvider, IdentityT>(ContentStreamProvider.class)
.applyMutation(consumer).build());
}

/**
* Method that takes in a request and returns a signed version of the request.
* Method that takes in inputs to sign a request with async payload and returns a signed version of the request.
*
* @param request The request to sign, with sync payload
* @return A signed version of the input request
* @param request The inputs to sign a request.
* @return A signed version of the request.
*/
SignedHttpRequest<ContentStreamProvider> sign(HttpSignRequest<? extends ContentStreamProvider> request);
SignedHttpRequest<Publisher<ByteBuffer>> signAsync(HttpSignRequest<Publisher<ByteBuffer>, IdentityT> request);

/**
* Method that takes in a request and returns a signed version of the request.
* Method that takes in inputs to sign a request with async payload and returns a signed version of the request.
* <p>
* Similar to {@link #signAsync(HttpSignRequest)}, but takes a lambda to configure a new {@link HttpSignRequest.Builder}. This
* removes the need to call {@link HttpSignRequest#builder(Class, Class)}} and {@link HttpSignRequest.Builder#build()}.
*
* @param request The request to sign, with async payload
* @return A signed version of the input request
* @param consumer A {@link Consumer} to which an empty {@link HttpSignRequest.Builder} will be given.
* @return A signed version of the request.
*/
SignedHttpRequest<Publisher<ByteBuffer>> signAsync(HttpSignRequest<? extends Publisher<? extends ByteBuffer>> request);
default SignedHttpRequest<Publisher<ByteBuffer>> signAsync(
Consumer<HttpSignRequest.Builder<Publisher<ByteBuffer>, IdentityT>> consumer) {
return signAsync(new DefaultHttpSignRequest.BuilderImpl<Publisher<ByteBuffer>, IdentityT>((Class)Publisher.class)
.applyMutation(consumer).build());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
/**
* Represents a request that has been signed by {@link HttpSigner}.
*
* @param <PayloadT> The type of payload of this request.
* @param <PayloadT> The type of payload of the request.
*/
@SdkPublicApi
@Immutable
Expand All @@ -37,7 +37,7 @@ public interface SignedHttpRequest<PayloadT> {
* Get a new builder for creating a {@link SignedHttpRequest}.
*/
static <T> Builder<T> builder(Class<T> payloadType) {
return new DefaultSignedHttpRequest.BuilderImpl(payloadType);
return new DefaultSignedHttpRequest.BuilderImpl<>(payloadType);
}

/**
Expand All @@ -58,16 +58,16 @@ static <T> Builder<T> builder(Class<T> payloadType) {
/**
* A builder for a {@link SignedHttpRequest}.
*/
interface Builder<PayloadT> extends SdkBuilder<Builder<PayloadT>, SignedHttpRequest> {
interface Builder<PayloadT> extends SdkBuilder<Builder<PayloadT>, SignedHttpRequest<PayloadT>> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment here. CopyableBuilder?


/**
* Set the HTTP request object, without the request body payload.
*/
Builder request(SdkHttpRequest request);
Builder<PayloadT> request(SdkHttpRequest request);

/**
* Set the body payload of the request. A payload is optional. By default, the payload will be empty.
*/
Builder payload(PayloadT payload);
Builder<PayloadT> payload(PayloadT payload);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

/**
* A strongly-typed property for input to an {@link HttpSigner}.
* @param <T> The type of the attribute.
* @param <T> The type of the property.
*/
@SdkPublicApi
@Immutable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ public static final class BuilderImpl implements Builder {
private final Map<SignerProperty<?>, Object> signerProperties = new HashMap<>();

@Override
public <T> Builder schemeId(String schemeId) {
public Builder schemeId(String schemeId) {
this.schemeId = schemeId;
return this;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,24 @@
import software.amazon.awssdk.http.SdkHttpRequest;
import software.amazon.awssdk.http.auth.spi.HttpSignRequest;
import software.amazon.awssdk.http.auth.spi.SignerProperty;
import software.amazon.awssdk.identity.spi.Identity;
import software.amazon.awssdk.utils.ToString;
import software.amazon.awssdk.utils.Validate;

@SdkInternalApi
public final class DefaultHttpSignRequest<PayloadT> implements HttpSignRequest<PayloadT> {
public final class DefaultHttpSignRequest<PayloadT, IdentityT extends Identity> implements HttpSignRequest<PayloadT, IdentityT> {

private final Class<PayloadT> payloadType;
private final SdkHttpRequest request;
private final PayloadT payload;
private final IdentityT identity;
private final Map<SignerProperty<?>, Object> properties;

DefaultHttpSignRequest(BuilderImpl<PayloadT> builder) {
DefaultHttpSignRequest(BuilderImpl<PayloadT, IdentityT> builder) {
this.payloadType = Validate.paramNotNull(builder.payloadType, "payloadType");
this.request = Validate.paramNotNull(builder.request, "request");
this.payload = builder.payload;
this.identity = Validate.paramNotNull(builder.identity, "identity");
this.properties = new HashMap<>(builder.properties);
}

Expand All @@ -51,10 +54,15 @@ public SdkHttpRequest request() {
}

@Override
public Optional payload() {
public Optional<PayloadT> payload() {
return payload == null ? Optional.empty() : Optional.of(payload);
}

@Override
public IdentityT identity() {
return identity;
}

@Override
public <T> T property(SignerProperty<T> property) {
return (T) properties.get(property);
Expand All @@ -70,37 +78,44 @@ public String toString() {
}


public static final class BuilderImpl<PayloadT> implements Builder<PayloadT> {
public static final class BuilderImpl<PayloadT, IdentityT extends Identity> implements Builder<PayloadT, IdentityT> {
private final Class<PayloadT> payloadType;
private SdkHttpRequest request;
private PayloadT payload;
private IdentityT identity;
private final Map<SignerProperty<?>, Object> properties = new HashMap<>();

public BuilderImpl(Class<PayloadT> payloadType) {
this.payloadType = payloadType;
}

@Override
public Builder request(SdkHttpRequest request) {
public Builder<PayloadT, IdentityT> request(SdkHttpRequest request) {
this.request = request;
return this;
}

@Override
public Builder payload(PayloadT payload) {
public Builder<PayloadT, IdentityT> payload(PayloadT payload) {
this.payload = payload;
return this;
}

@Override
public <T> Builder putProperty(SignerProperty<T> key, T value) {
public Builder<PayloadT, IdentityT> identity(IdentityT identity) {
this.identity = identity;
return this;
}

@Override
public <T> Builder<PayloadT, IdentityT> putProperty(SignerProperty<T> key, T value) {
this.properties.put(key, value);
return this;
}

@Override
public HttpSignRequest build() {
return new DefaultHttpSignRequest(this);
public HttpSignRequest<PayloadT, IdentityT> build() {
return new DefaultHttpSignRequest<>(this);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public SdkHttpRequest request() {
}

@Override
public Optional payload() {
public Optional<PayloadT> payload() {
return payload == null ? Optional.empty() : Optional.of(payload);
}

Expand All @@ -68,20 +68,20 @@ public BuilderImpl(Class<PayloadT> payloadType) {
}

@Override
public Builder request(SdkHttpRequest request) {
public Builder<PayloadT> request(SdkHttpRequest request) {
this.request = request;
return this;
}

@Override
public Builder payload(PayloadT payload) {
public Builder<PayloadT> payload(PayloadT payload) {
this.payload = payload;
return this;
}

@Override
public SignedHttpRequest build() {
return new DefaultSignedHttpRequest(this);
public SignedHttpRequest<PayloadT> build() {
return new DefaultSignedHttpRequest<>(this);
}
}
}
Loading