middleware.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. package v4a
  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. internalauth "github.com/aws/aws-sdk-go-v2/internal/auth"
  8. "github.com/aws/smithy-go/middleware"
  9. smithyhttp "github.com/aws/smithy-go/transport/http"
  10. "net/http"
  11. "time"
  12. )
  13. // HTTPSigner is SigV4a HTTP signer implementation
  14. type HTTPSigner interface {
  15. SignHTTP(ctx context.Context, credentials Credentials, r *http.Request, payloadHash string, service string, regionSet []string, signingTime time.Time, optfns ...func(*SignerOptions)) error
  16. }
  17. // SignHTTPRequestMiddlewareOptions is the middleware options for constructing a SignHTTPRequestMiddleware.
  18. type SignHTTPRequestMiddlewareOptions struct {
  19. Credentials CredentialsProvider
  20. Signer HTTPSigner
  21. LogSigning bool
  22. }
  23. // SignHTTPRequestMiddleware is a middleware for signing an HTTP request using SigV4a.
  24. type SignHTTPRequestMiddleware struct {
  25. credentials CredentialsProvider
  26. signer HTTPSigner
  27. logSigning bool
  28. }
  29. // NewSignHTTPRequestMiddleware constructs a SignHTTPRequestMiddleware using the given SignHTTPRequestMiddlewareOptions.
  30. func NewSignHTTPRequestMiddleware(options SignHTTPRequestMiddlewareOptions) *SignHTTPRequestMiddleware {
  31. return &SignHTTPRequestMiddleware{
  32. credentials: options.Credentials,
  33. signer: options.Signer,
  34. logSigning: options.LogSigning,
  35. }
  36. }
  37. // ID the middleware identifier.
  38. func (s *SignHTTPRequestMiddleware) ID() string {
  39. return "Signing"
  40. }
  41. // HandleFinalize signs an HTTP request using SigV4a.
  42. func (s *SignHTTPRequestMiddleware) HandleFinalize(
  43. ctx context.Context, in middleware.FinalizeInput, next middleware.FinalizeHandler,
  44. ) (
  45. out middleware.FinalizeOutput, metadata middleware.Metadata, err error,
  46. ) {
  47. if !hasCredentialProvider(s.credentials) {
  48. return next.HandleFinalize(ctx, in)
  49. }
  50. req, ok := in.Request.(*smithyhttp.Request)
  51. if !ok {
  52. return out, metadata, fmt.Errorf("unexpected request middleware type %T", in.Request)
  53. }
  54. signingName, signingRegion := awsmiddleware.GetSigningName(ctx), awsmiddleware.GetSigningRegion(ctx)
  55. payloadHash := v4.GetPayloadHash(ctx)
  56. if len(payloadHash) == 0 {
  57. return out, metadata, &SigningError{Err: fmt.Errorf("computed payload hash missing from context")}
  58. }
  59. credentials, err := s.credentials.RetrievePrivateKey(ctx)
  60. if err != nil {
  61. return out, metadata, &SigningError{Err: fmt.Errorf("failed to retrieve credentials: %w", err)}
  62. }
  63. signerOptions := []func(o *SignerOptions){
  64. func(o *SignerOptions) {
  65. o.Logger = middleware.GetLogger(ctx)
  66. o.LogSigning = s.logSigning
  67. },
  68. }
  69. // existing DisableURIPathEscaping is equivalent in purpose
  70. // to authentication scheme property DisableDoubleEncoding
  71. disableDoubleEncoding, overridden := internalauth.GetDisableDoubleEncoding(ctx)
  72. if overridden {
  73. signerOptions = append(signerOptions, func(o *SignerOptions) {
  74. o.DisableURIPathEscaping = disableDoubleEncoding
  75. })
  76. }
  77. err = s.signer.SignHTTP(ctx, credentials, req.Request, payloadHash, signingName, []string{signingRegion}, time.Now().UTC(), signerOptions...)
  78. if err != nil {
  79. return out, metadata, &SigningError{Err: fmt.Errorf("failed to sign http request, %w", err)}
  80. }
  81. return next.HandleFinalize(ctx, in)
  82. }
  83. func hasCredentialProvider(p CredentialsProvider) bool {
  84. if p == nil {
  85. return false
  86. }
  87. return true
  88. }
  89. // RegisterSigningMiddleware registers the SigV4a signing middleware to the stack. If a signing middleware is already
  90. // present, this provided middleware will be swapped. Otherwise the middleware will be added at the tail of the
  91. // finalize step.
  92. func RegisterSigningMiddleware(stack *middleware.Stack, signingMiddleware *SignHTTPRequestMiddleware) (err error) {
  93. const signedID = "Signing"
  94. _, present := stack.Finalize.Get(signedID)
  95. if present {
  96. _, err = stack.Finalize.Swap(signedID, signingMiddleware)
  97. } else {
  98. err = stack.Finalize.Add(signingMiddleware, middleware.After)
  99. }
  100. return err
  101. }