middleware.go 3.9 KB

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