1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
|
package checksum
import (
"context"
"fmt"
"strings"
"github.com/aws/smithy-go"
"github.com/aws/smithy-go/logging"
"github.com/aws/smithy-go/middleware"
smithyhttp "github.com/aws/smithy-go/transport/http"
)
// outputValidationAlgorithmsUsedKey is the metadata key for indexing the algorithms
// that were used, by the middleware's validation.
type outputValidationAlgorithmsUsedKey struct{}
// GetOutputValidationAlgorithmsUsed returns the checksum algorithms used
// stored in the middleware Metadata. Returns false if no algorithms were
// stored in the Metadata.
func GetOutputValidationAlgorithmsUsed(m middleware.Metadata) ([]string, bool) {
vs, ok := m.Get(outputValidationAlgorithmsUsedKey{}).([]string)
return vs, ok
}
// SetOutputValidationAlgorithmsUsed stores the checksum algorithms used in the
// middleware Metadata.
func SetOutputValidationAlgorithmsUsed(m *middleware.Metadata, vs []string) {
m.Set(outputValidationAlgorithmsUsedKey{}, vs)
}
// validateOutputPayloadChecksum middleware computes payload checksum of the
// received response and validates with checksum returned by the service.
type validateOutputPayloadChecksum struct {
// Algorithms represents a priority-ordered list of valid checksum
// algorithm that should be validated when present in HTTP response
// headers.
Algorithms []Algorithm
// IgnoreMultipartValidation indicates multipart checksums ending with "-#"
// will be ignored.
IgnoreMultipartValidation bool
// When set the middleware will log when output does not have checksum or
// algorithm to validate.
LogValidationSkipped bool
// When set the middleware will log when the output contains a multipart
// checksum that was, skipped and not validated.
LogMultipartValidationSkipped bool
}
func (m *validateOutputPayloadChecksum) ID() string {
return "AWSChecksum:ValidateOutputPayloadChecksum"
}
// HandleDeserialize is a Deserialize middleware that wraps the HTTP response
// body with an io.ReadCloser that will validate the its checksum.
func (m *validateOutputPayloadChecksum) HandleDeserialize(
ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler,
) (
out middleware.DeserializeOutput, metadata middleware.Metadata, err error,
) {
out, metadata, err = next.HandleDeserialize(ctx, in)
if err != nil {
return out, metadata, err
}
// If there is no validation mode specified nothing is supported.
if mode := getContextOutputValidationMode(ctx); mode != "ENABLED" {
return out, metadata, err
}
response, ok := out.RawResponse.(*smithyhttp.Response)
if !ok {
return out, metadata, &smithy.DeserializationError{
Err: fmt.Errorf("unknown transport type %T", out.RawResponse),
}
}
var expectedChecksum string
var algorithmToUse Algorithm
for _, algorithm := range m.Algorithms {
value := response.Header.Get(AlgorithmHTTPHeader(algorithm))
if len(value) == 0 {
continue
}
expectedChecksum = value
algorithmToUse = algorithm
}
// TODO this must validate the validation mode is set to enabled.
logger := middleware.GetLogger(ctx)
// Skip validation if no checksum algorithm or checksum is available.
if len(expectedChecksum) == 0 || len(algorithmToUse) == 0 {
if m.LogValidationSkipped {
// TODO this probably should have more information about the
// operation output that won't be validated.
logger.Logf(logging.Warn,
"Response has no supported checksum. Not validating response payload.")
}
return out, metadata, nil
}
// Ignore multipart validation
if m.IgnoreMultipartValidation && strings.Contains(expectedChecksum, "-") {
if m.LogMultipartValidationSkipped {
// TODO this probably should have more information about the
// operation output that won't be validated.
logger.Logf(logging.Warn, "Skipped validation of multipart checksum.")
}
return out, metadata, nil
}
body, err := newValidateChecksumReader(response.Body, algorithmToUse, expectedChecksum)
if err != nil {
return out, metadata, fmt.Errorf("failed to create checksum validation reader, %w", err)
}
response.Body = body
// Update the metadata to include the set of the checksum algorithms that
// will be validated.
SetOutputValidationAlgorithmsUsed(&metadata, []string{
string(algorithmToUse),
})
return out, metadata, nil
}
|