middleware.go (4020B)
1 package presignedurl 2 3 import ( 4 "context" 5 "fmt" 6 7 awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" 8 v4 "github.com/aws/aws-sdk-go-v2/aws/signer/v4" 9 10 "github.com/aws/smithy-go/middleware" 11 ) 12 13 // URLPresigner provides the interface to presign the input parameters in to a 14 // presigned URL. 15 type URLPresigner interface { 16 // PresignURL presigns a URL. 17 PresignURL(ctx context.Context, srcRegion string, params interface{}) (*v4.PresignedHTTPRequest, error) 18 } 19 20 // ParameterAccessor provides an collection of accessor to for retrieving and 21 // setting the values needed to PresignedURL generation 22 type ParameterAccessor struct { 23 // GetPresignedURL accessor points to a function that retrieves a presigned url if present 24 GetPresignedURL func(interface{}) (string, bool, error) 25 26 // GetSourceRegion accessor points to a function that retrieves source region for presigned url 27 GetSourceRegion func(interface{}) (string, bool, error) 28 29 // CopyInput accessor points to a function that takes in an input, and returns a copy. 30 CopyInput func(interface{}) (interface{}, error) 31 32 // SetDestinationRegion accessor points to a function that sets destination region on api input struct 33 SetDestinationRegion func(interface{}, string) error 34 35 // SetPresignedURL accessor points to a function that sets presigned url on api input struct 36 SetPresignedURL func(interface{}, string) error 37 } 38 39 // Options provides the set of options needed by the presigned URL middleware. 40 type Options struct { 41 // Accessor are the parameter accessors used by this middleware 42 Accessor ParameterAccessor 43 44 // Presigner is the URLPresigner used by the middleware 45 Presigner URLPresigner 46 } 47 48 // AddMiddleware adds the Presign URL middleware to the middleware stack. 49 func AddMiddleware(stack *middleware.Stack, opts Options) error { 50 return stack.Initialize.Add(&presign{options: opts}, middleware.Before) 51 } 52 53 // RemoveMiddleware removes the Presign URL middleware from the stack. 54 func RemoveMiddleware(stack *middleware.Stack) error { 55 _, err := stack.Initialize.Remove((*presign)(nil).ID()) 56 return err 57 } 58 59 type presign struct { 60 options Options 61 } 62 63 func (m *presign) ID() string { return "Presign" } 64 65 func (m *presign) HandleInitialize( 66 ctx context.Context, input middleware.InitializeInput, next middleware.InitializeHandler, 67 ) ( 68 out middleware.InitializeOutput, metadata middleware.Metadata, err error, 69 ) { 70 // If PresignedURL is already set ignore middleware. 71 if _, ok, err := m.options.Accessor.GetPresignedURL(input.Parameters); err != nil { 72 return out, metadata, fmt.Errorf("presign middleware failed, %w", err) 73 } else if ok { 74 return next.HandleInitialize(ctx, input) 75 } 76 77 // If have source region is not set ignore middleware. 78 srcRegion, ok, err := m.options.Accessor.GetSourceRegion(input.Parameters) 79 if err != nil { 80 return out, metadata, fmt.Errorf("presign middleware failed, %w", err) 81 } else if !ok || len(srcRegion) == 0 { 82 return next.HandleInitialize(ctx, input) 83 } 84 85 // Create a copy of the original input so the destination region value can 86 // be added. This ensures that value does not leak into the original 87 // request parameters. 88 paramCpy, err := m.options.Accessor.CopyInput(input.Parameters) 89 if err != nil { 90 return out, metadata, fmt.Errorf("unable to create presigned URL, %w", err) 91 } 92 93 // Destination region is the API client's configured region. 94 dstRegion := awsmiddleware.GetRegion(ctx) 95 if err = m.options.Accessor.SetDestinationRegion(paramCpy, dstRegion); err != nil { 96 return out, metadata, fmt.Errorf("presign middleware failed, %w", err) 97 } 98 99 presignedReq, err := m.options.Presigner.PresignURL(ctx, srcRegion, paramCpy) 100 if err != nil { 101 return out, metadata, fmt.Errorf("unable to create presigned URL, %w", err) 102 } 103 104 // Update the original input with the presigned URL value. 105 if err = m.options.Accessor.SetPresignedURL(input.Parameters, presignedReq.URL); err != nil { 106 return out, metadata, fmt.Errorf("presign middleware failed, %w", err) 107 } 108 109 return next.HandleInitialize(ctx, input) 110 }