mirror of
https://github.com/minio/minio.git
synced 2024-12-24 06:05:55 -05:00
SignatureV4 validation with Metadata in the presignedUrl (#5894)
The `X-Amz-Meta-`/`X-Minio-Meta-` will now be recognized in query string also. Fixes #5857 #5950
This commit is contained in:
parent
c9bc7e47b9
commit
44865596db
@ -601,7 +601,8 @@ func (api objectAPIHandlers) PostPolicyBucketHandler(w http.ResponseWriter, r *h
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Extract metadata to be saved from received Form.
|
// Extract metadata to be saved from received Form.
|
||||||
metadata, err := extractMetadataFromHeader(ctx, formValues)
|
metadata := make(map[string]string)
|
||||||
|
err = extractMetadataFromMap(ctx, formValues, metadata)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeErrorResponse(w, ErrInternalError, r.URL)
|
writeErrorResponse(w, ErrInternalError, r.URL)
|
||||||
return
|
return
|
||||||
|
@ -114,40 +114,54 @@ var userMetadataKeyPrefixes = []string{
|
|||||||
"X-Minio-Meta-",
|
"X-Minio-Meta-",
|
||||||
}
|
}
|
||||||
|
|
||||||
// extractMetadataFromHeader extracts metadata from HTTP header.
|
// extractMetadata extracts metadata from HTTP header and HTTP queryString.
|
||||||
func extractMetadataFromHeader(ctx context.Context, header http.Header) (map[string]string, error) {
|
func extractMetadata(ctx context.Context, r *http.Request) (metadata map[string]string, err error) {
|
||||||
if header == nil {
|
query := r.URL.Query()
|
||||||
logger.LogIf(ctx, errInvalidArgument)
|
header := r.Header
|
||||||
return nil, errInvalidArgument
|
metadata = make(map[string]string)
|
||||||
|
// Extract all query values.
|
||||||
|
err = extractMetadataFromMap(ctx, query, metadata)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
metadata := make(map[string]string)
|
|
||||||
|
|
||||||
|
// Extract all header values.
|
||||||
|
err = extractMetadataFromMap(ctx, header, metadata)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Success.
|
||||||
|
return metadata, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// extractMetadata extracts metadata from map values.
|
||||||
|
func extractMetadataFromMap(ctx context.Context, v map[string][]string, m map[string]string) error {
|
||||||
|
if v == nil {
|
||||||
|
logger.LogIf(ctx, errInvalidArgument)
|
||||||
|
return errInvalidArgument
|
||||||
|
}
|
||||||
// Save all supported headers.
|
// Save all supported headers.
|
||||||
for _, supportedHeader := range supportedHeaders {
|
for _, supportedHeader := range supportedHeaders {
|
||||||
canonicalHeader := http.CanonicalHeaderKey(supportedHeader)
|
if value, ok := v[http.CanonicalHeaderKey(supportedHeader)]; ok {
|
||||||
// HTTP headers are case insensitive, look for both canonical
|
m[supportedHeader] = value[0]
|
||||||
// and non canonical entries.
|
} else if value, ok := v[supportedHeader]; ok {
|
||||||
if _, ok := header[canonicalHeader]; ok {
|
m[supportedHeader] = value[0]
|
||||||
metadata[supportedHeader] = header.Get(canonicalHeader)
|
|
||||||
} else if _, ok := header[supportedHeader]; ok {
|
|
||||||
metadata[supportedHeader] = header.Get(supportedHeader)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for key := range v {
|
||||||
// Go through all other headers for any additional headers that needs to be saved.
|
|
||||||
for key := range header {
|
|
||||||
if key != http.CanonicalHeaderKey(key) {
|
|
||||||
logger.LogIf(ctx, errInvalidArgument)
|
|
||||||
return nil, errInvalidArgument
|
|
||||||
}
|
|
||||||
for _, prefix := range userMetadataKeyPrefixes {
|
for _, prefix := range userMetadataKeyPrefixes {
|
||||||
if strings.HasPrefix(key, prefix) {
|
if !strings.HasPrefix(strings.ToLower(key), strings.ToLower(prefix)) {
|
||||||
metadata[key] = header.Get(key)
|
continue
|
||||||
|
}
|
||||||
|
value, ok := v[key]
|
||||||
|
if ok {
|
||||||
|
m[key] = value[0]
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return metadata, nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// The Query string for the redirect URL the client is
|
// The Query string for the redirect URL the client is
|
||||||
|
@ -165,9 +165,9 @@ func TestExtractMetadataHeaders(t *testing.T) {
|
|||||||
"x-amz-meta-appid": []string{"amz-meta"},
|
"x-amz-meta-appid": []string{"amz-meta"},
|
||||||
},
|
},
|
||||||
metadata: map[string]string{
|
metadata: map[string]string{
|
||||||
"X-Amz-Meta-Appid": "amz-meta",
|
"x-amz-meta-appid": "amz-meta",
|
||||||
},
|
},
|
||||||
shouldFail: true,
|
shouldFail: false,
|
||||||
},
|
},
|
||||||
// Empty header input returns empty metadata.
|
// Empty header input returns empty metadata.
|
||||||
{
|
{
|
||||||
@ -179,7 +179,8 @@ func TestExtractMetadataHeaders(t *testing.T) {
|
|||||||
|
|
||||||
// Validate if the extracting headers.
|
// Validate if the extracting headers.
|
||||||
for i, testCase := range testCases {
|
for i, testCase := range testCases {
|
||||||
metadata, err := extractMetadataFromHeader(context.Background(), testCase.header)
|
metadata := make(map[string]string)
|
||||||
|
err := extractMetadataFromMap(context.Background(), testCase.header, metadata)
|
||||||
if err != nil && !testCase.shouldFail {
|
if err != nil && !testCase.shouldFail {
|
||||||
t.Fatalf("Test %d failed to extract metadata: %v", i+1, err)
|
t.Fatalf("Test %d failed to extract metadata: %v", i+1, err)
|
||||||
}
|
}
|
||||||
|
@ -313,7 +313,7 @@ func (api objectAPIHandlers) HeadObjectHandler(w http.ResponseWriter, r *http.Re
|
|||||||
|
|
||||||
// Extract metadata relevant for an CopyObject operation based on conditional
|
// Extract metadata relevant for an CopyObject operation based on conditional
|
||||||
// header values specified in X-Amz-Metadata-Directive.
|
// header values specified in X-Amz-Metadata-Directive.
|
||||||
func getCpObjMetadataFromHeader(ctx context.Context, header http.Header, userMeta map[string]string) (map[string]string, error) {
|
func getCpObjMetadataFromHeader(ctx context.Context, r *http.Request, userMeta map[string]string) (map[string]string, error) {
|
||||||
// Make a copy of the supplied metadata to avoid
|
// Make a copy of the supplied metadata to avoid
|
||||||
// to change the original one.
|
// to change the original one.
|
||||||
defaultMeta := make(map[string]string, len(userMeta))
|
defaultMeta := make(map[string]string, len(userMeta))
|
||||||
@ -323,13 +323,13 @@ func getCpObjMetadataFromHeader(ctx context.Context, header http.Header, userMet
|
|||||||
|
|
||||||
// if x-amz-metadata-directive says REPLACE then
|
// if x-amz-metadata-directive says REPLACE then
|
||||||
// we extract metadata from the input headers.
|
// we extract metadata from the input headers.
|
||||||
if isMetadataReplace(header) {
|
if isMetadataReplace(r.Header) {
|
||||||
return extractMetadataFromHeader(ctx, header)
|
return extractMetadata(ctx, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
// if x-amz-metadata-directive says COPY then we
|
// if x-amz-metadata-directive says COPY then we
|
||||||
// return the default metadata.
|
// return the default metadata.
|
||||||
if isMetadataCopy(header) {
|
if isMetadataCopy(r.Header) {
|
||||||
return defaultMeta, nil
|
return defaultMeta, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -514,7 +514,7 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re
|
|||||||
}
|
}
|
||||||
srcInfo.Writer = writer
|
srcInfo.Writer = writer
|
||||||
|
|
||||||
srcInfo.UserDefined, err = getCpObjMetadataFromHeader(ctx, r.Header, srcInfo.UserDefined)
|
srcInfo.UserDefined, err = getCpObjMetadataFromHeader(ctx, r, srcInfo.UserDefined)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
pipeWriter.CloseWithError(err)
|
pipeWriter.CloseWithError(err)
|
||||||
writeErrorResponse(w, ErrInternalError, r.URL)
|
writeErrorResponse(w, ErrInternalError, r.URL)
|
||||||
@ -699,12 +699,12 @@ func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Req
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract metadata to be saved from incoming HTTP header.
|
metadata, err := extractMetadata(ctx, r)
|
||||||
metadata, err := extractMetadataFromHeader(ctx, r.Header)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeErrorResponse(w, ErrInternalError, r.URL)
|
writeErrorResponse(w, ErrInternalError, r.URL)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if rAuthType == authTypeStreamingSigned {
|
if rAuthType == authTypeStreamingSigned {
|
||||||
if contentEncoding, ok := metadata["content-encoding"]; ok {
|
if contentEncoding, ok := metadata["content-encoding"]; ok {
|
||||||
contentEncoding = trimAwsChunkedContentEncoding(contentEncoding)
|
contentEncoding = trimAwsChunkedContentEncoding(contentEncoding)
|
||||||
@ -806,6 +806,7 @@ func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Req
|
|||||||
if api.CacheAPI() != nil && !hasSSECustomerHeader(r.Header) {
|
if api.CacheAPI() != nil && !hasSSECustomerHeader(r.Header) {
|
||||||
putObject = api.CacheAPI().PutObject
|
putObject = api.CacheAPI().PutObject
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the object..
|
// Create the object..
|
||||||
objInfo, err := putObject(ctx, bucket, object, hashReader, metadata)
|
objInfo, err := putObject(ctx, bucket, object, hashReader, metadata)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -901,7 +902,7 @@ func (api objectAPIHandlers) NewMultipartUploadHandler(w http.ResponseWriter, r
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Extract metadata that needs to be saved.
|
// Extract metadata that needs to be saved.
|
||||||
metadata, err := extractMetadataFromHeader(ctx, r.Header)
|
metadata, err := extractMetadata(ctx, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeErrorResponse(w, ErrInternalError, r.URL)
|
writeErrorResponse(w, ErrInternalError, r.URL)
|
||||||
return
|
return
|
||||||
|
@ -249,6 +249,12 @@ func doesPresignedSignatureMatch(hashedPayload string, r *http.Request, region s
|
|||||||
|
|
||||||
// Save other headers available in the request parameters.
|
// Save other headers available in the request parameters.
|
||||||
for k, v := range req.URL.Query() {
|
for k, v := range req.URL.Query() {
|
||||||
|
|
||||||
|
// Handle the metadata in presigned put query string
|
||||||
|
if strings.Contains(strings.ToLower(k), "x-amz-meta-") {
|
||||||
|
query.Set(k, v[0])
|
||||||
|
}
|
||||||
|
|
||||||
if strings.HasPrefix(strings.ToLower(k), "x-amz") {
|
if strings.HasPrefix(strings.ToLower(k), "x-amz") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -629,7 +629,7 @@ func (web *webAPIHandlers) Upload(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Extract incoming metadata if any.
|
// Extract incoming metadata if any.
|
||||||
metadata, err := extractMetadataFromHeader(context.Background(), r.Header)
|
metadata, err := extractMetadata(context.Background(), r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeErrorResponse(w, ErrInternalError, r.URL)
|
writeErrorResponse(w, ErrInternalError, r.URL)
|
||||||
return
|
return
|
||||||
|
Loading…
Reference in New Issue
Block a user