src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

presign_middleware.go (3866B)


      1 package v4
      2 
      3 import (
      4 	"context"
      5 	"fmt"
      6 	"net/http"
      7 	"time"
      8 
      9 	"github.com/aws/aws-sdk-go-v2/aws"
     10 	awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware"
     11 	"github.com/aws/aws-sdk-go-v2/internal/sdk"
     12 	"github.com/aws/smithy-go/middleware"
     13 	smithyHTTP "github.com/aws/smithy-go/transport/http"
     14 )
     15 
     16 // HTTPPresigner is an interface to a SigV4 signer that can sign create a
     17 // presigned URL for a HTTP requests.
     18 type HTTPPresigner interface {
     19 	PresignHTTP(
     20 		ctx context.Context, credentials aws.Credentials, r *http.Request,
     21 		payloadHash string, service string, region string, signingTime time.Time,
     22 		optFns ...func(*SignerOptions),
     23 	) (url string, signedHeader http.Header, err error)
     24 }
     25 
     26 // PresignedHTTPRequest provides the URL and signed headers that are included
     27 // in the presigned URL.
     28 type PresignedHTTPRequest struct {
     29 	URL          string
     30 	Method       string
     31 	SignedHeader http.Header
     32 }
     33 
     34 // PresignHTTPRequestMiddlewareOptions is the options for the PresignHTTPRequestMiddleware middleware.
     35 type PresignHTTPRequestMiddlewareOptions struct {
     36 	CredentialsProvider aws.CredentialsProvider
     37 	Presigner           HTTPPresigner
     38 	LogSigning          bool
     39 }
     40 
     41 // PresignHTTPRequestMiddleware provides the Finalize middleware for creating a
     42 // presigned URL for an HTTP request.
     43 //
     44 // Will short circuit the middleware stack and not forward onto the next
     45 // Finalize handler.
     46 type PresignHTTPRequestMiddleware struct {
     47 	credentialsProvider aws.CredentialsProvider
     48 	presigner           HTTPPresigner
     49 	logSigning          bool
     50 }
     51 
     52 // NewPresignHTTPRequestMiddleware returns a new PresignHTTPRequestMiddleware
     53 // initialized with the presigner.
     54 func NewPresignHTTPRequestMiddleware(options PresignHTTPRequestMiddlewareOptions) *PresignHTTPRequestMiddleware {
     55 	return &PresignHTTPRequestMiddleware{
     56 		credentialsProvider: options.CredentialsProvider,
     57 		presigner:           options.Presigner,
     58 		logSigning:          options.LogSigning,
     59 	}
     60 }
     61 
     62 // ID provides the middleware ID.
     63 func (*PresignHTTPRequestMiddleware) ID() string { return "PresignHTTPRequest" }
     64 
     65 // HandleFinalize will take the provided input and create a presigned url for
     66 // the http request using the SigV4 presign authentication scheme.
     67 //
     68 // Since the signed request is not a valid HTTP request
     69 func (s *PresignHTTPRequestMiddleware) HandleFinalize(
     70 	ctx context.Context, in middleware.FinalizeInput, next middleware.FinalizeHandler,
     71 ) (
     72 	out middleware.FinalizeOutput, metadata middleware.Metadata, err error,
     73 ) {
     74 	req, ok := in.Request.(*smithyHTTP.Request)
     75 	if !ok {
     76 		return out, metadata, &SigningError{
     77 			Err: fmt.Errorf("unexpected request middleware type %T", in.Request),
     78 		}
     79 	}
     80 
     81 	httpReq := req.Build(ctx)
     82 	if !haveCredentialProvider(s.credentialsProvider) {
     83 		out.Result = &PresignedHTTPRequest{
     84 			URL:          httpReq.URL.String(),
     85 			Method:       httpReq.Method,
     86 			SignedHeader: http.Header{},
     87 		}
     88 
     89 		return out, metadata, nil
     90 	}
     91 
     92 	signingName := awsmiddleware.GetSigningName(ctx)
     93 	signingRegion := awsmiddleware.GetSigningRegion(ctx)
     94 	payloadHash := GetPayloadHash(ctx)
     95 	if len(payloadHash) == 0 {
     96 		return out, metadata, &SigningError{
     97 			Err: fmt.Errorf("computed payload hash missing from context"),
     98 		}
     99 	}
    100 
    101 	credentials, err := s.credentialsProvider.Retrieve(ctx)
    102 	if err != nil {
    103 		return out, metadata, &SigningError{
    104 			Err: fmt.Errorf("failed to retrieve credentials: %w", err),
    105 		}
    106 	}
    107 
    108 	u, h, err := s.presigner.PresignHTTP(ctx, credentials,
    109 		httpReq, payloadHash, signingName, signingRegion, sdk.NowTime(),
    110 		func(o *SignerOptions) {
    111 			o.Logger = middleware.GetLogger(ctx)
    112 			o.LogSigning = s.logSigning
    113 		})
    114 	if err != nil {
    115 		return out, metadata, &SigningError{
    116 			Err: fmt.Errorf("failed to sign http request, %w", err),
    117 		}
    118 	}
    119 
    120 	out.Result = &PresignedHTTPRequest{
    121 		URL:          u,
    122 		Method:       httpReq.Method,
    123 		SignedHeader: h,
    124 	}
    125 
    126 	return out, metadata, nil
    127 }