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 all 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
@@ -0,0 +1,50 @@
/*
* 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;

import java.nio.ByteBuffer;
import org.reactivestreams.Publisher;
import software.amazon.awssdk.annotations.Immutable;
import software.amazon.awssdk.annotations.SdkPublicApi;
import software.amazon.awssdk.annotations.ThreadSafe;
import software.amazon.awssdk.http.auth.spi.internal.DefaultAsyncHttpSignRequest;
import software.amazon.awssdk.identity.spi.Identity;
import software.amazon.awssdk.utils.builder.SdkBuilder;

/**
* Input parameters to sign a request with async payload, using {@link HttpSigner}.
*
* @param <IdentityT> The type of the identity.
*/
@SdkPublicApi
@Immutable
@ThreadSafe
public interface AsyncHttpSignRequest<IdentityT extends Identity> extends HttpSignRequest<Publisher<ByteBuffer>, IdentityT> {
/**
* Get a new builder for creating a {@link AsyncHttpSignRequest}.
*/
static <IdentityT extends Identity> Builder<IdentityT> builder(IdentityT identity) {
return new DefaultAsyncHttpSignRequest.BuilderImpl<>(identity);
}

/**
* A builder for a {@link AsyncHttpSignRequest}.
*/
interface Builder<IdentityT extends Identity>
extends HttpSignRequest.Builder<Builder<IdentityT>, Publisher<ByteBuffer>, IdentityT>,
SdkBuilder<Builder<IdentityT>, AsyncHttpSignRequest<IdentityT>> {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* 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;

import java.nio.ByteBuffer;
import org.reactivestreams.Publisher;
import software.amazon.awssdk.annotations.Immutable;
import software.amazon.awssdk.annotations.SdkPublicApi;
import software.amazon.awssdk.annotations.ThreadSafe;
import software.amazon.awssdk.http.auth.spi.internal.DefaultAsyncSignedHttpRequest;
import software.amazon.awssdk.utils.builder.SdkBuilder;

/**
* Represents a request with async payload that has been signed by {@link HttpSigner}.
*/
@SdkPublicApi
@Immutable
@ThreadSafe
public interface AsyncSignedHttpRequest extends SignedHttpRequest<Publisher<ByteBuffer>> {

/**
* Get a new builder for creating a {@link AsyncSignedHttpRequest}.
*/
static Builder builder() {
return new DefaultAsyncSignedHttpRequest.BuilderImpl();
}

/**
* A builder for a {@link AsyncSignedHttpRequest}.
*/
interface Builder extends SignedHttpRequest.Builder<Builder, Publisher<ByteBuffer>>,
SdkBuilder<Builder, AsyncSignedHttpRequest> {
}
}
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 @@ -20,30 +20,18 @@
import software.amazon.awssdk.annotations.SdkPublicApi;
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.utils.builder.SdkBuilder;
import software.amazon.awssdk.identity.spi.Identity;

/**
* 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> {

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

/**
* Returns the type of the payload.
*/
Class<PayloadT> payloadType();
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?


/**
* Returns the HTTP request object, without the request body payload.
Expand All @@ -56,28 +44,38 @@ static <T> Builder<T> builder(Class<T> payloadType) {
Optional<PayloadT> payload();

/**
* Returns the property that the {@link HttpSigner} can use during signing.
* Returns the identity.
*/
IdentityT identity();
Comment on lines 44 to +49
Copy link
Contributor

Choose a reason for hiding this comment

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

Imo, I'd rather not parameterize this types and just do Optional<?> payload() and Identity identity() here. Subtypes would override them with the more-specific return type.

This makes it so that code that deal with the base sign request can just deal with a SignRequest instead of a SignRequest<?, ?>, and the base builder can just deal with a Builder<?> instead of a Builder<?, ?, ?>.

WDYT?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think code that deals with base sign request, might benefit from the type, atleast the IdentityT. So the BearerTokenSigner can extract the token from HttpSignRequest<?, TokenIdentity identity>. Though I can see removing the PayloadT (will reduce it by one), but means the subtypes have to (re)define the payload() method instead of specifying the generic in type definition. And as an interface, having a HttpSignRequest that has a payload() accessor but doesn't know it's type doesn't feel great to me, so I'm in favor of keeping them.

Copy link
Contributor

Choose a reason for hiding this comment

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

Cool. And we can reassess at the surface area review as well, once we can see the impact on the signers, etc.


/**
* 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<B extends Builder<B, PayloadT, IdentityT>, PayloadT, IdentityT extends Identity> {

/**
* Set the HTTP request object, without the request body payload.
*/
Builder request(SdkHttpRequest request);
B 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);
B payload(PayloadT payload);

/**
* Set the identity of the request.
*/
B identity(IdentityT identity);
Comment on lines 66 to +74
Copy link
Contributor

Choose a reason for hiding this comment

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

If we go with the suggestion I have above, we'd remove these from the base builder and put it in the subtypes.


/**
* Set a property that the {@link HttpSigner} can use during signing.
*/
<T> Builder putProperty(SignerProperty<T> key, T value);
<T> B putProperty(SignerProperty<T> key, T value);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,31 +15,62 @@

package software.amazon.awssdk.http.auth.spi;

import java.nio.ByteBuffer;
import org.reactivestreams.Publisher;
import java.util.function.Consumer;
import software.amazon.awssdk.annotations.SdkPublicApi;
import software.amazon.awssdk.http.ContentStreamProvider;
import software.amazon.awssdk.http.auth.spi.internal.DefaultAsyncHttpSignRequest;
import software.amazon.awssdk.http.auth.spi.internal.DefaultSyncHttpSignRequest;
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.
*/
SyncSignedHttpRequest sign(SyncHttpSignRequest<? extends 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(SyncHttpSignRequest)}, but takes a lambda to configure a new {@link SyncHttpSignRequest.Builder}.
* This removes the need to call {@link SyncHttpSignRequest#builder(IdentityT)}} and
* {@link SyncHttpSignRequest.Builder#build()}.
*
* @param consumer A {@link Consumer} to which an empty {@link SyncHttpSignRequest.Builder} will be given.
* @return A signed version of the request.
*/
default SyncSignedHttpRequest sign(Consumer<SyncHttpSignRequest.Builder<IdentityT>> consumer) {
return sign(new DefaultSyncHttpSignRequest.BuilderImpl<IdentityT>().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);
AsyncSignedHttpRequest signAsync(AsyncHttpSignRequest<? extends 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(AsyncHttpSignRequest)}, but takes a lambda to configure a new
* {@link AsyncHttpSignRequest.Builder}. This removes the need to call {@link AsyncHttpSignRequest#builder(IdentityT)}} and
* {@link AsyncHttpSignRequest.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 AsyncSignedHttpRequest signAsync(Consumer<AsyncHttpSignRequest.Builder<IdentityT>> consumer) {
return signAsync(new DefaultAsyncHttpSignRequest.BuilderImpl<IdentityT>().applyMutation(consumer).build());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,31 +20,17 @@
import software.amazon.awssdk.annotations.SdkPublicApi;
import software.amazon.awssdk.annotations.ThreadSafe;
import software.amazon.awssdk.http.SdkHttpRequest;
import software.amazon.awssdk.http.auth.spi.internal.DefaultSignedHttpRequest;
import software.amazon.awssdk.utils.builder.SdkBuilder;

/**
* 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
@ThreadSafe
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);
}

/**
* Returns the type of the payload.
*/
Class<PayloadT> payloadType();

/**
* Returns the HTTP request object, without the request body payload.
*/
Expand All @@ -58,16 +44,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<B extends Builder<B, PayloadT>, PayloadT> {

/**
* Set the HTTP request object, without the request body payload.
*/
Builder request(SdkHttpRequest request);
B 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);
B 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
@@ -0,0 +1,49 @@
/*
* 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;

import software.amazon.awssdk.annotations.Immutable;
import software.amazon.awssdk.annotations.SdkPublicApi;
import software.amazon.awssdk.annotations.ThreadSafe;
import software.amazon.awssdk.http.ContentStreamProvider;
import software.amazon.awssdk.http.auth.spi.internal.DefaultSyncHttpSignRequest;
import software.amazon.awssdk.identity.spi.Identity;
import software.amazon.awssdk.utils.builder.SdkBuilder;

/**
* Input parameters to sign a request with sync payload, using {@link HttpSigner}.
*
* @param <IdentityT> The type of the identity.
*/
@SdkPublicApi
@Immutable
@ThreadSafe
public interface SyncHttpSignRequest<IdentityT extends Identity> extends HttpSignRequest<ContentStreamProvider, IdentityT> {
/**
* Get a new builder for creating a {@link SyncHttpSignRequest}.
*/
static <IdentityT extends Identity> Builder<IdentityT> builder(IdentityT identity) {
return new DefaultSyncHttpSignRequest.BuilderImpl<>(identity);
}

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