middleware.go

  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}