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