1package presignedurl
2
3import (
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.
15type 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
22type 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.
40type 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.
49func 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.
54func RemoveMiddleware(stack *middleware.Stack) error {
55 _, err := stack.Initialize.Remove((*presign)(nil).ID())
56 return err
57}
58
59type presign struct {
60 options Options
61}
62
63func (m *presign) ID() string { return "Presign" }
64
65func (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}