storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/cmd/bucket-handlers.go (about)

     1  /*
     2   * MinIO Cloud Storage, (C) 2015-2020 MinIO, Inc.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package cmd
    18  
    19  import (
    20  	"bytes"
    21  	"crypto/subtle"
    22  	"encoding/base64"
    23  	"encoding/json"
    24  	"encoding/xml"
    25  	"fmt"
    26  	"io"
    27  	"net/http"
    28  	"net/textproto"
    29  	"net/url"
    30  	"path"
    31  	"strconv"
    32  	"strings"
    33  
    34  	"github.com/google/uuid"
    35  	"github.com/gorilla/mux"
    36  	"github.com/minio/minio-go/v7/pkg/tags"
    37  
    38  	"storj.io/minio/cmd/crypto"
    39  	xhttp "storj.io/minio/cmd/http"
    40  	"storj.io/minio/cmd/logger"
    41  	"storj.io/minio/pkg/bucket/lifecycle"
    42  	objectlock "storj.io/minio/pkg/bucket/object/lock"
    43  	"storj.io/minio/pkg/bucket/policy"
    44  	"storj.io/minio/pkg/bucket/replication"
    45  	"storj.io/minio/pkg/event"
    46  	"storj.io/minio/pkg/handlers"
    47  	"storj.io/minio/pkg/hash"
    48  	iampolicy "storj.io/minio/pkg/iam/policy"
    49  )
    50  
    51  const (
    52  	objectLockConfig        = "object-lock.xml"
    53  	bucketTaggingConfig     = "tagging.xml"
    54  	bucketReplicationConfig = "replication.xml"
    55  )
    56  
    57  // GetBucketLocationHandler - GET Bucket location.
    58  // -------------------------
    59  // This operation returns bucket location.
    60  func (api ObjectAPIHandlers) GetBucketLocationHandler(w http.ResponseWriter, r *http.Request) {
    61  	ctx := NewContext(r, w, "GetBucketLocation")
    62  
    63  	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
    64  
    65  	vars := mux.Vars(r)
    66  	bucket := vars["bucket"]
    67  
    68  	objectAPI := api.ObjectAPI()
    69  	if objectAPI == nil {
    70  		WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL, guessIsBrowserReq(r))
    71  		return
    72  	}
    73  
    74  	if s3Error := checkRequestAuthType(ctx, r, policy.GetBucketLocationAction, bucket, ""); s3Error != ErrNone {
    75  		WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL, guessIsBrowserReq(r))
    76  		return
    77  	}
    78  
    79  	getBucketInfo := objectAPI.GetBucketInfo
    80  
    81  	if _, err := getBucketInfo(ctx, bucket); err != nil {
    82  		WriteErrorResponse(ctx, w, ToAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
    83  		return
    84  	}
    85  
    86  	// Generate response.
    87  	encodedSuccessResponse := EncodeResponse(LocationResponse{})
    88  	// Get current region.
    89  	region := globalServerRegion
    90  	if region != globalMinioDefaultRegion {
    91  		encodedSuccessResponse = EncodeResponse(LocationResponse{
    92  			Location: region,
    93  		})
    94  	}
    95  
    96  	// Write success response.
    97  	WriteSuccessResponseXML(w, encodedSuccessResponse)
    98  }
    99  
   100  // ListMultipartUploadsHandler - GET Bucket (List Multipart uploads)
   101  // -------------------------
   102  // This operation lists in-progress multipart uploads. An in-progress
   103  // multipart upload is a multipart upload that has been initiated,
   104  // using the Initiate Multipart Upload request, but has not yet been
   105  // completed or aborted. This operation returns at most 1,000 multipart
   106  // uploads in the response.
   107  //
   108  func (api ObjectAPIHandlers) ListMultipartUploadsHandler(w http.ResponseWriter, r *http.Request) {
   109  	ctx := NewContext(r, w, "ListMultipartUploads")
   110  
   111  	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
   112  
   113  	vars := mux.Vars(r)
   114  	bucket := vars["bucket"]
   115  
   116  	objectAPI := api.ObjectAPI()
   117  	if objectAPI == nil {
   118  		WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL, guessIsBrowserReq(r))
   119  		return
   120  	}
   121  
   122  	if s3Error := checkRequestAuthType(ctx, r, policy.ListBucketMultipartUploadsAction, bucket, ""); s3Error != ErrNone {
   123  		WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL, guessIsBrowserReq(r))
   124  		return
   125  	}
   126  
   127  	prefix, keyMarker, uploadIDMarker, delimiter, maxUploads, encodingType, errCode := getBucketMultipartResources(r.URL.Query())
   128  	if errCode != ErrNone {
   129  		WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(errCode), r.URL, guessIsBrowserReq(r))
   130  		return
   131  	}
   132  
   133  	if maxUploads < 0 {
   134  		WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrInvalidMaxUploads), r.URL, guessIsBrowserReq(r))
   135  		return
   136  	}
   137  
   138  	if keyMarker != "" {
   139  		// Marker not common with prefix is not implemented.
   140  		if !HasPrefix(keyMarker, prefix) {
   141  			WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL, guessIsBrowserReq(r))
   142  			return
   143  		}
   144  	}
   145  
   146  	listMultipartsInfo, err := objectAPI.ListMultipartUploads(ctx, bucket, prefix, keyMarker, uploadIDMarker, delimiter, maxUploads)
   147  	if err != nil {
   148  		WriteErrorResponse(ctx, w, ToAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
   149  		return
   150  	}
   151  	// generate response
   152  	response := generateListMultipartUploadsResponse(bucket, listMultipartsInfo, encodingType)
   153  	encodedSuccessResponse := EncodeResponse(response)
   154  
   155  	// write success response.
   156  	WriteSuccessResponseXML(w, encodedSuccessResponse)
   157  }
   158  
   159  // ListBucketsHandler - GET Service.
   160  // -----------
   161  // This implementation of the GET operation returns a list of all buckets
   162  // owned by the authenticated sender of the request.
   163  func (api ObjectAPIHandlers) ListBucketsHandler(w http.ResponseWriter, r *http.Request) {
   164  	ctx := NewContext(r, w, "ListBuckets")
   165  
   166  	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
   167  
   168  	objectAPI := api.ObjectAPI()
   169  	if objectAPI == nil {
   170  		WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL, guessIsBrowserReq(r))
   171  		return
   172  	}
   173  
   174  	listBuckets := objectAPI.ListBuckets
   175  
   176  	cred, owner, s3Error := CheckRequestAuthTypeCredential(ctx, r, policy.ListAllMyBucketsAction, "", "")
   177  	if s3Error != ErrNone && s3Error != ErrAccessDenied {
   178  		WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL, guessIsBrowserReq(r))
   179  		return
   180  	}
   181  
   182  	// Invoke the list buckets.
   183  	bucketsInfo, err := listBuckets(ctx)
   184  	if err != nil {
   185  		WriteErrorResponse(ctx, w, ToAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
   186  		return
   187  	}
   188  
   189  	if s3Error == ErrAccessDenied {
   190  		// Set prefix value for "s3:prefix" policy conditionals.
   191  		r.Header.Set("prefix", "")
   192  
   193  		// Set delimiter value for "s3:delimiter" policy conditionals.
   194  		r.Header.Set("delimiter", SlashSeparator)
   195  
   196  		// err will be nil here as we already called this function
   197  		// earlier in this request.
   198  		claims, _ := getClaimsFromToken(getSessionToken(r))
   199  		n := 0
   200  		// Use the following trick to filter in place
   201  		// https://github.com/golang/go/wiki/SliceTricks#filter-in-place
   202  		for _, bucketInfo := range bucketsInfo {
   203  			if GlobalIAMSys.IsAllowed(iampolicy.Args{
   204  				AccountName:     cred.AccessKey,
   205  				Groups:          cred.Groups,
   206  				Action:          iampolicy.ListBucketAction,
   207  				BucketName:      bucketInfo.Name,
   208  				ConditionValues: getConditionValues(r, "", cred.AccessKey, claims),
   209  				IsOwner:         owner,
   210  				ObjectName:      "",
   211  				Claims:          claims,
   212  			}) {
   213  				bucketsInfo[n] = bucketInfo
   214  				n++
   215  			}
   216  		}
   217  		bucketsInfo = bucketsInfo[:n]
   218  		// No buckets can be filtered return access denied error.
   219  		if len(bucketsInfo) == 0 {
   220  			WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL, guessIsBrowserReq(r))
   221  			return
   222  		}
   223  	}
   224  
   225  	// Generate response.
   226  	response := generateListBucketsResponse(bucketsInfo)
   227  	encodedSuccessResponse := EncodeResponse(response)
   228  
   229  	// Write response.
   230  	WriteSuccessResponseXML(w, encodedSuccessResponse)
   231  }
   232  
   233  // DeleteMultipleObjectsHandler - deletes multiple objects.
   234  func (api ObjectAPIHandlers) DeleteMultipleObjectsHandler(w http.ResponseWriter, r *http.Request) {
   235  	ctx := NewContext(r, w, "DeleteMultipleObjects")
   236  
   237  	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
   238  
   239  	vars := mux.Vars(r)
   240  	bucket := vars["bucket"]
   241  
   242  	objectAPI := api.ObjectAPI()
   243  	if objectAPI == nil {
   244  		WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL, guessIsBrowserReq(r))
   245  		return
   246  	}
   247  
   248  	// Content-Md5 is requied should be set
   249  	// http://docs.aws.amazon.com/AmazonS3/latest/API/multiobjectdeleteapi.html
   250  	if _, ok := r.Header[xhttp.ContentMD5]; !ok {
   251  		WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrMissingContentMD5), r.URL, guessIsBrowserReq(r))
   252  		return
   253  	}
   254  
   255  	// Content-Length is required and should be non-zero
   256  	// http://docs.aws.amazon.com/AmazonS3/latest/API/multiobjectdeleteapi.html
   257  	if r.ContentLength <= 0 {
   258  		WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrMissingContentLength), r.URL, guessIsBrowserReq(r))
   259  		return
   260  	}
   261  
   262  	// The max. XML contains 100000 object names (each at most 1024 bytes long) + XML overhead
   263  	const maxBodySize = 2 * 100000 * 1024
   264  
   265  	// Unmarshal list of keys to be deleted.
   266  	deleteObjects := &DeleteObjectsRequest{}
   267  	if err := xmlDecoder(r.Body, deleteObjects, maxBodySize); err != nil {
   268  		logger.LogIf(ctx, err, logger.Application)
   269  		WriteErrorResponse(ctx, w, ToAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
   270  		return
   271  	}
   272  
   273  	// Convert object name delete objects if it has `/` in the beginning.
   274  	for i := range deleteObjects.Objects {
   275  		deleteObjects.Objects[i].ObjectName = trimLeadingSlash(deleteObjects.Objects[i].ObjectName)
   276  	}
   277  
   278  	// Call checkRequestAuthType to populate ReqInfo.AccessKey before GetBucketInfo()
   279  	// Ignore errors here to preserve the S3 error behavior of GetBucketInfo()
   280  	checkRequestAuthType(ctx, r, policy.DeleteObjectAction, bucket, "")
   281  
   282  	// Before proceeding validate if bucket exists.
   283  	_, err := objectAPI.GetBucketInfo(ctx, bucket)
   284  	if err != nil {
   285  		WriteErrorResponse(ctx, w, ToAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
   286  		return
   287  	}
   288  
   289  	deleteObjectsFn := objectAPI.DeleteObjects
   290  	if api.CacheAPI() != nil {
   291  		deleteObjectsFn = api.CacheAPI().DeleteObjects
   292  	}
   293  
   294  	// Return Malformed XML as S3 spec if the list of objects is empty
   295  	if len(deleteObjects.Objects) == 0 {
   296  		WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrMalformedXML), r.URL, guessIsBrowserReq(r))
   297  		return
   298  	}
   299  
   300  	var objectsToDelete = map[ObjectToDelete]int{}
   301  	getObjectInfoFn := objectAPI.GetObjectInfo
   302  	if api.CacheAPI() != nil {
   303  		getObjectInfoFn = api.CacheAPI().GetObjectInfo
   304  	}
   305  	var (
   306  		hasLockEnabled, hasLifecycleConfig, replicateSync bool
   307  		goi                                               ObjectInfo
   308  		gerr                                              error
   309  	)
   310  	replicateDeletes := hasReplicationRules(ctx, bucket, deleteObjects.Objects)
   311  	if rcfg, _ := globalBucketObjectLockSys.Get(bucket); rcfg.LockEnabled {
   312  		hasLockEnabled = true
   313  	}
   314  	if _, err := globalBucketMetadataSys.GetLifecycleConfig(bucket); err == nil {
   315  		hasLifecycleConfig = true
   316  	}
   317  	dErrs := make([]DeleteError, len(deleteObjects.Objects))
   318  	for index, object := range deleteObjects.Objects {
   319  		if apiErrCode := checkRequestAuthType(ctx, r, policy.DeleteObjectAction, bucket, object.ObjectName); apiErrCode != ErrNone {
   320  			if apiErrCode == ErrSignatureDoesNotMatch || apiErrCode == ErrInvalidAccessKeyID {
   321  				WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(apiErrCode), r.URL, guessIsBrowserReq(r))
   322  				return
   323  			}
   324  			apiErr := errorCodes.ToAPIErr(apiErrCode)
   325  			dErrs[index] = DeleteError{
   326  				Code:      apiErr.Code,
   327  				Message:   apiErr.Description,
   328  				Key:       object.ObjectName,
   329  				VersionID: object.VersionID,
   330  			}
   331  			continue
   332  		}
   333  		if object.VersionID != "" && object.VersionID != nullVersionID {
   334  			if _, err := uuid.Parse(object.VersionID); err != nil {
   335  				logger.LogIf(ctx, fmt.Errorf("invalid version-id specified %w", err))
   336  				apiErr := errorCodes.ToAPIErr(ErrNoSuchVersion)
   337  				dErrs[index] = DeleteError{
   338  					Code:      apiErr.Code,
   339  					Message:   apiErr.Description,
   340  					Key:       object.ObjectName,
   341  					VersionID: object.VersionID,
   342  				}
   343  				continue
   344  			}
   345  		}
   346  
   347  		if replicateDeletes || hasLockEnabled || hasLifecycleConfig {
   348  			goi, gerr = getObjectInfoFn(ctx, bucket, object.ObjectName, ObjectOptions{
   349  				VersionID: object.VersionID,
   350  			})
   351  		}
   352  		if hasLifecycleConfig && gerr == nil {
   353  			object.PurgeTransitioned = goi.TransitionStatus
   354  		}
   355  		if replicateDeletes {
   356  			replicate, repsync := checkReplicateDelete(ctx, bucket, ObjectToDelete{
   357  				ObjectName: object.ObjectName,
   358  				VersionID:  object.VersionID,
   359  			}, goi, gerr)
   360  			replicateSync = repsync
   361  			if replicate {
   362  				if apiErrCode := checkRequestAuthType(ctx, r, policy.ReplicateDeleteAction, bucket, object.ObjectName); apiErrCode != ErrNone {
   363  					if apiErrCode == ErrSignatureDoesNotMatch || apiErrCode == ErrInvalidAccessKeyID {
   364  						WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(apiErrCode), r.URL, guessIsBrowserReq(r))
   365  						return
   366  					}
   367  					continue
   368  				}
   369  				if object.VersionID != "" {
   370  					object.VersionPurgeStatus = Pending
   371  				} else {
   372  					object.DeleteMarkerReplicationStatus = string(replication.Pending)
   373  				}
   374  			}
   375  		}
   376  		if object.VersionID != "" {
   377  			if hasLockEnabled {
   378  				if apiErrCode := enforceRetentionBypassForDelete(ctx, r, bucket, object, goi, gerr); apiErrCode != ErrNone {
   379  					apiErr := errorCodes.ToAPIErr(apiErrCode)
   380  					dErrs[index] = DeleteError{
   381  						Code:      apiErr.Code,
   382  						Message:   apiErr.Description,
   383  						Key:       object.ObjectName,
   384  						VersionID: object.VersionID,
   385  					}
   386  					continue
   387  				}
   388  			}
   389  		}
   390  
   391  		// Avoid duplicate objects, we use map to filter them out.
   392  		if _, ok := objectsToDelete[object]; !ok {
   393  			objectsToDelete[object] = index
   394  		}
   395  	}
   396  
   397  	toNames := func(input map[ObjectToDelete]int) (output []ObjectToDelete) {
   398  		output = make([]ObjectToDelete, len(input))
   399  		idx := 0
   400  		for obj := range input {
   401  			output[idx] = obj
   402  			idx++
   403  		}
   404  		return
   405  	}
   406  
   407  	deleteList := toNames(objectsToDelete)
   408  	dObjects, errs := deleteObjectsFn(ctx, bucket, deleteList, ObjectOptions{
   409  		Versioned:        globalBucketVersioningSys.Enabled(bucket),
   410  		VersionSuspended: globalBucketVersioningSys.Suspended(bucket),
   411  	})
   412  	deletedObjects := make([]DeletedObject, len(deleteObjects.Objects))
   413  	for i := range errs {
   414  		// DeleteMarkerVersionID is not used specifically to avoid
   415  		// lookup errors, since DeleteMarkerVersionID is only
   416  		// created during DeleteMarker creation when client didn't
   417  		// specify a versionID.
   418  		objToDel := ObjectToDelete{
   419  			ObjectName:                    dObjects[i].ObjectName,
   420  			VersionID:                     dObjects[i].VersionID,
   421  			VersionPurgeStatus:            dObjects[i].VersionPurgeStatus,
   422  			DeleteMarkerReplicationStatus: dObjects[i].DeleteMarkerReplicationStatus,
   423  			PurgeTransitioned:             dObjects[i].PurgeTransitioned,
   424  		}
   425  		dindex := objectsToDelete[objToDel]
   426  		if errs[i] == nil || isErrObjectNotFound(errs[i]) || isErrVersionNotFound(errs[i]) {
   427  			if replicateDeletes {
   428  				dObjects[i].DeleteMarkerReplicationStatus = deleteList[i].DeleteMarkerReplicationStatus
   429  				dObjects[i].VersionPurgeStatus = deleteList[i].VersionPurgeStatus
   430  			}
   431  			deletedObjects[dindex] = dObjects[i]
   432  			continue
   433  		}
   434  		apiErr := ToAPIError(ctx, errs[i])
   435  		dErrs[dindex] = DeleteError{
   436  			Code:      apiErr.Code,
   437  			Message:   apiErr.Description,
   438  			Key:       deleteList[i].ObjectName,
   439  			VersionID: deleteList[i].VersionID,
   440  		}
   441  	}
   442  
   443  	var deleteErrors []DeleteError
   444  	for _, dErr := range dErrs {
   445  		if dErr.Code != "" {
   446  			deleteErrors = append(deleteErrors, dErr)
   447  		}
   448  	}
   449  
   450  	// Generate response
   451  	response := generateMultiDeleteResponse(deleteObjects.Quiet, deletedObjects, deleteErrors)
   452  	encodedSuccessResponse := EncodeResponse(response)
   453  
   454  	// Write success response.
   455  	WriteSuccessResponseXML(w, encodedSuccessResponse)
   456  	for _, dobj := range deletedObjects {
   457  		if dobj.ObjectName == "" {
   458  			continue
   459  		}
   460  
   461  		if replicateDeletes {
   462  			if dobj.DeleteMarkerReplicationStatus == string(replication.Pending) || dobj.VersionPurgeStatus == Pending {
   463  				dv := DeletedObjectVersionInfo{
   464  					DeletedObject: dobj,
   465  					Bucket:        bucket,
   466  				}
   467  				scheduleReplicationDelete(ctx, dv, objectAPI, replicateSync)
   468  			}
   469  		}
   470  
   471  		if hasLifecycleConfig && dobj.PurgeTransitioned == lifecycle.TransitionComplete { // clean up transitioned tier
   472  			deleteTransitionedObject(ctx, objectAPI, bucket, dobj.ObjectName, lifecycle.ObjectOpts{
   473  				Name:         dobj.ObjectName,
   474  				VersionID:    dobj.VersionID,
   475  				DeleteMarker: dobj.DeleteMarker,
   476  			}, false, true)
   477  		}
   478  
   479  		eventName := event.ObjectRemovedDelete
   480  		objInfo := ObjectInfo{
   481  			Name:         dobj.ObjectName,
   482  			VersionID:    dobj.VersionID,
   483  			DeleteMarker: dobj.DeleteMarker,
   484  		}
   485  
   486  		if objInfo.DeleteMarker {
   487  			objInfo.VersionID = dobj.DeleteMarkerVersionID
   488  			eventName = event.ObjectRemovedDeleteMarkerCreated
   489  		}
   490  
   491  		sendEvent(eventArgs{
   492  			EventName:    eventName,
   493  			BucketName:   bucket,
   494  			Object:       objInfo,
   495  			ReqParams:    extractReqParams(r),
   496  			RespElements: extractRespElements(w),
   497  			UserAgent:    r.UserAgent(),
   498  			Host:         handlers.GetSourceIP(r),
   499  		})
   500  	}
   501  }
   502  
   503  // PutBucketHandler - PUT Bucket
   504  // ----------
   505  // This implementation of the PUT operation creates a new bucket for authenticated request
   506  func (api ObjectAPIHandlers) PutBucketHandler(w http.ResponseWriter, r *http.Request) {
   507  	ctx := NewContext(r, w, "PutBucket")
   508  
   509  	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
   510  
   511  	objectAPI := api.ObjectAPI()
   512  	if objectAPI == nil {
   513  		WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL, guessIsBrowserReq(r))
   514  		return
   515  	}
   516  
   517  	vars := mux.Vars(r)
   518  	bucket := vars["bucket"]
   519  
   520  	objectLockEnabled := false
   521  	if vs, found := r.Header[http.CanonicalHeaderKey("x-amz-bucket-object-lock-enabled")]; found {
   522  		v := strings.ToLower(strings.Join(vs, ""))
   523  		if v != "true" && v != "false" {
   524  			WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrInvalidRequest), r.URL, guessIsBrowserReq(r))
   525  			return
   526  		}
   527  		objectLockEnabled = v == "true"
   528  	}
   529  
   530  	if s3Error := checkRequestAuthType(ctx, r, policy.CreateBucketAction, bucket, ""); s3Error != ErrNone {
   531  		WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL, guessIsBrowserReq(r))
   532  		return
   533  	}
   534  
   535  	// Parse incoming location constraint.
   536  	location, s3Error := parseLocationConstraint(r)
   537  	if s3Error != ErrNone {
   538  		WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL, guessIsBrowserReq(r))
   539  		return
   540  	}
   541  
   542  	// Validate if location sent by the client is valid, reject
   543  	// requests which do not follow valid region requirements.
   544  	if !isValidLocation(location) {
   545  		WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrInvalidRegion), r.URL, guessIsBrowserReq(r))
   546  		return
   547  	}
   548  
   549  	opts := BucketOptions{
   550  		Location:    location,
   551  		LockEnabled: objectLockEnabled,
   552  	}
   553  
   554  	// Proceed to creating a bucket.
   555  	err := objectAPI.MakeBucketWithLocation(ctx, bucket, opts)
   556  	if err != nil {
   557  		WriteErrorResponse(ctx, w, ToAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
   558  		return
   559  	}
   560  
   561  	// Load updated bucket metadata into memory.
   562  	GlobalNotificationSys.LoadBucketMetadata(GlobalContext, bucket)
   563  
   564  	// Make sure to add Location information here only for bucket
   565  	if cp := pathClean(r.URL.Path); cp != "" {
   566  		w.Header().Set(xhttp.Location, cp) // Clean any trailing slashes.
   567  	}
   568  
   569  	writeSuccessResponseHeadersOnly(w)
   570  
   571  	sendEvent(eventArgs{
   572  		EventName:    event.BucketCreated,
   573  		BucketName:   bucket,
   574  		ReqParams:    extractReqParams(r),
   575  		RespElements: extractRespElements(w),
   576  		UserAgent:    r.UserAgent(),
   577  		Host:         handlers.GetSourceIP(r),
   578  	})
   579  }
   580  
   581  // PostPolicyBucketHandler - POST policy
   582  // ----------
   583  // This implementation of the POST operation handles object creation with a specified
   584  // signature policy in multipart/form-data
   585  func (api ObjectAPIHandlers) PostPolicyBucketHandler(w http.ResponseWriter, r *http.Request) {
   586  	ctx := NewContext(r, w, "PostPolicyBucket")
   587  
   588  	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
   589  
   590  	objectAPI := api.ObjectAPI()
   591  	if objectAPI == nil {
   592  		WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL, guessIsBrowserReq(r))
   593  		return
   594  	}
   595  
   596  	if crypto.S3KMS.IsRequested(r.Header) { // SSE-KMS is not supported
   597  		WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL, guessIsBrowserReq(r))
   598  		return
   599  	}
   600  
   601  	if _, ok := crypto.IsRequested(r.Header); !objectAPI.IsEncryptionSupported() && ok {
   602  		WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL, guessIsBrowserReq(r))
   603  		return
   604  	}
   605  
   606  	bucket := mux.Vars(r)["bucket"]
   607  
   608  	// Require Content-Length to be set in the request
   609  	size := r.ContentLength
   610  	if size < 0 {
   611  		WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrMissingContentLength), r.URL, guessIsBrowserReq(r))
   612  		return
   613  	}
   614  
   615  	resource, err := getResource(r.URL.Path, r.Host, globalDomainNames)
   616  	if err != nil {
   617  		WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrInvalidRequest), r.URL, guessIsBrowserReq(r))
   618  		return
   619  	}
   620  
   621  	// Make sure that the URL does not contain object name.
   622  	if bucket != path.Clean(resource[1:]) {
   623  		WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrMethodNotAllowed), r.URL, guessIsBrowserReq(r))
   624  		return
   625  	}
   626  
   627  	// Here the parameter is the size of the form data that should
   628  	// be loaded in memory, the remaining being put in temporary files.
   629  	reader, err := r.MultipartReader()
   630  	if err != nil {
   631  		logger.LogIf(ctx, err)
   632  		WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrMalformedPOSTRequest), r.URL, guessIsBrowserReq(r))
   633  		return
   634  	}
   635  
   636  	// Read multipart data and save in memory and in the disk if needed
   637  	form, err := reader.ReadForm(maxFormMemory)
   638  	if err != nil {
   639  		logger.LogIf(ctx, err, logger.Application)
   640  		WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrMalformedPOSTRequest), r.URL, guessIsBrowserReq(r))
   641  		return
   642  	}
   643  
   644  	// Remove all tmp files created during multipart upload
   645  	defer form.RemoveAll()
   646  
   647  	// Extract all form fields
   648  	fileBody, fileName, fileSize, formValues, err := extractPostPolicyFormValues(ctx, form)
   649  	if err != nil {
   650  		logger.LogIf(ctx, err, logger.Application)
   651  		WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrMalformedPOSTRequest), r.URL, guessIsBrowserReq(r))
   652  		return
   653  	}
   654  
   655  	// Check if file is provided, error out otherwise.
   656  	if fileBody == nil {
   657  		WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrPOSTFileRequired), r.URL, guessIsBrowserReq(r))
   658  		return
   659  	}
   660  
   661  	// Close multipart file
   662  	defer fileBody.Close()
   663  
   664  	formValues.Set("Bucket", bucket)
   665  	if fileName != "" && strings.Contains(formValues.Get("Key"), "${filename}") {
   666  		// S3 feature to replace ${filename} found in Key form field
   667  		// by the filename attribute passed in multipart
   668  		formValues.Set("Key", strings.Replace(formValues.Get("Key"), "${filename}", fileName, -1))
   669  	}
   670  	object := trimLeadingSlash(formValues.Get("Key"))
   671  
   672  	successRedirect := formValues.Get("success_action_redirect")
   673  	successStatus := formValues.Get("success_action_status")
   674  	var redirectURL *url.URL
   675  	if successRedirect != "" {
   676  		redirectURL, err = url.Parse(successRedirect)
   677  		if err != nil {
   678  			WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrMalformedPOSTRequest), r.URL, guessIsBrowserReq(r))
   679  			return
   680  		}
   681  	}
   682  
   683  	// Verify policy signature.
   684  	cred, errCode := doesPolicySignatureMatch(ctx, formValues)
   685  	if errCode != ErrNone {
   686  		WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(errCode), r.URL, guessIsBrowserReq(r))
   687  		return
   688  	}
   689  
   690  	// Once signature is validated, check if the user has
   691  	// explicit permissions for the user.
   692  	{
   693  		token := formValues.Get(xhttp.AmzSecurityToken)
   694  		if token != "" && cred.AccessKey == "" {
   695  			WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrNoAccessKey), r.URL, guessIsBrowserReq(r))
   696  			return
   697  		}
   698  
   699  		if cred.IsServiceAccount() && token == "" {
   700  			token = cred.SessionToken
   701  		}
   702  
   703  		if subtle.ConstantTimeCompare([]byte(token), []byte(cred.SessionToken)) != 1 {
   704  			WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrInvalidToken), r.URL, guessIsBrowserReq(r))
   705  			return
   706  		}
   707  
   708  		// Extract claims if any.
   709  		claims, err := getClaimsFromToken(token)
   710  		if err != nil {
   711  			WriteErrorResponse(ctx, w, ToAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
   712  			return
   713  		}
   714  
   715  		if !GlobalIAMSys.IsAllowed(iampolicy.Args{
   716  			AccountName:     cred.AccessKey,
   717  			Action:          iampolicy.PutObjectAction,
   718  			ConditionValues: getConditionValues(r, "", cred.AccessKey, claims),
   719  			BucketName:      bucket,
   720  			ObjectName:      object,
   721  			IsOwner:         globalActiveCred.AccessKey == cred.AccessKey,
   722  			Claims:          claims,
   723  		}) {
   724  			WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrAccessDenied), r.URL, guessIsBrowserReq(r))
   725  			return
   726  		}
   727  	}
   728  
   729  	policyBytes, err := base64.StdEncoding.DecodeString(formValues.Get("Policy"))
   730  	if err != nil {
   731  		WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrMalformedPOSTRequest), r.URL, guessIsBrowserReq(r))
   732  		return
   733  	}
   734  
   735  	// Handle policy if it is set.
   736  	if len(policyBytes) > 0 {
   737  		postPolicyForm, err := parsePostPolicyForm(bytes.NewReader(policyBytes))
   738  		if err != nil {
   739  			errAPI := errorCodes.ToAPIErr(ErrPostPolicyConditionInvalidFormat)
   740  			errAPI.Description = fmt.Sprintf("%s '(%s)'", errAPI.Description, err)
   741  			WriteErrorResponse(ctx, w, errAPI, r.URL, guessIsBrowserReq(r))
   742  			return
   743  		}
   744  
   745  		// Make sure formValues adhere to policy restrictions.
   746  		if err = checkPostPolicy(formValues, postPolicyForm); err != nil {
   747  			WriteErrorResponse(ctx, w, errorCodes.ToAPIErrWithErr(ErrAccessDenied, err), r.URL, guessIsBrowserReq(r))
   748  			return
   749  		}
   750  
   751  		// Ensure that the object size is within expected range, also the file size
   752  		// should not exceed the maximum single Put size (5 GiB)
   753  		lengthRange := postPolicyForm.Conditions.ContentLengthRange
   754  		if lengthRange.Valid {
   755  			if fileSize < lengthRange.Min {
   756  				WriteErrorResponse(ctx, w, ToAPIError(ctx, errDataTooSmall), r.URL, guessIsBrowserReq(r))
   757  				return
   758  			}
   759  
   760  			if fileSize > lengthRange.Max || isMaxObjectSize(fileSize) {
   761  				WriteErrorResponse(ctx, w, ToAPIError(ctx, errDataTooLarge), r.URL, guessIsBrowserReq(r))
   762  				return
   763  			}
   764  		}
   765  	}
   766  
   767  	// Extract metadata to be saved from received Form.
   768  	metadata := make(map[string]string)
   769  	err = extractMetadataFromMime(ctx, textproto.MIMEHeader(formValues), metadata)
   770  	if err != nil {
   771  		WriteErrorResponse(ctx, w, ToAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
   772  		return
   773  	}
   774  
   775  	hashReader, err := hash.NewReader(fileBody, fileSize, "", "", fileSize)
   776  	if err != nil {
   777  		logger.LogIf(ctx, err)
   778  		WriteErrorResponse(ctx, w, ToAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
   779  		return
   780  	}
   781  	rawReader := hashReader
   782  	pReader := NewPutObjReader(rawReader)
   783  	var objectEncryptionKey crypto.ObjectKey
   784  
   785  	// Check if bucket encryption is enabled
   786  	if _, err = globalBucketSSEConfigSys.Get(bucket); err == nil || globalAutoEncryption {
   787  		// This request header needs to be set prior to setting ObjectOptions
   788  		if !crypto.SSEC.IsRequested(r.Header) {
   789  			r.Header.Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES)
   790  		}
   791  	}
   792  
   793  	// get gateway encryption options
   794  	var opts ObjectOptions
   795  	opts, err = putOpts(ctx, r, bucket, object, metadata)
   796  	if err != nil {
   797  		writeErrorResponseHeadersOnly(w, ToAPIError(ctx, err))
   798  		return
   799  	}
   800  	if objectAPI.IsEncryptionSupported() {
   801  		if _, ok := crypto.IsRequested(formValues); ok && !HasSuffix(object, SlashSeparator) { // handle SSE requests
   802  			if crypto.SSECopy.IsRequested(r.Header) {
   803  				WriteErrorResponse(ctx, w, ToAPIError(ctx, errInvalidEncryptionParameters), r.URL, guessIsBrowserReq(r))
   804  				return
   805  			}
   806  			var reader io.Reader
   807  			var key []byte
   808  			if crypto.SSEC.IsRequested(formValues) {
   809  				key, err = ParseSSECustomerHeader(formValues)
   810  				if err != nil {
   811  					WriteErrorResponse(ctx, w, ToAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
   812  					return
   813  				}
   814  			}
   815  			reader, objectEncryptionKey, err = newEncryptReader(hashReader, key, bucket, object, metadata, crypto.S3.IsRequested(formValues))
   816  			if err != nil {
   817  				WriteErrorResponse(ctx, w, ToAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
   818  				return
   819  			}
   820  			info := ObjectInfo{Size: fileSize}
   821  			// do not try to verify encrypted content
   822  			hashReader, err = hash.NewReader(reader, info.EncryptedSize(), "", "", fileSize)
   823  			if err != nil {
   824  				WriteErrorResponse(ctx, w, ToAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
   825  				return
   826  			}
   827  			pReader, err = pReader.WithEncryption(hashReader, &objectEncryptionKey)
   828  			if err != nil {
   829  				WriteErrorResponse(ctx, w, ToAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
   830  				return
   831  			}
   832  		}
   833  	}
   834  
   835  	objInfo, err := objectAPI.PutObject(ctx, bucket, object, pReader, opts)
   836  	if err != nil {
   837  		WriteErrorResponse(ctx, w, ToAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
   838  		return
   839  	}
   840  
   841  	// We must not use the http.Header().Set method here because some (broken)
   842  	// clients expect the ETag header key to be literally "ETag" - not "Etag" (case-sensitive).
   843  	// Therefore, we have to set the ETag directly as map entry.
   844  	w.Header()[xhttp.ETag] = []string{`"` + objInfo.ETag + `"`}
   845  
   846  	// Set the relevant version ID as part of the response header.
   847  	if objInfo.VersionID != "" {
   848  		w.Header()[xhttp.AmzVersionID] = []string{objInfo.VersionID}
   849  	}
   850  
   851  	w.Header().Set(xhttp.Location, getObjectLocation(r, globalDomainNames, bucket, object))
   852  
   853  	// Notify object created event.
   854  	defer sendEvent(eventArgs{
   855  		EventName:    event.ObjectCreatedPost,
   856  		BucketName:   objInfo.Bucket,
   857  		Object:       objInfo,
   858  		ReqParams:    extractReqParams(r),
   859  		RespElements: extractRespElements(w),
   860  		UserAgent:    r.UserAgent(),
   861  		Host:         handlers.GetSourceIP(r),
   862  	})
   863  
   864  	if successRedirect != "" {
   865  		// Replace raw query params..
   866  		redirectURL.RawQuery = getRedirectPostRawQuery(objInfo)
   867  		writeRedirectSeeOther(w, redirectURL.String())
   868  		return
   869  	}
   870  
   871  	// Decide what http response to send depending on success_action_status parameter
   872  	switch successStatus {
   873  	case "201":
   874  		resp := EncodeResponse(PostResponse{
   875  			Bucket:   objInfo.Bucket,
   876  			Key:      objInfo.Name,
   877  			ETag:     `"` + objInfo.ETag + `"`,
   878  			Location: w.Header().Get(xhttp.Location),
   879  		})
   880  		writeResponse(w, http.StatusCreated, resp, mimeXML)
   881  	case "200":
   882  		writeSuccessResponseHeadersOnly(w)
   883  	default:
   884  		writeSuccessNoContent(w)
   885  	}
   886  }
   887  
   888  // GetBucketPolicyStatusHandler -  Retrieves the policy status
   889  // for an MinIO bucket, indicating whether the bucket is public.
   890  func (api ObjectAPIHandlers) GetBucketPolicyStatusHandler(w http.ResponseWriter, r *http.Request) {
   891  	ctx := NewContext(r, w, "GetBucketPolicyStatus")
   892  
   893  	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
   894  
   895  	vars := mux.Vars(r)
   896  	bucket := vars["bucket"]
   897  
   898  	objectAPI := api.ObjectAPI()
   899  	if objectAPI == nil {
   900  		writeErrorResponseHeadersOnly(w, errorCodes.ToAPIErr(ErrServerNotInitialized))
   901  		return
   902  	}
   903  
   904  	if s3Error := checkRequestAuthType(ctx, r, policy.GetBucketPolicyStatusAction, bucket, ""); s3Error != ErrNone {
   905  		writeErrorResponseHeadersOnly(w, errorCodes.ToAPIErr(s3Error))
   906  		return
   907  	}
   908  
   909  	// Check if bucket exists.
   910  	if _, err := objectAPI.GetBucketInfo(ctx, bucket); err != nil {
   911  		WriteErrorResponse(ctx, w, ToAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
   912  		return
   913  	}
   914  
   915  	// Check if anonymous (non-owner) has access to list objects.
   916  	readable := globalPolicySys.IsAllowed(policy.Args{
   917  		Action:          policy.ListBucketAction,
   918  		BucketName:      bucket,
   919  		ConditionValues: getConditionValues(r, "", "", nil),
   920  		IsOwner:         false,
   921  	})
   922  
   923  	// Check if anonymous (non-owner) has access to upload objects.
   924  	writable := globalPolicySys.IsAllowed(policy.Args{
   925  		Action:          policy.PutObjectAction,
   926  		BucketName:      bucket,
   927  		ConditionValues: getConditionValues(r, "", "", nil),
   928  		IsOwner:         false,
   929  	})
   930  
   931  	encodedSuccessResponse := EncodeResponse(PolicyStatus{
   932  		IsPublic: func() string {
   933  			// Silly to have special 'boolean' values yes
   934  			// but complying with silly implementation
   935  			// https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketPolicyStatus.html
   936  			if readable && writable {
   937  				return "TRUE"
   938  			}
   939  			return "FALSE"
   940  		}(),
   941  	})
   942  
   943  	WriteSuccessResponseXML(w, encodedSuccessResponse)
   944  }
   945  
   946  // HeadBucketHandler - HEAD Bucket
   947  // ----------
   948  // This operation is useful to determine if a bucket exists.
   949  // The operation returns a 200 OK if the bucket exists and you
   950  // have permission to access it. Otherwise, the operation might
   951  // return responses such as 404 Not Found and 403 Forbidden.
   952  func (api ObjectAPIHandlers) HeadBucketHandler(w http.ResponseWriter, r *http.Request) {
   953  	ctx := NewContext(r, w, "HeadBucket")
   954  
   955  	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
   956  
   957  	vars := mux.Vars(r)
   958  	bucket := vars["bucket"]
   959  
   960  	objectAPI := api.ObjectAPI()
   961  	if objectAPI == nil {
   962  		writeErrorResponseHeadersOnly(w, errorCodes.ToAPIErr(ErrServerNotInitialized))
   963  		return
   964  	}
   965  
   966  	if s3Error := checkRequestAuthType(ctx, r, policy.ListBucketAction, bucket, ""); s3Error != ErrNone {
   967  		writeErrorResponseHeadersOnly(w, errorCodes.ToAPIErr(s3Error))
   968  		return
   969  	}
   970  
   971  	getBucketInfo := objectAPI.GetBucketInfo
   972  
   973  	if _, err := getBucketInfo(ctx, bucket); err != nil {
   974  		writeErrorResponseHeadersOnly(w, ToAPIError(ctx, err))
   975  		return
   976  	}
   977  
   978  	writeSuccessResponseHeadersOnly(w)
   979  }
   980  
   981  // DeleteBucketHandler - Delete bucket
   982  func (api ObjectAPIHandlers) DeleteBucketHandler(w http.ResponseWriter, r *http.Request) {
   983  	ctx := NewContext(r, w, "DeleteBucket")
   984  
   985  	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
   986  
   987  	vars := mux.Vars(r)
   988  	bucket := vars["bucket"]
   989  
   990  	objectAPI := api.ObjectAPI()
   991  	if objectAPI == nil {
   992  		WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL, guessIsBrowserReq(r))
   993  		return
   994  	}
   995  
   996  	// Verify if the caller has sufficient permissions.
   997  	if s3Error := checkRequestAuthType(ctx, r, policy.DeleteBucketAction, bucket, ""); s3Error != ErrNone {
   998  		WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL, guessIsBrowserReq(r))
   999  		return
  1000  	}
  1001  
  1002  	forceDelete := false
  1003  	if value := r.Header.Get(xhttp.MinIOForceDelete); value != "" {
  1004  		var err error
  1005  		forceDelete, err = strconv.ParseBool(value)
  1006  		if err != nil {
  1007  			apiErr := errorCodes.ToAPIErr(ErrInvalidRequest)
  1008  			apiErr.Description = err.Error()
  1009  			WriteErrorResponse(ctx, w, apiErr, r.URL, guessIsBrowserReq(r))
  1010  			return
  1011  		}
  1012  
  1013  		// if force delete header is set, we need to evaluate the policy anyways
  1014  		// regardless of it being true or not.
  1015  		if s3Error := checkRequestAuthType(ctx, r, policy.ForceDeleteBucketAction, bucket, ""); s3Error != ErrNone {
  1016  			WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL, guessIsBrowserReq(r))
  1017  			return
  1018  		}
  1019  
  1020  		if forceDelete {
  1021  			if rcfg, _ := globalBucketObjectLockSys.Get(bucket); rcfg.LockEnabled {
  1022  				WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrMethodNotAllowed), r.URL, guessIsBrowserReq(r))
  1023  				return
  1024  			}
  1025  		}
  1026  	}
  1027  
  1028  	deleteBucket := objectAPI.DeleteBucket
  1029  
  1030  	// Attempt to delete bucket.
  1031  	if err := deleteBucket(ctx, bucket, forceDelete); err != nil {
  1032  		if _, ok := err.(BucketNotEmpty); ok && (globalBucketVersioningSys.Enabled(bucket) || globalBucketVersioningSys.Suspended(bucket)) {
  1033  			apiErr := ToAPIError(ctx, err)
  1034  			apiErr.Description = "The bucket you tried to delete is not empty. You must delete all versions in the bucket."
  1035  			WriteErrorResponse(ctx, w, apiErr, r.URL, guessIsBrowserReq(r))
  1036  		} else {
  1037  			WriteErrorResponse(ctx, w, ToAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
  1038  		}
  1039  		return
  1040  	}
  1041  
  1042  	GlobalNotificationSys.DeleteBucketMetadata(ctx, bucket)
  1043  
  1044  	// Write success response.
  1045  	writeSuccessNoContent(w)
  1046  
  1047  	sendEvent(eventArgs{
  1048  		EventName:    event.BucketRemoved,
  1049  		BucketName:   bucket,
  1050  		ReqParams:    extractReqParams(r),
  1051  		RespElements: extractRespElements(w),
  1052  		UserAgent:    r.UserAgent(),
  1053  		Host:         handlers.GetSourceIP(r),
  1054  	})
  1055  }
  1056  
  1057  // PutBucketObjectLockConfigHandler - PUT Bucket object lock configuration.
  1058  // ----------
  1059  // Places an Object Lock configuration on the specified bucket. The rule
  1060  // specified in the Object Lock configuration will be applied by default
  1061  // to every new object placed in the specified bucket.
  1062  func (api ObjectAPIHandlers) PutBucketObjectLockConfigHandler(w http.ResponseWriter, r *http.Request) {
  1063  	ctx := NewContext(r, w, "PutBucketObjectLockConfig")
  1064  
  1065  	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
  1066  
  1067  	vars := mux.Vars(r)
  1068  	bucket := vars["bucket"]
  1069  
  1070  	objectAPI := api.ObjectAPI()
  1071  	if objectAPI == nil {
  1072  		WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL, guessIsBrowserReq(r))
  1073  		return
  1074  	}
  1075  	if !globalIsErasure {
  1076  		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL)
  1077  		return
  1078  	}
  1079  	if s3Error := checkRequestAuthType(ctx, r, policy.PutBucketObjectLockConfigurationAction, bucket, ""); s3Error != ErrNone {
  1080  		WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL, guessIsBrowserReq(r))
  1081  		return
  1082  	}
  1083  
  1084  	config, err := objectlock.ParseObjectLockConfig(r.Body)
  1085  	if err != nil {
  1086  		apiErr := errorCodes.ToAPIErr(ErrMalformedXML)
  1087  		apiErr.Description = err.Error()
  1088  		WriteErrorResponse(ctx, w, apiErr, r.URL, guessIsBrowserReq(r))
  1089  		return
  1090  	}
  1091  
  1092  	configData, err := xml.Marshal(config)
  1093  	if err != nil {
  1094  		WriteErrorResponse(ctx, w, ToAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
  1095  		return
  1096  	}
  1097  
  1098  	// Deny object locking configuration settings on existing buckets without object lock enabled.
  1099  	if _, err = globalBucketMetadataSys.GetObjectLockConfig(bucket); err != nil {
  1100  		WriteErrorResponse(ctx, w, ToAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
  1101  		return
  1102  	}
  1103  
  1104  	if err = globalBucketMetadataSys.Update(bucket, objectLockConfig, configData); err != nil {
  1105  		WriteErrorResponse(ctx, w, ToAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
  1106  		return
  1107  	}
  1108  
  1109  	// Write success response.
  1110  	writeSuccessResponseHeadersOnly(w)
  1111  }
  1112  
  1113  // GetBucketObjectLockConfigHandler - GET Bucket object lock configuration.
  1114  // ----------
  1115  // Gets the Object Lock configuration for a bucket. The rule specified in
  1116  // the Object Lock configuration will be applied by default to every new
  1117  // object placed in the specified bucket.
  1118  func (api ObjectAPIHandlers) GetBucketObjectLockConfigHandler(w http.ResponseWriter, r *http.Request) {
  1119  	ctx := NewContext(r, w, "GetBucketObjectLockConfig")
  1120  
  1121  	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
  1122  
  1123  	vars := mux.Vars(r)
  1124  	bucket := vars["bucket"]
  1125  
  1126  	objectAPI := api.ObjectAPI()
  1127  	if objectAPI == nil {
  1128  		WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL, guessIsBrowserReq(r))
  1129  		return
  1130  	}
  1131  
  1132  	// check if user has permissions to perform this operation
  1133  	if s3Error := checkRequestAuthType(ctx, r, policy.GetBucketObjectLockConfigurationAction, bucket, ""); s3Error != ErrNone {
  1134  		WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL, guessIsBrowserReq(r))
  1135  		return
  1136  	}
  1137  
  1138  	config, err := globalBucketMetadataSys.GetObjectLockConfig(bucket)
  1139  	if err != nil {
  1140  		WriteErrorResponse(ctx, w, ToAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
  1141  		return
  1142  	}
  1143  
  1144  	configData, err := xml.Marshal(config)
  1145  	if err != nil {
  1146  		WriteErrorResponse(ctx, w, ToAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
  1147  		return
  1148  	}
  1149  
  1150  	// Write success response.
  1151  	WriteSuccessResponseXML(w, configData)
  1152  }
  1153  
  1154  // PutBucketTaggingHandler - PUT Bucket tagging.
  1155  // ----------
  1156  func (api ObjectAPIHandlers) PutBucketTaggingHandler(w http.ResponseWriter, r *http.Request) {
  1157  	ctx := NewContext(r, w, "PutBucketTagging")
  1158  
  1159  	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
  1160  
  1161  	vars := mux.Vars(r)
  1162  	bucket := vars["bucket"]
  1163  
  1164  	objectAPI := api.ObjectAPI()
  1165  	if objectAPI == nil {
  1166  		WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL, guessIsBrowserReq(r))
  1167  		return
  1168  	}
  1169  
  1170  	if s3Error := checkRequestAuthType(ctx, r, policy.PutBucketTaggingAction, bucket, ""); s3Error != ErrNone {
  1171  		WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL, guessIsBrowserReq(r))
  1172  		return
  1173  	}
  1174  
  1175  	tags, err := tags.ParseBucketXML(io.LimitReader(r.Body, r.ContentLength))
  1176  	if err != nil {
  1177  		apiErr := errorCodes.ToAPIErr(ErrMalformedXML)
  1178  		apiErr.Description = err.Error()
  1179  		WriteErrorResponse(ctx, w, apiErr, r.URL, guessIsBrowserReq(r))
  1180  		return
  1181  	}
  1182  
  1183  	configData, err := xml.Marshal(tags)
  1184  	if err != nil {
  1185  		WriteErrorResponse(ctx, w, ToAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
  1186  		return
  1187  	}
  1188  
  1189  	if err = globalBucketMetadataSys.Update(bucket, bucketTaggingConfig, configData); err != nil {
  1190  		WriteErrorResponse(ctx, w, ToAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
  1191  		return
  1192  	}
  1193  
  1194  	// Write success response.
  1195  	writeSuccessResponseHeadersOnly(w)
  1196  }
  1197  
  1198  // GetBucketTaggingHandler - GET Bucket tagging.
  1199  // ----------
  1200  func (api ObjectAPIHandlers) GetBucketTaggingHandler(w http.ResponseWriter, r *http.Request) {
  1201  	ctx := NewContext(r, w, "GetBucketTagging")
  1202  
  1203  	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
  1204  
  1205  	vars := mux.Vars(r)
  1206  	bucket := vars["bucket"]
  1207  
  1208  	objectAPI := api.ObjectAPI()
  1209  	if objectAPI == nil {
  1210  		WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL, guessIsBrowserReq(r))
  1211  		return
  1212  	}
  1213  
  1214  	// check if user has permissions to perform this operation
  1215  	if s3Error := checkRequestAuthType(ctx, r, policy.GetBucketTaggingAction, bucket, ""); s3Error != ErrNone {
  1216  		WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL, guessIsBrowserReq(r))
  1217  		return
  1218  	}
  1219  
  1220  	config, err := globalBucketMetadataSys.GetTaggingConfig(bucket)
  1221  	if err != nil {
  1222  		WriteErrorResponse(ctx, w, ToAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
  1223  		return
  1224  	}
  1225  
  1226  	configData, err := xml.Marshal(config)
  1227  	if err != nil {
  1228  		WriteErrorResponse(ctx, w, ToAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
  1229  		return
  1230  	}
  1231  
  1232  	// Write success response.
  1233  	WriteSuccessResponseXML(w, configData)
  1234  }
  1235  
  1236  // DeleteBucketTaggingHandler - DELETE Bucket tagging.
  1237  // ----------
  1238  func (api ObjectAPIHandlers) DeleteBucketTaggingHandler(w http.ResponseWriter, r *http.Request) {
  1239  	ctx := NewContext(r, w, "DeleteBucketTagging")
  1240  
  1241  	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
  1242  
  1243  	vars := mux.Vars(r)
  1244  	bucket := vars["bucket"]
  1245  
  1246  	objectAPI := api.ObjectAPI()
  1247  	if objectAPI == nil {
  1248  		WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL, guessIsBrowserReq(r))
  1249  		return
  1250  	}
  1251  
  1252  	if s3Error := checkRequestAuthType(ctx, r, policy.PutBucketTaggingAction, bucket, ""); s3Error != ErrNone {
  1253  		WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL, guessIsBrowserReq(r))
  1254  		return
  1255  	}
  1256  
  1257  	if err := globalBucketMetadataSys.Update(bucket, bucketTaggingConfig, nil); err != nil {
  1258  		WriteErrorResponse(ctx, w, ToAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
  1259  		return
  1260  	}
  1261  
  1262  	// Write success response.
  1263  	writeSuccessResponseHeadersOnly(w)
  1264  }
  1265  
  1266  // PutBucketReplicationConfigHandler - PUT Bucket replication configuration.
  1267  // ----------
  1268  // Add a replication configuration on the specified bucket as specified in https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketReplication.html
  1269  func (api ObjectAPIHandlers) PutBucketReplicationConfigHandler(w http.ResponseWriter, r *http.Request) {
  1270  	ctx := NewContext(r, w, "PutBucketReplicationConfig")
  1271  	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
  1272  
  1273  	vars := mux.Vars(r)
  1274  	bucket := vars["bucket"]
  1275  	objectAPI := api.ObjectAPI()
  1276  	if objectAPI == nil {
  1277  		WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL, guessIsBrowserReq(r))
  1278  		return
  1279  	}
  1280  	if !globalIsErasure {
  1281  		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL)
  1282  		return
  1283  	}
  1284  	if s3Error := checkRequestAuthType(ctx, r, policy.PutReplicationConfigurationAction, bucket, ""); s3Error != ErrNone {
  1285  		WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL, guessIsBrowserReq(r))
  1286  		return
  1287  	}
  1288  	// Check if bucket exists.
  1289  	if _, err := objectAPI.GetBucketInfo(ctx, bucket); err != nil {
  1290  		WriteErrorResponse(ctx, w, ToAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
  1291  		return
  1292  	}
  1293  
  1294  	if versioned := globalBucketVersioningSys.Enabled(bucket); !versioned {
  1295  		WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrReplicationNeedsVersioningError), r.URL, guessIsBrowserReq(r))
  1296  		return
  1297  	}
  1298  	replicationConfig, err := replication.ParseConfig(io.LimitReader(r.Body, r.ContentLength))
  1299  	if err != nil {
  1300  		apiErr := errorCodes.ToAPIErr(ErrMalformedXML)
  1301  		apiErr.Description = err.Error()
  1302  		WriteErrorResponse(ctx, w, apiErr, r.URL, guessIsBrowserReq(r))
  1303  		return
  1304  	}
  1305  	sameTarget, err := validateReplicationDestination(ctx, bucket, replicationConfig)
  1306  	if err != nil {
  1307  		WriteErrorResponse(ctx, w, ToAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
  1308  		return
  1309  	}
  1310  	// Validate the received bucket replication config
  1311  	if err = replicationConfig.Validate(bucket, sameTarget); err != nil {
  1312  		WriteErrorResponse(ctx, w, ToAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
  1313  		return
  1314  	}
  1315  	configData, err := xml.Marshal(replicationConfig)
  1316  	if err != nil {
  1317  		WriteErrorResponse(ctx, w, ToAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
  1318  		return
  1319  	}
  1320  	if err = globalBucketMetadataSys.Update(bucket, bucketReplicationConfig, configData); err != nil {
  1321  		WriteErrorResponse(ctx, w, ToAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
  1322  		return
  1323  	}
  1324  
  1325  	// Write success response.
  1326  	writeSuccessResponseHeadersOnly(w)
  1327  }
  1328  
  1329  // GetBucketReplicationConfigHandler - GET Bucket replication configuration.
  1330  // ----------
  1331  // Gets the replication configuration for a bucket.
  1332  func (api ObjectAPIHandlers) GetBucketReplicationConfigHandler(w http.ResponseWriter, r *http.Request) {
  1333  	ctx := NewContext(r, w, "GetBucketReplicationConfig")
  1334  
  1335  	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
  1336  
  1337  	vars := mux.Vars(r)
  1338  	bucket := vars["bucket"]
  1339  
  1340  	objectAPI := api.ObjectAPI()
  1341  	if objectAPI == nil {
  1342  		WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL, guessIsBrowserReq(r))
  1343  		return
  1344  	}
  1345  
  1346  	// check if user has permissions to perform this operation
  1347  	if s3Error := checkRequestAuthType(ctx, r, policy.GetReplicationConfigurationAction, bucket, ""); s3Error != ErrNone {
  1348  		WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL, guessIsBrowserReq(r))
  1349  		return
  1350  	}
  1351  	// Check if bucket exists.
  1352  	if _, err := objectAPI.GetBucketInfo(ctx, bucket); err != nil {
  1353  		WriteErrorResponse(ctx, w, ToAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
  1354  		return
  1355  	}
  1356  
  1357  	config, err := globalBucketMetadataSys.GetReplicationConfig(ctx, bucket)
  1358  	if err != nil {
  1359  		WriteErrorResponse(ctx, w, ToAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
  1360  		return
  1361  	}
  1362  	configData, err := xml.Marshal(config)
  1363  	if err != nil {
  1364  		WriteErrorResponse(ctx, w, ToAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
  1365  		return
  1366  	}
  1367  
  1368  	// Write success response.
  1369  	WriteSuccessResponseXML(w, configData)
  1370  }
  1371  
  1372  // DeleteBucketReplicationConfigHandler - DELETE Bucket replication config.
  1373  // ----------
  1374  func (api ObjectAPIHandlers) DeleteBucketReplicationConfigHandler(w http.ResponseWriter, r *http.Request) {
  1375  	ctx := NewContext(r, w, "DeleteBucketReplicationConfig")
  1376  	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
  1377  	vars := mux.Vars(r)
  1378  	bucket := vars["bucket"]
  1379  
  1380  	objectAPI := api.ObjectAPI()
  1381  	if objectAPI == nil {
  1382  		WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL, guessIsBrowserReq(r))
  1383  		return
  1384  	}
  1385  
  1386  	if s3Error := checkRequestAuthType(ctx, r, policy.PutReplicationConfigurationAction, bucket, ""); s3Error != ErrNone {
  1387  		WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL, guessIsBrowserReq(r))
  1388  		return
  1389  	}
  1390  	// Check if bucket exists.
  1391  	if _, err := objectAPI.GetBucketInfo(ctx, bucket); err != nil {
  1392  		WriteErrorResponse(ctx, w, ToAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
  1393  		return
  1394  	}
  1395  	if err := globalBucketMetadataSys.Update(bucket, bucketReplicationConfig, nil); err != nil {
  1396  		WriteErrorResponse(ctx, w, ToAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
  1397  		return
  1398  	}
  1399  
  1400  	// Write success response.
  1401  	writeSuccessResponseHeadersOnly(w)
  1402  }
  1403  
  1404  // GetBucketReplicationMetricsHandler - GET Bucket replication metrics.
  1405  // ----------
  1406  // Gets the replication metrics for a bucket.
  1407  func (api ObjectAPIHandlers) GetBucketReplicationMetricsHandler(w http.ResponseWriter, r *http.Request) {
  1408  	ctx := NewContext(r, w, "GetBucketReplicationMetrics")
  1409  
  1410  	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
  1411  
  1412  	vars := mux.Vars(r)
  1413  	bucket := vars["bucket"]
  1414  
  1415  	objectAPI := api.ObjectAPI()
  1416  	if objectAPI == nil {
  1417  		WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL, guessIsBrowserReq(r))
  1418  		return
  1419  	}
  1420  
  1421  	// check if user has permissions to perform this operation
  1422  	if s3Error := checkRequestAuthType(ctx, r, policy.GetReplicationConfigurationAction, bucket, ""); s3Error != ErrNone {
  1423  		WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL, guessIsBrowserReq(r))
  1424  		return
  1425  	}
  1426  
  1427  	// Check if bucket exists.
  1428  	if _, err := objectAPI.GetBucketInfo(ctx, bucket); err != nil {
  1429  		WriteErrorResponse(ctx, w, ToAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
  1430  		return
  1431  	}
  1432  
  1433  	bucketStats := GlobalNotificationSys.GetClusterBucketStats(r.Context(), bucket)
  1434  	bucketReplStats := BucketReplicationStats{}
  1435  	// sum up metrics from each node in the cluster
  1436  	for _, bucketStat := range bucketStats {
  1437  		bucketReplStats.FailedCount += bucketStat.ReplicationStats.FailedCount
  1438  		bucketReplStats.FailedSize += bucketStat.ReplicationStats.FailedSize
  1439  		bucketReplStats.PendingCount += bucketStat.ReplicationStats.PendingCount
  1440  		bucketReplStats.PendingSize += bucketStat.ReplicationStats.PendingSize
  1441  		bucketReplStats.ReplicaSize += bucketStat.ReplicationStats.ReplicaSize
  1442  		bucketReplStats.ReplicatedSize += bucketStat.ReplicationStats.ReplicatedSize
  1443  	}
  1444  	// add initial usage from the time of cluster up
  1445  	usageStat := globalReplicationStats.GetInitialUsage(bucket)
  1446  	bucketReplStats.FailedCount += usageStat.FailedCount
  1447  	bucketReplStats.FailedSize += usageStat.FailedSize
  1448  	bucketReplStats.PendingCount += usageStat.PendingCount
  1449  	bucketReplStats.PendingSize += usageStat.PendingSize
  1450  	bucketReplStats.ReplicaSize += usageStat.ReplicaSize
  1451  	bucketReplStats.ReplicatedSize += usageStat.ReplicatedSize
  1452  
  1453  	if err := json.NewEncoder(w).Encode(&bucketReplStats); err != nil {
  1454  		WriteErrorResponse(ctx, w, ToAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
  1455  		return
  1456  	}
  1457  	w.(http.Flusher).Flush()
  1458  }