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 }