mirror of
https://github.com/versity/versitygw.git
synced 2025-12-23 05:05:16 +00:00
We have some leftover constants from some previous changes. This just cleans up all that are no longer needed.
212 lines
7.0 KiB
Go
212 lines
7.0 KiB
Go
// Copyright 2024 Versity Software
|
|
// This file is licensed under the Apache License, Version 2.0
|
|
// (the "License"); you may not use this file except in compliance
|
|
// with the License. You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing,
|
|
// software distributed under the License is distributed on an
|
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
// KIND, either express or implied. See the License for the
|
|
// specific language governing permissions and limitations
|
|
// under the License.
|
|
|
|
package utils
|
|
|
|
import (
|
|
"encoding/hex"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/gofiber/fiber/v2"
|
|
"github.com/versity/versitygw/debuglogger"
|
|
"github.com/versity/versitygw/s3err"
|
|
)
|
|
|
|
const (
|
|
maxObjSizeLimit = 5 * 1024 * 1024 * 1024 // 5gb
|
|
)
|
|
|
|
type payloadType string
|
|
|
|
const (
|
|
payloadTypeUnsigned payloadType = "UNSIGNED-PAYLOAD"
|
|
payloadTypeStreamingUnsignedTrailer payloadType = "STREAMING-UNSIGNED-PAYLOAD-TRAILER"
|
|
payloadTypeStreamingSigned payloadType = "STREAMING-AWS4-HMAC-SHA256-PAYLOAD"
|
|
payloadTypeStreamingSignedTrailer payloadType = "STREAMING-AWS4-HMAC-SHA256-PAYLOAD-TRAILER"
|
|
payloadTypeStreamingEcdsa payloadType = "STREAMING-AWS4-ECDSA-P256-SHA256-PAYLOAD"
|
|
payloadTypeStreamingEcdsaTrailer payloadType = "STREAMING-AWS4-ECDSA-P256-SHA256-PAYLOAD-TRAILER"
|
|
)
|
|
|
|
func getPayloadTypeNotSupportedErr(p payloadType) error {
|
|
return s3err.APIError{
|
|
HTTPStatusCode: http.StatusNotImplemented,
|
|
Code: "NotImplemented",
|
|
Description: fmt.Sprintf("The chunk encoding algorithm %v is not supported.", p),
|
|
}
|
|
}
|
|
|
|
var (
|
|
specialValues = map[payloadType]bool{
|
|
payloadTypeUnsigned: true,
|
|
payloadTypeStreamingUnsignedTrailer: true,
|
|
payloadTypeStreamingSigned: true,
|
|
payloadTypeStreamingSignedTrailer: true,
|
|
payloadTypeStreamingEcdsa: true,
|
|
payloadTypeStreamingEcdsaTrailer: true,
|
|
}
|
|
)
|
|
|
|
func (pt payloadType) isValid() bool {
|
|
return pt == payloadTypeUnsigned ||
|
|
pt == payloadTypeStreamingUnsignedTrailer ||
|
|
pt == payloadTypeStreamingSigned ||
|
|
pt == payloadTypeStreamingSignedTrailer ||
|
|
pt == payloadTypeStreamingEcdsa ||
|
|
pt == payloadTypeStreamingEcdsaTrailer
|
|
}
|
|
|
|
type checksumType string
|
|
|
|
const (
|
|
checksumTypeCrc32 checksumType = "x-amz-checksum-crc32"
|
|
checksumTypeCrc32c checksumType = "x-amz-checksum-crc32c"
|
|
checksumTypeSha1 checksumType = "x-amz-checksum-sha1"
|
|
checksumTypeSha256 checksumType = "x-amz-checksum-sha256"
|
|
checksumTypeCrc64nvme checksumType = "x-amz-checksum-crc64nvme"
|
|
)
|
|
|
|
func (c checksumType) isValid() bool {
|
|
return c == checksumTypeCrc32 ||
|
|
c == checksumTypeCrc32c ||
|
|
c == checksumTypeSha1 ||
|
|
c == checksumTypeSha256 ||
|
|
c == checksumTypeCrc64nvme
|
|
}
|
|
|
|
// Extracts and validates the checksum type from the 'X-Amz-Trailer' header
|
|
func ExtractChecksumType(ctx *fiber.Ctx) (checksumType, error) {
|
|
trailer := ctx.Get("X-Amz-Trailer")
|
|
chType := checksumType(strings.ToLower(trailer))
|
|
if chType != "" && !chType.isValid() {
|
|
debuglogger.Logf("invalid value for 'X-Amz-Trailer': %v", chType)
|
|
return "", s3err.GetAPIError(s3err.ErrTrailerHeaderNotSupported)
|
|
}
|
|
|
|
return chType, nil
|
|
}
|
|
|
|
// IsSpecialPayload checks for special authorization types
|
|
func IsSpecialPayload(str string) bool {
|
|
return specialValues[payloadType(str)]
|
|
}
|
|
|
|
// IsValidSh256PayloadHeader checks if the provided x-amz-content-sha256
|
|
// paylod header is valid special paylod type or a valid sh256 hash
|
|
func IsValidSh256PayloadHeader(value string) bool {
|
|
// empty header is valid
|
|
if value == "" {
|
|
return true
|
|
}
|
|
// special values are valid
|
|
if specialValues[payloadType(value)] {
|
|
return true
|
|
}
|
|
|
|
// check to be a valid sha256
|
|
if len(value) != 64 {
|
|
return false
|
|
}
|
|
|
|
// decode the string as hex
|
|
_, err := hex.DecodeString(value)
|
|
return err == nil
|
|
}
|
|
|
|
// Checks if the provided string is unsigned payload trailer type
|
|
func IsUnsignedStreamingPayload(str string) bool {
|
|
return payloadType(str) == payloadTypeStreamingUnsignedTrailer
|
|
}
|
|
|
|
// IsAnonymousPayloadHashSupported returns error if payload hash
|
|
// is streaming signed.
|
|
// e.g.
|
|
// "STREAMING-AWS4-HMAC-SHA256-PAYLOAD", "STREAMING-AWS4-ECDSA-P256-SHA256-PAYLOAD" ...
|
|
func IsAnonymousPayloadHashSupported(hash string) error {
|
|
switch payloadType(hash) {
|
|
case payloadTypeStreamingEcdsa, payloadTypeStreamingEcdsaTrailer, payloadTypeStreamingSigned, payloadTypeStreamingSignedTrailer:
|
|
return s3err.GetAPIError(s3err.ErrUnsupportedAnonymousSignedStreaming)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// IsUnsignedPaylod checks if the provided payload hash type
|
|
// is "UNSIGNED-PAYLOAD"
|
|
func IsUnsignedPaylod(hash string) bool {
|
|
return hash == string(payloadTypeUnsigned)
|
|
}
|
|
|
|
// IsChunkEncoding checks for streaming/unsigned authorization types
|
|
func IsStreamingPayload(str string) bool {
|
|
pt := payloadType(str)
|
|
return pt == payloadTypeStreamingUnsignedTrailer ||
|
|
pt == payloadTypeStreamingSigned ||
|
|
pt == payloadTypeStreamingSignedTrailer
|
|
}
|
|
|
|
func NewChunkReader(ctx *fiber.Ctx, r io.Reader, authdata AuthData, region, secret string, date time.Time) (io.Reader, error) {
|
|
decContLengthStr := ctx.Get("X-Amz-Decoded-Content-Length")
|
|
if decContLengthStr == "" {
|
|
debuglogger.Logf("missing required header 'X-Amz-Decoded-Content-Length'")
|
|
return nil, s3err.GetAPIError(s3err.ErrMissingContentLength)
|
|
}
|
|
decContLength, err := strconv.ParseInt(decContLengthStr, 10, 64)
|
|
//TODO: not sure if InvalidRequest should be returned in this case
|
|
if err != nil {
|
|
debuglogger.Logf("invalid value for 'X-Amz-Decoded-Content-Length': %v", decContLengthStr)
|
|
return nil, s3err.GetAPIError(s3err.ErrInvalidRequest)
|
|
}
|
|
|
|
if decContLength > maxObjSizeLimit {
|
|
debuglogger.Logf("the object size exceeds the allowed limit: (size): %v, (limit): %v", decContLength, int64(maxObjSizeLimit))
|
|
return nil, s3err.GetAPIError(s3err.ErrEntityTooLarge)
|
|
}
|
|
|
|
contentSha256 := payloadType(ctx.Get("X-Amz-Content-Sha256"))
|
|
if !contentSha256.isValid() {
|
|
//TODO: Add proper APIError
|
|
debuglogger.Logf("invalid value for 'X-Amz-Content-Sha256': %v", contentSha256)
|
|
return nil, fmt.Errorf("invalid x-amz-content-sha256: %v", string(contentSha256))
|
|
}
|
|
|
|
checksumType, err := ExtractChecksumType(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if contentSha256 != payloadTypeStreamingSigned && checksumType == "" {
|
|
debuglogger.Logf("empty value for required trailer header 'X-Amz-Trailer': %v", checksumType)
|
|
return nil, s3err.GetAPIError(s3err.ErrTrailerHeaderNotSupported)
|
|
}
|
|
|
|
switch contentSha256 {
|
|
case payloadTypeStreamingUnsignedTrailer:
|
|
return NewUnsignedChunkReader(r, checksumType)
|
|
case payloadTypeStreamingSignedTrailer:
|
|
return NewSignedChunkReader(r, authdata, region, secret, date, checksumType)
|
|
case payloadTypeStreamingSigned:
|
|
return NewSignedChunkReader(r, authdata, region, secret, date, "")
|
|
// return not supported for:
|
|
// - STREAMING-AWS4-ECDSA-P256-SHA256-PAYLOAD
|
|
// - STREAMING-AWS4-ECDSA-P256-SHA256-PAYLOAD-TRAILER
|
|
default:
|
|
debuglogger.Logf("unsupported chunk reader algorithm: %v", contentSha256)
|
|
return nil, getPayloadTypeNotSupportedErr(contentSha256)
|
|
}
|
|
}
|