acl: Support PUT calls with success for 'private' ACL's (#9000)

Add dummy calls which respond success when ACL's
are set to be private and fails, if user tries
to change them from their default 'private'

Some applications such as nuxeo may have an
unnecessary requirement for this operation,
we support this anyways such that don't have
to fully implement the functionality just that
we can respond with success for default ACLs
master
Harshavardhana 5 years ago committed by GitHub
parent 9f298d2311
commit 712e82344c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 134
      cmd/acl-handlers.go
  2. 13
      cmd/api-errors.go
  3. 4
      cmd/api-router.go
  4. 9
      cmd/auth-handler.go
  5. 13
      cmd/bucket-encryption-handlers.go
  6. 2
      cmd/bucket-handlers.go
  7. 13
      cmd/bucket-lifecycle-handler.go
  8. 30
      cmd/generic-handlers.go
  9. 3
      cmd/http/headers.go
  10. 3
      cmd/object-handlers.go
  11. 10
      cmd/server_test.go
  12. 4
      cmd/utils.go

@ -1,5 +1,5 @@
/* /*
* MinIO Cloud Storage, (C) 2018 MinIO, Inc. * MinIO Cloud Storage, (C) 2018-2020 MinIO, Inc.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -18,10 +18,12 @@ package cmd
import ( import (
"encoding/xml" "encoding/xml"
"io"
"net/http" "net/http"
"net/url" "net/url"
"github.com/gorilla/mux" "github.com/gorilla/mux"
xhttp "github.com/minio/minio/cmd/http"
"github.com/minio/minio/cmd/logger" "github.com/minio/minio/cmd/logger"
"github.com/minio/minio/pkg/bucket/policy" "github.com/minio/minio/pkg/bucket/policy"
) )
@ -51,6 +53,71 @@ type accessControlPolicy struct {
} `xml:"AccessControlList"` } `xml:"AccessControlList"`
} }
// PutBucketACLHandler - PUT Bucket ACL
// -----------------
// This operation uses the ACL subresource
// to set ACL for a bucket, this is a dummy call
// only responds success if the ACL is private.
func (api objectAPIHandlers) PutBucketACLHandler(w http.ResponseWriter, r *http.Request) {
ctx := newContext(r, w, "PutBucketACL")
defer logger.AuditLog(w, r, "PutBucketACL", mustGetClaimsFromToken(r))
vars := mux.Vars(r)
bucket := vars["bucket"]
objAPI := api.ObjectAPI()
if objAPI == nil {
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL, guessIsBrowserReq(r))
return
}
// Allow putBucketACL if policy action is set, since this is a dummy call
// we are simply re-purposing the bucketPolicyAction.
if s3Error := checkRequestAuthType(ctx, r, policy.PutBucketPolicyAction, bucket, ""); s3Error != ErrNone {
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL, guessIsBrowserReq(r))
return
}
// Before proceeding validate if bucket exists.
_, err := objAPI.GetBucketInfo(ctx, bucket)
if err != nil {
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
return
}
aclHeader := r.Header.Get(xhttp.AmzACL)
if aclHeader == "" {
acl := &accessControlPolicy{}
if err = xmlDecoder(r.Body, acl, r.ContentLength); err != nil {
if err == io.EOF {
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrMissingSecurityHeader),
r.URL, guessIsBrowserReq(r))
return
}
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
return
}
if len(acl.AccessControlList.Grants) == 0 {
writeErrorResponse(ctx, w, toAPIError(ctx, errInvalidArgument), r.URL, guessIsBrowserReq(r))
return
}
if acl.AccessControlList.Grants[0].Permission != "FULL_CONTROL" {
writeErrorResponse(ctx, w, toAPIError(ctx, errInvalidArgument), r.URL, guessIsBrowserReq(r))
return
}
}
if aclHeader != "" && aclHeader != "private" {
writeErrorResponse(ctx, w, toAPIError(ctx, errInvalidArgument), r.URL, guessIsBrowserReq(r))
return
}
w.(http.Flusher).Flush()
}
// GetBucketACLHandler - GET Bucket ACL // GetBucketACLHandler - GET Bucket ACL
// ----------------- // -----------------
// This operation uses the ACL // This operation uses the ACL
@ -100,6 +167,71 @@ func (api objectAPIHandlers) GetBucketACLHandler(w http.ResponseWriter, r *http.
w.(http.Flusher).Flush() w.(http.Flusher).Flush()
} }
// PutObjectACLHandler - PUT Object ACL
// -----------------
// This operation uses the ACL subresource
// to set ACL for a bucket, this is a dummy call
// only responds success if the ACL is private.
func (api objectAPIHandlers) PutObjectACLHandler(w http.ResponseWriter, r *http.Request) {
ctx := newContext(r, w, "PutObjectACL")
defer logger.AuditLog(w, r, "PutObjectACL", mustGetClaimsFromToken(r))
vars := mux.Vars(r)
bucket := vars["bucket"]
object, err := url.PathUnescape(vars["object"])
if err != nil {
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
return
}
objAPI := api.ObjectAPI()
if objAPI == nil {
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL, guessIsBrowserReq(r))
return
}
// Allow putObjectACL if policy action is set, since this is a dummy call
// we are simply re-purposing the bucketPolicyAction.
if s3Error := checkRequestAuthType(ctx, r, policy.PutBucketPolicyAction, bucket, ""); s3Error != ErrNone {
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL, guessIsBrowserReq(r))
return
}
// Before proceeding validate if object exists.
_, err = objAPI.GetObjectInfo(ctx, bucket, object, ObjectOptions{})
if err != nil {
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
return
}
aclHeader := r.Header.Get(xhttp.AmzACL)
if aclHeader == "" {
acl := &accessControlPolicy{}
if err = xmlDecoder(r.Body, acl, r.ContentLength); err != nil {
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
return
}
if len(acl.AccessControlList.Grants) == 0 {
writeErrorResponse(ctx, w, toAPIError(ctx, errInvalidArgument), r.URL, guessIsBrowserReq(r))
return
}
if acl.AccessControlList.Grants[0].Permission != "FULL_CONTROL" {
writeErrorResponse(ctx, w, toAPIError(ctx, errInvalidArgument), r.URL, guessIsBrowserReq(r))
return
}
}
if aclHeader != "" && aclHeader != "private" {
writeErrorResponse(ctx, w, toAPIError(ctx, errInvalidArgument), r.URL, guessIsBrowserReq(r))
return
}
w.(http.Flusher).Flush()
}
// GetObjectACLHandler - GET Object ACL // GetObjectACLHandler - GET Object ACL
// ----------------- // -----------------
// This operation uses the ACL // This operation uses the ACL

@ -95,6 +95,7 @@ const (
ErrMissingContentLength ErrMissingContentLength
ErrMissingContentMD5 ErrMissingContentMD5
ErrMissingRequestBodyError ErrMissingRequestBodyError
ErrMissingSecurityHeader
ErrNoSuchBucket ErrNoSuchBucket
ErrNoSuchBucketPolicy ErrNoSuchBucketPolicy
ErrNoSuchBucketLifecycle ErrNoSuchBucketLifecycle
@ -471,6 +472,11 @@ var errorCodes = errorCodeMap{
Description: "Missing required header for this request: Content-Md5.", Description: "Missing required header for this request: Content-Md5.",
HTTPStatusCode: http.StatusBadRequest, HTTPStatusCode: http.StatusBadRequest,
}, },
ErrMissingSecurityHeader: {
Code: "MissingSecurityHeader",
Description: "Your request was missing a required header",
HTTPStatusCode: http.StatusBadRequest,
},
ErrMissingRequestBodyError: { ErrMissingRequestBodyError: {
Code: "MissingRequestBodyError", Code: "MissingRequestBodyError",
Description: "Request body is empty.", Description: "Request body is empty.",
@ -1803,6 +1809,13 @@ func toAPIError(ctx context.Context, err error) APIError {
// their internal error types. This code is only // their internal error types. This code is only
// useful with gateway implementations. // useful with gateway implementations.
switch e := err.(type) { switch e := err.(type) {
case *xml.SyntaxError:
apiErr = APIError{
Code: "MalformedXML",
Description: fmt.Sprintf("%s (%s)", errorCodes[ErrMalformedXML].Description,
e.Error()),
HTTPStatusCode: errorCodes[ErrMalformedXML].HTTPStatusCode,
}
case url.EscapeError: case url.EscapeError:
apiErr = APIError{ apiErr = APIError{
Code: "XMinioInvalidObjectName", Code: "XMinioInvalidObjectName",

@ -105,6 +105,8 @@ func registerAPIRouter(router *mux.Router, encryptionEnabled, allowSSEKMS bool)
bucket.Methods(http.MethodDelete).Path("/{object:.+}").HandlerFunc(collectAPIStats("abortmultipartupload", httpTraceAll(api.AbortMultipartUploadHandler))).Queries("uploadId", "{uploadId:.*}") bucket.Methods(http.MethodDelete).Path("/{object:.+}").HandlerFunc(collectAPIStats("abortmultipartupload", httpTraceAll(api.AbortMultipartUploadHandler))).Queries("uploadId", "{uploadId:.*}")
// GetObjectACL - this is a dummy call. // GetObjectACL - this is a dummy call.
bucket.Methods(http.MethodGet).Path("/{object:.+}").HandlerFunc(collectAPIStats("getobjectacl", httpTraceHdrs(api.GetObjectACLHandler))).Queries("acl", "") bucket.Methods(http.MethodGet).Path("/{object:.+}").HandlerFunc(collectAPIStats("getobjectacl", httpTraceHdrs(api.GetObjectACLHandler))).Queries("acl", "")
// PutObjectACL - this is a dummy call.
bucket.Methods(http.MethodPut).Path("/{object:.+}").HandlerFunc(collectAPIStats("putobjectacl", httpTraceHdrs(api.PutObjectACLHandler))).Queries("acl", "")
// GetObjectTagging // GetObjectTagging
bucket.Methods(http.MethodGet).Path("/{object:.+}").HandlerFunc(collectAPIStats("getobjecttagging", httpTraceHdrs(api.GetObjectTaggingHandler))).Queries("tagging", "") bucket.Methods(http.MethodGet).Path("/{object:.+}").HandlerFunc(collectAPIStats("getobjecttagging", httpTraceHdrs(api.GetObjectTaggingHandler))).Queries("tagging", "")
// PutObjectTagging // PutObjectTagging
@ -144,6 +146,8 @@ func registerAPIRouter(router *mux.Router, encryptionEnabled, allowSSEKMS bool)
// Dummy Bucket Calls // Dummy Bucket Calls
// GetBucketACL -- this is a dummy call. // GetBucketACL -- this is a dummy call.
bucket.Methods(http.MethodGet).HandlerFunc(collectAPIStats("getbucketacl", httpTraceAll(api.GetBucketACLHandler))).Queries("acl", "") bucket.Methods(http.MethodGet).HandlerFunc(collectAPIStats("getbucketacl", httpTraceAll(api.GetBucketACLHandler))).Queries("acl", "")
// PutBucketACL -- this is a dummy call.
bucket.Methods(http.MethodPut).HandlerFunc(collectAPIStats("putbucketacl", httpTraceAll(api.PutBucketACLHandler))).Queries("acl", "")
// GetBucketCors - this is a dummy call. // GetBucketCors - this is a dummy call.
bucket.Methods(http.MethodGet).HandlerFunc(collectAPIStats("getbucketcors", httpTraceAll(api.GetBucketCorsHandler))).Queries("cors", "") bucket.Methods(http.MethodGet).HandlerFunc(collectAPIStats("getbucketcors", httpTraceAll(api.GetBucketCorsHandler))).Queries("cors", "")
// GetBucketWebsiteHandler - this is a dummy call. // GetBucketWebsiteHandler - this is a dummy call.

@ -380,12 +380,11 @@ func isReqAuthenticated(ctx context.Context, r *http.Request, region string, sty
err error err error
contentMD5, contentSHA256 []byte contentMD5, contentSHA256 []byte
) )
// Extract 'Content-Md5' if present. // Extract 'Content-Md5' if present.
if _, ok := r.Header[xhttp.ContentMD5]; ok { contentMD5, err = checkValidMD5(r.Header)
contentMD5, err = base64.StdEncoding.Strict().DecodeString(r.Header.Get(xhttp.ContentMD5)) if err != nil {
if err != nil || len(contentMD5) == 0 { return ErrInvalidDigest
return ErrInvalidDigest
}
} }
// Extract either 'X-Amz-Content-Sha256' header or 'X-Amz-Content-Sha256' query parameter (if V4 presigned) // Extract either 'X-Amz-Content-Sha256' header or 'X-Amz-Content-Sha256' query parameter (if V4 presigned)

@ -22,6 +22,7 @@ import (
"net/http" "net/http"
"github.com/gorilla/mux" "github.com/gorilla/mux"
xhttp "github.com/minio/minio/cmd/http"
"github.com/minio/minio/cmd/logger" "github.com/minio/minio/cmd/logger"
bucketsse "github.com/minio/minio/pkg/bucket/encryption" bucketsse "github.com/minio/minio/pkg/bucket/encryption"
"github.com/minio/minio/pkg/bucket/policy" "github.com/minio/minio/pkg/bucket/policy"
@ -48,6 +49,12 @@ func (api objectAPIHandlers) PutBucketEncryptionHandler(w http.ResponseWriter, r
vars := mux.Vars(r) vars := mux.Vars(r)
bucket := vars["bucket"] bucket := vars["bucket"]
// PutBucketEncyrption API requires Content-Md5
if _, ok := r.Header[xhttp.ContentMD5]; !ok {
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrMissingContentMD5), r.URL, guessIsBrowserReq(r))
return
}
if s3Error := checkRequestAuthType(ctx, r, policy.PutBucketEncryptionAction, bucket, ""); s3Error != ErrNone { if s3Error := checkRequestAuthType(ctx, r, policy.PutBucketEncryptionAction, bucket, ""); s3Error != ErrNone {
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL, guessIsBrowserReq(r)) writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL, guessIsBrowserReq(r))
return return
@ -59,12 +66,6 @@ func (api objectAPIHandlers) PutBucketEncryptionHandler(w http.ResponseWriter, r
return return
} }
// PutBucketEncyrption API requires Content-Md5
if _, ok := r.Header["Content-Md5"]; !ok {
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrMissingContentMD5), r.URL, guessIsBrowserReq(r))
return
}
// Parse bucket encryption xml // Parse bucket encryption xml
encConfig, err := validateBucketSSEConfig(io.LimitReader(r.Body, maxBucketSSEConfigSize)) encConfig, err := validateBucketSSEConfig(io.LimitReader(r.Body, maxBucketSSEConfigSize))
if err != nil { if err != nil {

@ -351,7 +351,7 @@ func (api objectAPIHandlers) DeleteMultipleObjectsHandler(w http.ResponseWriter,
// Content-Md5 is requied should be set // Content-Md5 is requied should be set
// http://docs.aws.amazon.com/AmazonS3/latest/API/multiobjectdeleteapi.html // http://docs.aws.amazon.com/AmazonS3/latest/API/multiobjectdeleteapi.html
if _, ok := r.Header["Content-Md5"]; !ok { if _, ok := r.Header[xhttp.ContentMD5]; !ok {
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrMissingContentMD5), r.URL, guessIsBrowserReq(r)) writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrMissingContentMD5), r.URL, guessIsBrowserReq(r))
return return
} }

@ -22,6 +22,7 @@ import (
"net/http" "net/http"
"github.com/gorilla/mux" "github.com/gorilla/mux"
xhttp "github.com/minio/minio/cmd/http"
"github.com/minio/minio/cmd/logger" "github.com/minio/minio/cmd/logger"
"github.com/minio/minio/pkg/bucket/lifecycle" "github.com/minio/minio/pkg/bucket/lifecycle"
"github.com/minio/minio/pkg/bucket/policy" "github.com/minio/minio/pkg/bucket/policy"
@ -48,6 +49,12 @@ func (api objectAPIHandlers) PutBucketLifecycleHandler(w http.ResponseWriter, r
vars := mux.Vars(r) vars := mux.Vars(r)
bucket := vars["bucket"] bucket := vars["bucket"]
// PutBucketLifecycle always needs a Content-Md5
if _, ok := r.Header[xhttp.ContentMD5]; !ok {
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrMissingContentMD5), r.URL, guessIsBrowserReq(r))
return
}
if s3Error := checkRequestAuthType(ctx, r, policy.PutBucketLifecycleAction, bucket, ""); s3Error != ErrNone { if s3Error := checkRequestAuthType(ctx, r, policy.PutBucketLifecycleAction, bucket, ""); s3Error != ErrNone {
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL, guessIsBrowserReq(r)) writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL, guessIsBrowserReq(r))
return return
@ -59,12 +66,6 @@ func (api objectAPIHandlers) PutBucketLifecycleHandler(w http.ResponseWriter, r
return return
} }
// PutBucketLifecycle always needs a Content-Md5
if _, ok := r.Header["Content-Md5"]; !ok {
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrMissingContentMD5), r.URL, guessIsBrowserReq(r))
return
}
bucketLifecycle, err := lifecycle.ParseLifecycleConfig(io.LimitReader(r.Body, r.ContentLength)) bucketLifecycle, err := lifecycle.ParseLifecycleConfig(io.LimitReader(r.Body, r.ContentLength))
if err != nil { if err != nil {
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r)) writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))

@ -436,12 +436,16 @@ func setIgnoreResourcesHandler(h http.Handler) http.Handler {
// Checks requests for not implemented Bucket resources // Checks requests for not implemented Bucket resources
func ignoreNotImplementedBucketResources(req *http.Request) bool { func ignoreNotImplementedBucketResources(req *http.Request) bool {
for name := range req.URL.Query() { for name := range req.URL.Query() {
// Enable GetBucketACL, GetBucketCors, GetBucketWebsite, // Enable PutBucketACL, GetBucketACL, GetBucketCors,
// GetBucketAcccelerate, GetBucketRequestPayment, // GetBucketWebsite, GetBucketAcccelerate,
// GetBucketLogging, GetBucketLifecycle, // GetBucketRequestPayment, GetBucketLogging,
// GetBucketReplication, GetBucketTagging, // GetBucketLifecycle, GetBucketReplication,
// GetBucketVersioning, DeleteBucketTagging, // GetBucketTagging, GetBucketVersioning,
// and DeleteBucketWebsite dummy calls specifically. // DeleteBucketTagging, and DeleteBucketWebsite
// dummy calls specifically.
if name == "acl" && req.Method == http.MethodPut {
return false
}
if ((name == "acl" || if ((name == "acl" ||
name == "cors" || name == "cors" ||
name == "website" || name == "website" ||
@ -457,7 +461,7 @@ func ignoreNotImplementedBucketResources(req *http.Request) bool {
return false return false
} }
if notimplementedBucketResourceNames[name] { if notImplementedBucketResourceNames[name] {
return true return true
} }
} }
@ -467,11 +471,11 @@ func ignoreNotImplementedBucketResources(req *http.Request) bool {
// Checks requests for not implemented Object resources // Checks requests for not implemented Object resources
func ignoreNotImplementedObjectResources(req *http.Request) bool { func ignoreNotImplementedObjectResources(req *http.Request) bool {
for name := range req.URL.Query() { for name := range req.URL.Query() {
// Enable GetObjectACL dummy call specifically. // Enable Get/PutObjectACL dummy call specifically.
if name == "acl" && req.Method == http.MethodGet { if name == "acl" && (req.Method == http.MethodGet || req.Method == http.MethodPut) {
return false return false
} }
if notimplementedObjectResourceNames[name] { if notImplementedObjectResourceNames[name] {
return true return true
} }
} }
@ -479,9 +483,8 @@ func ignoreNotImplementedObjectResources(req *http.Request) bool {
} }
// List of not implemented bucket queries // List of not implemented bucket queries
var notimplementedBucketResourceNames = map[string]bool{ var notImplementedBucketResourceNames = map[string]bool{
"accelerate": true, "accelerate": true,
"acl": true,
"cors": true, "cors": true,
"inventory": true, "inventory": true,
"logging": true, "logging": true,
@ -494,8 +497,7 @@ var notimplementedBucketResourceNames = map[string]bool{
} }
// List of not implemented object queries // List of not implemented object queries
var notimplementedObjectResourceNames = map[string]bool{ var notImplementedObjectResourceNames = map[string]bool{
"acl": true,
"restore": true, "restore": true,
"torrent": true, "torrent": true,
} }

@ -77,6 +77,9 @@ const (
AmzObjectLockLegalHold = "X-Amz-Object-Lock-Legal-Hold" AmzObjectLockLegalHold = "X-Amz-Object-Lock-Legal-Hold"
AmzObjectLockBypassGovernance = "X-Amz-Bypass-Governance-Retention" AmzObjectLockBypassGovernance = "X-Amz-Bypass-Governance-Retention"
// Dummy putBucketACL
AmzACL = "x-amz-acl"
// Signature V4 related contants. // Signature V4 related contants.
AmzContentSha256 = "X-Amz-Content-Sha256" AmzContentSha256 = "X-Amz-Content-Sha256"
AmzDate = "X-Amz-Date" AmzDate = "X-Amz-Date"

@ -1181,6 +1181,7 @@ func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Req
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrInvalidDigest), r.URL, guessIsBrowserReq(r)) writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrInvalidDigest), r.URL, guessIsBrowserReq(r))
return return
} }
/// if Content-Length is unknown/missing, deny the request /// if Content-Length is unknown/missing, deny the request
size := r.ContentLength size := r.ContentLength
rAuthType := getRequestAuthType(r) rAuthType := getRequestAuthType(r)
@ -2610,6 +2611,7 @@ func (api objectAPIHandlers) PutObjectLegalHoldHandler(w http.ResponseWriter, r
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrInvalidDigest), r.URL, guessIsBrowserReq(r)) writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrInvalidDigest), r.URL, guessIsBrowserReq(r))
return return
} }
if _, ok := globalBucketObjectLockConfig.Get(bucket); !ok { if _, ok := globalBucketObjectLockConfig.Get(bucket); !ok {
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrInvalidBucketObjectLockConfiguration), r.URL, guessIsBrowserReq(r)) writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrInvalidBucketObjectLockConfiguration), r.URL, guessIsBrowserReq(r))
return return
@ -2772,6 +2774,7 @@ func (api objectAPIHandlers) PutObjectRetentionHandler(w http.ResponseWriter, r
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrInvalidDigest), r.URL, guessIsBrowserReq(r)) writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrInvalidDigest), r.URL, guessIsBrowserReq(r))
return return
} }
if _, ok := globalBucketObjectLockConfig.Get(bucket); !ok { if _, ok := globalBucketObjectLockConfig.Get(bucket); !ok {
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrInvalidBucketObjectLockConfiguration), r.URL, guessIsBrowserReq(r)) writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrInvalidBucketObjectLockConfiguration), r.URL, guessIsBrowserReq(r))
return return

@ -1803,16 +1803,6 @@ func (s *TestSuiteCommon) TestPutBucketErrors(c *check) {
c.Assert(err, nil) c.Assert(err, nil)
verifyError(c, response, "BucketAlreadyOwnedByYou", "Your previous request to create the named bucket succeeded and you already own it.", verifyError(c, response, "BucketAlreadyOwnedByYou", "Your previous request to create the named bucket succeeded and you already own it.",
http.StatusConflict) http.StatusConflict)
// request for ACL.
// Since MinIO server doesn't support ACL's the request is expected to fail with "NotImplemented" error message.
request, err = newTestSignedRequest("PUT", s.endPoint+SlashSeparator+bucketName+"?acl",
0, nil, s.accessKey, s.secretKey, s.signer)
c.Assert(err, nil)
response, err = client.Do(request)
c.Assert(err, nil)
verifyError(c, response, "NotImplemented", "A header you provided implies functionality that is not implemented", http.StatusNotImplemented)
} }
func (s *TestSuiteCommon) TestGetObjectLarge10MiB(c *check) { func (s *TestSuiteCommon) TestGetObjectLarge10MiB(c *check) {

@ -117,12 +117,12 @@ func xmlDecoder(body io.Reader, v interface{}, size int64) error {
// checkValidMD5 - verify if valid md5, returns md5 in bytes. // checkValidMD5 - verify if valid md5, returns md5 in bytes.
func checkValidMD5(h http.Header) ([]byte, error) { func checkValidMD5(h http.Header) ([]byte, error) {
md5B64, ok := h["Content-Md5"] md5B64, ok := h[xhttp.ContentMD5]
if ok { if ok {
if md5B64[0] == "" { if md5B64[0] == "" {
return nil, fmt.Errorf("Content-Md5 header set to empty value") return nil, fmt.Errorf("Content-Md5 header set to empty value")
} }
return base64.StdEncoding.DecodeString(md5B64[0]) return base64.StdEncoding.Strict().DecodeString(md5B64[0])
} }
return []byte{}, nil return []byte{}, nil
} }

Loading…
Cancel
Save