119 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			119 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			Go
		
	
	
	
| package v4a
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"fmt"
 | |
| 	awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware"
 | |
| 	v4 "github.com/aws/aws-sdk-go-v2/aws/signer/v4"
 | |
| 	internalauth "github.com/aws/aws-sdk-go-v2/internal/auth"
 | |
| 	"github.com/aws/smithy-go/middleware"
 | |
| 	smithyhttp "github.com/aws/smithy-go/transport/http"
 | |
| 	"net/http"
 | |
| 	"time"
 | |
| )
 | |
| 
 | |
| // HTTPSigner is SigV4a HTTP signer implementation
 | |
| type HTTPSigner interface {
 | |
| 	SignHTTP(ctx context.Context, credentials Credentials, r *http.Request, payloadHash string, service string, regionSet []string, signingTime time.Time, optfns ...func(*SignerOptions)) error
 | |
| }
 | |
| 
 | |
| // SignHTTPRequestMiddlewareOptions is the middleware options for constructing a SignHTTPRequestMiddleware.
 | |
| type SignHTTPRequestMiddlewareOptions struct {
 | |
| 	Credentials CredentialsProvider
 | |
| 	Signer      HTTPSigner
 | |
| 	LogSigning  bool
 | |
| }
 | |
| 
 | |
| // SignHTTPRequestMiddleware is a middleware for signing an HTTP request using SigV4a.
 | |
| type SignHTTPRequestMiddleware struct {
 | |
| 	credentials CredentialsProvider
 | |
| 	signer      HTTPSigner
 | |
| 	logSigning  bool
 | |
| }
 | |
| 
 | |
| // NewSignHTTPRequestMiddleware constructs a SignHTTPRequestMiddleware using the given SignHTTPRequestMiddlewareOptions.
 | |
| func NewSignHTTPRequestMiddleware(options SignHTTPRequestMiddlewareOptions) *SignHTTPRequestMiddleware {
 | |
| 	return &SignHTTPRequestMiddleware{
 | |
| 		credentials: options.Credentials,
 | |
| 		signer:      options.Signer,
 | |
| 		logSigning:  options.LogSigning,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // ID the middleware identifier.
 | |
| func (s *SignHTTPRequestMiddleware) ID() string {
 | |
| 	return "Signing"
 | |
| }
 | |
| 
 | |
| // HandleFinalize signs an HTTP request using SigV4a.
 | |
| func (s *SignHTTPRequestMiddleware) HandleFinalize(
 | |
| 	ctx context.Context, in middleware.FinalizeInput, next middleware.FinalizeHandler,
 | |
| ) (
 | |
| 	out middleware.FinalizeOutput, metadata middleware.Metadata, err error,
 | |
| ) {
 | |
| 	if !hasCredentialProvider(s.credentials) {
 | |
| 		return next.HandleFinalize(ctx, in)
 | |
| 	}
 | |
| 
 | |
| 	req, ok := in.Request.(*smithyhttp.Request)
 | |
| 	if !ok {
 | |
| 		return out, metadata, fmt.Errorf("unexpected request middleware type %T", in.Request)
 | |
| 	}
 | |
| 
 | |
| 	signingName, signingRegion := awsmiddleware.GetSigningName(ctx), awsmiddleware.GetSigningRegion(ctx)
 | |
| 	payloadHash := v4.GetPayloadHash(ctx)
 | |
| 	if len(payloadHash) == 0 {
 | |
| 		return out, metadata, &SigningError{Err: fmt.Errorf("computed payload hash missing from context")}
 | |
| 	}
 | |
| 
 | |
| 	credentials, err := s.credentials.RetrievePrivateKey(ctx)
 | |
| 	if err != nil {
 | |
| 		return out, metadata, &SigningError{Err: fmt.Errorf("failed to retrieve credentials: %w", err)}
 | |
| 	}
 | |
| 
 | |
| 	signerOptions := []func(o *SignerOptions){
 | |
| 		func(o *SignerOptions) {
 | |
| 			o.Logger = middleware.GetLogger(ctx)
 | |
| 			o.LogSigning = s.logSigning
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	// existing DisableURIPathEscaping is equivalent in purpose
 | |
| 	// to authentication scheme property DisableDoubleEncoding
 | |
| 	disableDoubleEncoding, overridden := internalauth.GetDisableDoubleEncoding(ctx)
 | |
| 	if overridden {
 | |
| 		signerOptions = append(signerOptions, func(o *SignerOptions) {
 | |
| 			o.DisableURIPathEscaping = disableDoubleEncoding
 | |
| 		})
 | |
| 	}
 | |
| 
 | |
| 	err = s.signer.SignHTTP(ctx, credentials, req.Request, payloadHash, signingName, []string{signingRegion}, time.Now().UTC(), signerOptions...)
 | |
| 	if err != nil {
 | |
| 		return out, metadata, &SigningError{Err: fmt.Errorf("failed to sign http request, %w", err)}
 | |
| 	}
 | |
| 
 | |
| 	return next.HandleFinalize(ctx, in)
 | |
| }
 | |
| 
 | |
| func hasCredentialProvider(p CredentialsProvider) bool {
 | |
| 	if p == nil {
 | |
| 		return false
 | |
| 	}
 | |
| 
 | |
| 	return true
 | |
| }
 | |
| 
 | |
| // RegisterSigningMiddleware registers the SigV4a signing middleware to the stack. If a signing middleware is already
 | |
| // present, this provided middleware will be swapped. Otherwise the middleware will be added at the tail of the
 | |
| // finalize step.
 | |
| func RegisterSigningMiddleware(stack *middleware.Stack, signingMiddleware *SignHTTPRequestMiddleware) (err error) {
 | |
| 	const signedID = "Signing"
 | |
| 	_, present := stack.Finalize.Get(signedID)
 | |
| 	if present {
 | |
| 		_, err = stack.Finalize.Swap(signedID, signingMiddleware)
 | |
| 	} else {
 | |
| 		err = stack.Finalize.Add(signingMiddleware, middleware.After)
 | |
| 	}
 | |
| 	return err
 | |
| }
 |