118 lines
3.5 KiB
Go
118 lines
3.5 KiB
Go
package v4a
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"time"
|
|
|
|
awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware"
|
|
v4 "github.com/aws/aws-sdk-go-v2/aws/signer/v4"
|
|
"github.com/aws/aws-sdk-go-v2/internal/sdk"
|
|
"github.com/aws/smithy-go/middleware"
|
|
smithyHTTP "github.com/aws/smithy-go/transport/http"
|
|
)
|
|
|
|
// HTTPPresigner is an interface to a SigV4a signer that can sign create a
|
|
// presigned URL for a HTTP requests.
|
|
type HTTPPresigner interface {
|
|
PresignHTTP(
|
|
ctx context.Context, credentials Credentials, r *http.Request,
|
|
payloadHash string, service string, regionSet []string, signingTime time.Time,
|
|
optFns ...func(*SignerOptions),
|
|
) (url string, signedHeader http.Header, err error)
|
|
}
|
|
|
|
// PresignHTTPRequestMiddlewareOptions is the options for the PresignHTTPRequestMiddleware middleware.
|
|
type PresignHTTPRequestMiddlewareOptions struct {
|
|
CredentialsProvider CredentialsProvider
|
|
Presigner HTTPPresigner
|
|
LogSigning bool
|
|
}
|
|
|
|
// PresignHTTPRequestMiddleware provides the Finalize middleware for creating a
|
|
// presigned URL for an HTTP request.
|
|
//
|
|
// Will short circuit the middleware stack and not forward onto the next
|
|
// Finalize handler.
|
|
type PresignHTTPRequestMiddleware struct {
|
|
credentialsProvider CredentialsProvider
|
|
presigner HTTPPresigner
|
|
logSigning bool
|
|
}
|
|
|
|
// NewPresignHTTPRequestMiddleware returns a new PresignHTTPRequestMiddleware
|
|
// initialized with the presigner.
|
|
func NewPresignHTTPRequestMiddleware(options PresignHTTPRequestMiddlewareOptions) *PresignHTTPRequestMiddleware {
|
|
return &PresignHTTPRequestMiddleware{
|
|
credentialsProvider: options.CredentialsProvider,
|
|
presigner: options.Presigner,
|
|
logSigning: options.LogSigning,
|
|
}
|
|
}
|
|
|
|
// ID provides the middleware ID.
|
|
func (*PresignHTTPRequestMiddleware) ID() string { return "PresignHTTPRequest" }
|
|
|
|
// HandleFinalize will take the provided input and create a presigned url for
|
|
// the http request using the SigV4 presign authentication scheme.
|
|
func (s *PresignHTTPRequestMiddleware) HandleFinalize(
|
|
ctx context.Context, in middleware.FinalizeInput, next middleware.FinalizeHandler,
|
|
) (
|
|
out middleware.FinalizeOutput, metadata middleware.Metadata, err error,
|
|
) {
|
|
req, ok := in.Request.(*smithyHTTP.Request)
|
|
if !ok {
|
|
return out, metadata, &SigningError{
|
|
Err: fmt.Errorf("unexpected request middleware type %T", in.Request),
|
|
}
|
|
}
|
|
|
|
httpReq := req.Build(ctx)
|
|
if !hasCredentialProvider(s.credentialsProvider) {
|
|
out.Result = &v4.PresignedHTTPRequest{
|
|
URL: httpReq.URL.String(),
|
|
Method: httpReq.Method,
|
|
SignedHeader: http.Header{},
|
|
}
|
|
|
|
return out, metadata, nil
|
|
}
|
|
|
|
signingName := awsmiddleware.GetSigningName(ctx)
|
|
signingRegion := awsmiddleware.GetSigningRegion(ctx)
|
|
payloadHash := v4.GetPayloadHash(ctx)
|
|
if len(payloadHash) == 0 {
|
|
return out, metadata, &SigningError{
|
|
Err: fmt.Errorf("computed payload hash missing from context"),
|
|
}
|
|
}
|
|
|
|
credentials, err := s.credentialsProvider.RetrievePrivateKey(ctx)
|
|
if err != nil {
|
|
return out, metadata, &SigningError{
|
|
Err: fmt.Errorf("failed to retrieve credentials: %w", err),
|
|
}
|
|
}
|
|
|
|
u, h, err := s.presigner.PresignHTTP(ctx, credentials,
|
|
httpReq, payloadHash, signingName, []string{signingRegion}, sdk.NowTime(),
|
|
func(o *SignerOptions) {
|
|
o.Logger = middleware.GetLogger(ctx)
|
|
o.LogSigning = s.logSigning
|
|
})
|
|
if err != nil {
|
|
return out, metadata, &SigningError{
|
|
Err: fmt.Errorf("failed to sign http request, %w", err),
|
|
}
|
|
}
|
|
|
|
out.Result = &v4.PresignedHTTPRequest{
|
|
URL: u,
|
|
Method: httpReq.Method,
|
|
SignedHeader: h,
|
|
}
|
|
|
|
return out, metadata, nil
|
|
}
|