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

     1  /*
     2   * MinIO Cloud Storage, (C) 2016-2019 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  	"context"
    21  	"crypto/subtle"
    22  	"encoding/json"
    23  	"errors"
    24  	"fmt"
    25  	"io"
    26  	"net/http"
    27  	"net/url"
    28  	"os"
    29  	"path"
    30  	"reflect"
    31  	"runtime"
    32  	"strconv"
    33  	"strings"
    34  	"time"
    35  
    36  	"github.com/gorilla/mux"
    37  	"github.com/klauspost/compress/zip"
    38  	miniogopolicy "github.com/minio/minio-go/v7/pkg/policy"
    39  	"github.com/minio/minio-go/v7/pkg/s3utils"
    40  
    41  	"storj.io/minio/cmd/config/identity/openid"
    42  	"storj.io/minio/cmd/crypto"
    43  	xhttp "storj.io/minio/cmd/http"
    44  	"storj.io/minio/cmd/logger"
    45  	"storj.io/minio/pkg/auth"
    46  	"storj.io/minio/pkg/bucket/lifecycle"
    47  	objectlock "storj.io/minio/pkg/bucket/object/lock"
    48  	"storj.io/minio/pkg/bucket/policy"
    49  	"storj.io/minio/pkg/bucket/replication"
    50  	"storj.io/minio/pkg/etag"
    51  	"storj.io/minio/pkg/event"
    52  	"storj.io/minio/pkg/handlers"
    53  	"storj.io/minio/pkg/hash"
    54  	iampolicy "storj.io/minio/pkg/iam/policy"
    55  	"storj.io/minio/pkg/ioutil"
    56  	"storj.io/minio/pkg/rpc/json2"
    57  )
    58  
    59  func extractBucketObject(args reflect.Value) (bucketName, objectName string) {
    60  	switch args.Kind() {
    61  	case reflect.Ptr:
    62  		a := args.Elem()
    63  		for i := 0; i < a.NumField(); i++ {
    64  			switch a.Type().Field(i).Name {
    65  			case "BucketName":
    66  				bucketName = a.Field(i).String()
    67  			case "Prefix":
    68  				objectName = a.Field(i).String()
    69  			case "ObjectName":
    70  				objectName = a.Field(i).String()
    71  			}
    72  		}
    73  	}
    74  	return bucketName, objectName
    75  }
    76  
    77  // WebGenericArgs - empty struct for calls that don't accept arguments
    78  // for ex. ServerInfo
    79  type WebGenericArgs struct{}
    80  
    81  // WebGenericRep - reply structure for calls for which reply is success/failure
    82  // for ex. RemoveObject MakeBucket
    83  type WebGenericRep struct {
    84  	UIVersion string `json:"uiVersion"`
    85  }
    86  
    87  // ServerInfoRep - server info reply.
    88  type ServerInfoRep struct {
    89  	MinioVersion    string
    90  	MinioMemory     string
    91  	MinioPlatform   string
    92  	MinioRuntime    string
    93  	MinioGlobalInfo map[string]interface{}
    94  	MinioUserInfo   map[string]interface{}
    95  	UIVersion       string `json:"uiVersion"`
    96  }
    97  
    98  // ServerInfo - get server info.
    99  func (web *webAPIHandlers) ServerInfo(r *http.Request, args *WebGenericArgs, reply *ServerInfoRep) error {
   100  	ctx := newWebContext(r, args, "WebServerInfo")
   101  	claims, owner, authErr := webRequestAuthenticate(r)
   102  	if authErr != nil {
   103  		return toJSONError(ctx, authErr)
   104  	}
   105  	host, err := os.Hostname()
   106  	if err != nil {
   107  		host = ""
   108  	}
   109  	platform := fmt.Sprintf("Host: %s | OS: %s | Arch: %s",
   110  		host,
   111  		runtime.GOOS,
   112  		runtime.GOARCH)
   113  	goruntime := fmt.Sprintf("Version: %s | CPUs: %d", runtime.Version(), runtime.NumCPU())
   114  
   115  	reply.MinioVersion = Version
   116  	reply.MinioGlobalInfo = getGlobalInfo()
   117  
   118  	// Check if the user is IAM user.
   119  	reply.MinioUserInfo = map[string]interface{}{
   120  		"isIAMUser": !owner,
   121  	}
   122  
   123  	if !owner {
   124  		creds, ok := GlobalIAMSys.GetUser(ctx, claims.AccessKey)
   125  		if ok && creds.SessionToken != "" {
   126  			reply.MinioUserInfo["isTempUser"] = true
   127  		}
   128  	}
   129  
   130  	reply.MinioPlatform = platform
   131  	reply.MinioRuntime = goruntime
   132  	reply.UIVersion = Version
   133  	return nil
   134  }
   135  
   136  // StorageInfoRep - contains storage usage statistics.
   137  type StorageInfoRep struct {
   138  	Used      uint64 `json:"used"`
   139  	UIVersion string `json:"uiVersion"`
   140  }
   141  
   142  // StorageInfo - web call to gather storage usage statistics.
   143  func (web *webAPIHandlers) StorageInfo(r *http.Request, args *WebGenericArgs, reply *StorageInfoRep) error {
   144  	ctx := newWebContext(r, args, "WebStorageInfo")
   145  	objectAPI := web.ObjectAPI()
   146  	if objectAPI == nil {
   147  		return toJSONError(ctx, errServerNotInitialized)
   148  	}
   149  	_, _, authErr := webRequestAuthenticate(r)
   150  	if authErr != nil {
   151  		return toJSONError(ctx, authErr)
   152  	}
   153  	dataUsageInfo, _ := loadDataUsageFromBackend(ctx, objectAPI)
   154  	reply.Used = dataUsageInfo.ObjectsTotalSize
   155  	reply.UIVersion = Version
   156  	return nil
   157  }
   158  
   159  // MakeBucketArgs - make bucket args.
   160  type MakeBucketArgs struct {
   161  	BucketName string `json:"bucketName"`
   162  }
   163  
   164  // MakeBucket - creates a new bucket.
   165  func (web *webAPIHandlers) MakeBucket(r *http.Request, args *MakeBucketArgs, reply *WebGenericRep) error {
   166  	ctx := newWebContext(r, args, "WebMakeBucket")
   167  	objectAPI := web.ObjectAPI()
   168  	if objectAPI == nil {
   169  		return toJSONError(ctx, errServerNotInitialized)
   170  	}
   171  	claims, owner, authErr := webRequestAuthenticate(r)
   172  	if authErr != nil {
   173  		return toJSONError(ctx, authErr)
   174  	}
   175  
   176  	// For authenticated users apply IAM policy.
   177  	if !GlobalIAMSys.IsAllowed(iampolicy.Args{
   178  		AccountName:     claims.AccessKey,
   179  		Action:          iampolicy.CreateBucketAction,
   180  		BucketName:      args.BucketName,
   181  		ConditionValues: getConditionValues(r, "", claims.AccessKey, claims.Map()),
   182  		IsOwner:         owner,
   183  		Claims:          claims.Map(),
   184  	}) {
   185  		return toJSONError(ctx, errAccessDenied)
   186  	}
   187  
   188  	// Check if bucket is a reserved bucket name or invalid.
   189  	if isReservedOrInvalidBucket(args.BucketName, true) {
   190  		return toJSONError(ctx, errInvalidBucketName, args.BucketName)
   191  	}
   192  
   193  	opts := BucketOptions{
   194  		Location:    globalServerRegion,
   195  		LockEnabled: false,
   196  	}
   197  
   198  	if err := objectAPI.MakeBucketWithLocation(ctx, args.BucketName, opts); err != nil {
   199  		return toJSONError(ctx, err, args.BucketName)
   200  	}
   201  
   202  	reply.UIVersion = Version
   203  
   204  	reqParams := extractReqParams(r)
   205  	reqParams["accessKey"] = claims.GetAccessKey()
   206  
   207  	sendEvent(eventArgs{
   208  		EventName:  event.BucketCreated,
   209  		BucketName: args.BucketName,
   210  		ReqParams:  reqParams,
   211  		UserAgent:  r.UserAgent(),
   212  		Host:       handlers.GetSourceIP(r),
   213  	})
   214  
   215  	return nil
   216  }
   217  
   218  // RemoveBucketArgs - remove bucket args.
   219  type RemoveBucketArgs struct {
   220  	BucketName string `json:"bucketName"`
   221  }
   222  
   223  // DeleteBucket - removes a bucket, must be empty.
   224  func (web *webAPIHandlers) DeleteBucket(r *http.Request, args *RemoveBucketArgs, reply *WebGenericRep) error {
   225  	ctx := newWebContext(r, args, "WebDeleteBucket")
   226  	objectAPI := web.ObjectAPI()
   227  	if objectAPI == nil {
   228  		return toJSONError(ctx, errServerNotInitialized)
   229  	}
   230  	claims, owner, authErr := webRequestAuthenticate(r)
   231  	if authErr != nil {
   232  		return toJSONError(ctx, authErr)
   233  	}
   234  
   235  	// For authenticated users apply IAM policy.
   236  	if !GlobalIAMSys.IsAllowed(iampolicy.Args{
   237  		AccountName:     claims.AccessKey,
   238  		Action:          iampolicy.DeleteBucketAction,
   239  		BucketName:      args.BucketName,
   240  		ConditionValues: getConditionValues(r, "", claims.AccessKey, claims.Map()),
   241  		IsOwner:         owner,
   242  		Claims:          claims.Map(),
   243  	}) {
   244  		return toJSONError(ctx, errAccessDenied)
   245  	}
   246  
   247  	// Check if bucket is a reserved bucket name or invalid.
   248  	if isReservedOrInvalidBucket(args.BucketName, false) {
   249  		return toJSONError(ctx, errInvalidBucketName, args.BucketName)
   250  	}
   251  
   252  	reply.UIVersion = Version
   253  
   254  	deleteBucket := objectAPI.DeleteBucket
   255  
   256  	if err := deleteBucket(ctx, args.BucketName, false); err != nil {
   257  		return toJSONError(ctx, err, args.BucketName)
   258  	}
   259  
   260  	GlobalNotificationSys.DeleteBucketMetadata(ctx, args.BucketName)
   261  
   262  	reqParams := extractReqParams(r)
   263  	reqParams["accessKey"] = claims.AccessKey
   264  
   265  	sendEvent(eventArgs{
   266  		EventName:  event.BucketRemoved,
   267  		BucketName: args.BucketName,
   268  		ReqParams:  reqParams,
   269  		UserAgent:  r.UserAgent(),
   270  		Host:       handlers.GetSourceIP(r),
   271  	})
   272  
   273  	return nil
   274  }
   275  
   276  // ListBucketsRep - list buckets response
   277  type ListBucketsRep struct {
   278  	Buckets   []WebBucketInfo `json:"buckets"`
   279  	UIVersion string          `json:"uiVersion"`
   280  }
   281  
   282  // WebBucketInfo container for list buckets metadata.
   283  type WebBucketInfo struct {
   284  	// The name of the bucket.
   285  	Name string `json:"name"`
   286  	// Date the bucket was created.
   287  	CreationDate time.Time `json:"creationDate"`
   288  }
   289  
   290  // ListBuckets - list buckets api.
   291  func (web *webAPIHandlers) ListBuckets(r *http.Request, args *WebGenericArgs, reply *ListBucketsRep) error {
   292  	ctx := newWebContext(r, args, "WebListBuckets")
   293  	objectAPI := web.ObjectAPI()
   294  	if objectAPI == nil {
   295  		return toJSONError(ctx, errServerNotInitialized)
   296  	}
   297  	listBuckets := objectAPI.ListBuckets
   298  
   299  	claims, owner, authErr := webRequestAuthenticate(r)
   300  	if authErr != nil {
   301  		return toJSONError(ctx, authErr)
   302  	}
   303  
   304  	// Set prefix value for "s3:prefix" policy conditionals.
   305  	r.Header.Set("prefix", "")
   306  
   307  	// Set delimiter value for "s3:delimiter" policy conditionals.
   308  	r.Header.Set("delimiter", SlashSeparator)
   309  
   310  	buckets, err := listBuckets(ctx)
   311  	if err != nil {
   312  		return toJSONError(ctx, err)
   313  	}
   314  	for _, bucket := range buckets {
   315  		if GlobalIAMSys.IsAllowed(iampolicy.Args{
   316  			AccountName:     claims.AccessKey,
   317  			Action:          iampolicy.ListBucketAction,
   318  			BucketName:      bucket.Name,
   319  			ConditionValues: getConditionValues(r, "", claims.AccessKey, claims.Map()),
   320  			IsOwner:         owner,
   321  			ObjectName:      "",
   322  			Claims:          claims.Map(),
   323  		}) {
   324  			reply.Buckets = append(reply.Buckets, WebBucketInfo{
   325  				Name:         bucket.Name,
   326  				CreationDate: bucket.Created,
   327  			})
   328  		}
   329  	}
   330  
   331  	reply.UIVersion = Version
   332  	return nil
   333  }
   334  
   335  // ListObjectsArgs - list object args.
   336  type ListObjectsArgs struct {
   337  	BucketName string `json:"bucketName"`
   338  	Prefix     string `json:"prefix"`
   339  	Marker     string `json:"marker"`
   340  }
   341  
   342  // ListObjectsRep - list objects response.
   343  type ListObjectsRep struct {
   344  	Objects   []WebObjectInfo `json:"objects"`
   345  	Writable  bool            `json:"writable"` // Used by client to show "upload file" button.
   346  	UIVersion string          `json:"uiVersion"`
   347  }
   348  
   349  // WebObjectInfo container for list objects metadata.
   350  type WebObjectInfo struct {
   351  	// Name of the object
   352  	Key string `json:"name"`
   353  	// Date and time the object was last modified.
   354  	LastModified time.Time `json:"lastModified"`
   355  	// Size in bytes of the object.
   356  	Size int64 `json:"size"`
   357  	// ContentType is mime type of the object.
   358  	ContentType string `json:"contentType"`
   359  }
   360  
   361  // ListObjects - list objects api.
   362  func (web *webAPIHandlers) ListObjects(r *http.Request, args *ListObjectsArgs, reply *ListObjectsRep) error {
   363  	ctx := newWebContext(r, args, "WebListObjects")
   364  	reply.UIVersion = Version
   365  	objectAPI := web.ObjectAPI()
   366  	if objectAPI == nil {
   367  		return toJSONError(ctx, errServerNotInitialized)
   368  	}
   369  
   370  	listObjects := objectAPI.ListObjects
   371  
   372  	claims, owner, authErr := webRequestAuthenticate(r)
   373  	if authErr != nil {
   374  		if authErr == errNoAuthToken {
   375  			// Set prefix value for "s3:prefix" policy conditionals.
   376  			r.Header.Set("prefix", args.Prefix)
   377  
   378  			// Set delimiter value for "s3:delimiter" policy conditionals.
   379  			r.Header.Set("delimiter", SlashSeparator)
   380  
   381  			// Check if anonymous (non-owner) has access to download objects.
   382  			readable := globalPolicySys.IsAllowed(policy.Args{
   383  				Action:          policy.ListBucketAction,
   384  				BucketName:      args.BucketName,
   385  				ConditionValues: getConditionValues(r, "", "", nil),
   386  				IsOwner:         false,
   387  			})
   388  
   389  			// Check if anonymous (non-owner) has access to upload objects.
   390  			writable := globalPolicySys.IsAllowed(policy.Args{
   391  				Action:          policy.PutObjectAction,
   392  				BucketName:      args.BucketName,
   393  				ConditionValues: getConditionValues(r, "", "", nil),
   394  				IsOwner:         false,
   395  				ObjectName:      args.Prefix + SlashSeparator,
   396  			})
   397  
   398  			reply.Writable = writable
   399  			if !readable {
   400  				// Error out if anonymous user (non-owner) has no access to download or upload objects
   401  				if !writable {
   402  					return errAccessDenied
   403  				}
   404  				// return empty object list if access is write only
   405  				return nil
   406  			}
   407  		} else {
   408  			return toJSONError(ctx, authErr)
   409  		}
   410  	}
   411  
   412  	// For authenticated users apply IAM policy.
   413  	if authErr == nil {
   414  		// Set prefix value for "s3:prefix" policy conditionals.
   415  		r.Header.Set("prefix", args.Prefix)
   416  
   417  		// Set delimiter value for "s3:delimiter" policy conditionals.
   418  		r.Header.Set("delimiter", SlashSeparator)
   419  
   420  		readable := GlobalIAMSys.IsAllowed(iampolicy.Args{
   421  			AccountName:     claims.AccessKey,
   422  			Action:          iampolicy.ListBucketAction,
   423  			BucketName:      args.BucketName,
   424  			ConditionValues: getConditionValues(r, "", claims.AccessKey, claims.Map()),
   425  			IsOwner:         owner,
   426  			Claims:          claims.Map(),
   427  		})
   428  
   429  		writable := GlobalIAMSys.IsAllowed(iampolicy.Args{
   430  			AccountName:     claims.AccessKey,
   431  			Action:          iampolicy.PutObjectAction,
   432  			BucketName:      args.BucketName,
   433  			ConditionValues: getConditionValues(r, "", claims.AccessKey, claims.Map()),
   434  			IsOwner:         owner,
   435  			ObjectName:      args.Prefix + SlashSeparator,
   436  			Claims:          claims.Map(),
   437  		})
   438  
   439  		reply.Writable = writable
   440  		if !readable {
   441  			// Error out if anonymous user (non-owner) has no access to download or upload objects
   442  			if !writable {
   443  				return errAccessDenied
   444  			}
   445  			// return empty object list if access is write only
   446  			return nil
   447  		}
   448  	}
   449  
   450  	// Check if bucket is a reserved bucket name or invalid.
   451  	if isReservedOrInvalidBucket(args.BucketName, false) {
   452  		return toJSONError(ctx, errInvalidBucketName, args.BucketName)
   453  	}
   454  
   455  	nextMarker := ""
   456  	// Fetch all the objects
   457  	for {
   458  		// Limit browser to '1000' batches to be more responsive, scrolling friendly.
   459  		// Also don't change the maxKeys value silly GCS SDKs do not honor maxKeys
   460  		// values to be '-1'
   461  		lo, err := listObjects(ctx, args.BucketName, args.Prefix, nextMarker, SlashSeparator, 1000)
   462  		if err != nil {
   463  			return &json2.Error{Message: err.Error()}
   464  		}
   465  
   466  		nextMarker = lo.NextMarker
   467  		for i := range lo.Objects {
   468  			lo.Objects[i].Size, err = lo.Objects[i].GetActualSize()
   469  			if err != nil {
   470  				return toJSONError(ctx, err)
   471  			}
   472  		}
   473  
   474  		for _, obj := range lo.Objects {
   475  			reply.Objects = append(reply.Objects, WebObjectInfo{
   476  				Key:          obj.Name,
   477  				LastModified: obj.ModTime,
   478  				Size:         obj.Size,
   479  				ContentType:  obj.ContentType,
   480  			})
   481  		}
   482  		for _, prefix := range lo.Prefixes {
   483  			reply.Objects = append(reply.Objects, WebObjectInfo{
   484  				Key: prefix,
   485  			})
   486  		}
   487  
   488  		// Return when there are no more objects
   489  		if !lo.IsTruncated {
   490  			return nil
   491  		}
   492  	}
   493  }
   494  
   495  // RemoveObjectArgs - args to remove an object, JSON will look like.
   496  //
   497  // {
   498  //     "bucketname": "testbucket",
   499  //     "objects": [
   500  //         "photos/hawaii/",
   501  //         "photos/maldives/",
   502  //         "photos/sanjose.jpg"
   503  //     ]
   504  // }
   505  type RemoveObjectArgs struct {
   506  	Objects    []string `json:"objects"`    // Contains objects, prefixes.
   507  	BucketName string   `json:"bucketname"` // Contains bucket name.
   508  }
   509  
   510  // RemoveObject - removes an object, or all the objects at a given prefix.
   511  func (web *webAPIHandlers) RemoveObject(r *http.Request, args *RemoveObjectArgs, reply *WebGenericRep) error {
   512  	ctx := newWebContext(r, args, "WebRemoveObject")
   513  	objectAPI := web.ObjectAPI()
   514  	if objectAPI == nil {
   515  		return toJSONError(ctx, errServerNotInitialized)
   516  	}
   517  
   518  	deleteObjects := objectAPI.DeleteObjects
   519  	if web.CacheAPI() != nil {
   520  		deleteObjects = web.CacheAPI().DeleteObjects
   521  	}
   522  	getObjectInfoFn := objectAPI.GetObjectInfo
   523  	if web.CacheAPI() != nil {
   524  		getObjectInfoFn = web.CacheAPI().GetObjectInfo
   525  	}
   526  
   527  	claims, owner, authErr := webRequestAuthenticate(r)
   528  	if authErr != nil {
   529  		if authErr == errNoAuthToken {
   530  			// Check if all objects are allowed to be deleted anonymously
   531  			for _, object := range args.Objects {
   532  				if !globalPolicySys.IsAllowed(policy.Args{
   533  					Action:          policy.DeleteObjectAction,
   534  					BucketName:      args.BucketName,
   535  					ConditionValues: getConditionValues(r, "", "", nil),
   536  					IsOwner:         false,
   537  					ObjectName:      object,
   538  				}) {
   539  					return toJSONError(ctx, errAuthentication)
   540  				}
   541  			}
   542  		} else {
   543  			return toJSONError(ctx, authErr)
   544  		}
   545  	}
   546  
   547  	if args.BucketName == "" || len(args.Objects) == 0 {
   548  		return toJSONError(ctx, errInvalidArgument)
   549  	}
   550  
   551  	// Check if bucket is a reserved bucket name or invalid.
   552  	if isReservedOrInvalidBucket(args.BucketName, false) {
   553  		return toJSONError(ctx, errInvalidBucketName, args.BucketName)
   554  	}
   555  
   556  	reply.UIVersion = Version
   557  
   558  	opts := ObjectOptions{
   559  		Versioned:        globalBucketVersioningSys.Enabled(args.BucketName),
   560  		VersionSuspended: globalBucketVersioningSys.Suspended(args.BucketName),
   561  	}
   562  	var (
   563  		err           error
   564  		replicateSync bool
   565  	)
   566  
   567  	reqParams := extractReqParams(r)
   568  	reqParams["accessKey"] = claims.GetAccessKey()
   569  	sourceIP := handlers.GetSourceIP(r)
   570  
   571  next:
   572  	for _, objectName := range args.Objects {
   573  		// If not a directory, remove the object.
   574  		if !HasSuffix(objectName, SlashSeparator) && objectName != "" {
   575  			// Check permissions for non-anonymous user.
   576  			if authErr != errNoAuthToken {
   577  				if !GlobalIAMSys.IsAllowed(iampolicy.Args{
   578  					AccountName:     claims.AccessKey,
   579  					Action:          iampolicy.DeleteObjectAction,
   580  					BucketName:      args.BucketName,
   581  					ConditionValues: getConditionValues(r, "", claims.AccessKey, claims.Map()),
   582  					IsOwner:         owner,
   583  					ObjectName:      objectName,
   584  					Claims:          claims.Map(),
   585  				}) {
   586  					return toJSONError(ctx, errAccessDenied)
   587  				}
   588  			}
   589  
   590  			if authErr == errNoAuthToken {
   591  				// Check if object is allowed to be deleted anonymously.
   592  				if !globalPolicySys.IsAllowed(policy.Args{
   593  					Action:          policy.DeleteObjectAction,
   594  					BucketName:      args.BucketName,
   595  					ConditionValues: getConditionValues(r, "", "", nil),
   596  					IsOwner:         false,
   597  					ObjectName:      objectName,
   598  				}) {
   599  					return toJSONError(ctx, errAccessDenied)
   600  				}
   601  			}
   602  			var (
   603  				replicateDel, hasLifecycleConfig bool
   604  				goi                              ObjectInfo
   605  				gerr                             error
   606  			)
   607  			if _, err := globalBucketMetadataSys.GetLifecycleConfig(args.BucketName); err == nil {
   608  				hasLifecycleConfig = true
   609  			}
   610  			if hasReplicationRules(ctx, args.BucketName, []ObjectToDelete{{ObjectName: objectName}}) || hasLifecycleConfig {
   611  				goi, gerr = getObjectInfoFn(ctx, args.BucketName, objectName, opts)
   612  				if replicateDel, replicateSync = checkReplicateDelete(ctx, args.BucketName, ObjectToDelete{
   613  					ObjectName: objectName,
   614  					VersionID:  goi.VersionID,
   615  				}, goi, gerr); replicateDel {
   616  					opts.DeleteMarkerReplicationStatus = string(replication.Pending)
   617  					opts.DeleteMarker = true
   618  				}
   619  			}
   620  
   621  			deleteObject := objectAPI.DeleteObject
   622  			if web.CacheAPI() != nil {
   623  				deleteObject = web.CacheAPI().DeleteObject
   624  			}
   625  
   626  			oi, err := deleteObject(ctx, args.BucketName, objectName, opts)
   627  			if err != nil {
   628  				switch err.(type) {
   629  				case BucketNotFound:
   630  					return toJSONError(ctx, err)
   631  				}
   632  			}
   633  			if oi.Name == "" {
   634  				logger.LogIf(ctx, err)
   635  				continue
   636  			}
   637  
   638  			eventName := event.ObjectRemovedDelete
   639  			if oi.DeleteMarker {
   640  				eventName = event.ObjectRemovedDeleteMarkerCreated
   641  			}
   642  
   643  			// Notify object deleted event.
   644  			sendEvent(eventArgs{
   645  				EventName:  eventName,
   646  				BucketName: args.BucketName,
   647  				Object:     oi,
   648  				ReqParams:  reqParams,
   649  				UserAgent:  r.UserAgent(),
   650  				Host:       sourceIP,
   651  			})
   652  
   653  			if replicateDel {
   654  				dobj := DeletedObjectVersionInfo{
   655  					DeletedObject: DeletedObject{
   656  						ObjectName:                    objectName,
   657  						DeleteMarkerVersionID:         oi.VersionID,
   658  						DeleteMarkerReplicationStatus: string(oi.ReplicationStatus),
   659  						DeleteMarkerMTime:             DeleteMarkerMTime{oi.ModTime},
   660  						DeleteMarker:                  oi.DeleteMarker,
   661  						VersionPurgeStatus:            oi.VersionPurgeStatus,
   662  					},
   663  					Bucket: args.BucketName,
   664  				}
   665  				scheduleReplicationDelete(ctx, dobj, objectAPI, replicateSync)
   666  			}
   667  			if goi.TransitionStatus == lifecycle.TransitionComplete {
   668  				deleteTransitionedObject(ctx, objectAPI, args.BucketName, objectName, lifecycle.ObjectOpts{
   669  					Name:             objectName,
   670  					UserTags:         goi.UserTags,
   671  					VersionID:        goi.VersionID,
   672  					DeleteMarker:     goi.DeleteMarker,
   673  					TransitionStatus: goi.TransitionStatus,
   674  					IsLatest:         goi.IsLatest,
   675  				}, false, true)
   676  			}
   677  
   678  			logger.LogIf(ctx, err)
   679  			continue
   680  		}
   681  
   682  		if authErr == errNoAuthToken {
   683  			// Check if object is allowed to be deleted anonymously
   684  			if !globalPolicySys.IsAllowed(policy.Args{
   685  				Action:          iampolicy.DeleteObjectAction,
   686  				BucketName:      args.BucketName,
   687  				ConditionValues: getConditionValues(r, "", "", nil),
   688  				IsOwner:         false,
   689  				ObjectName:      objectName,
   690  			}) {
   691  				return toJSONError(ctx, errAccessDenied)
   692  			}
   693  		} else {
   694  			if !GlobalIAMSys.IsAllowed(iampolicy.Args{
   695  				AccountName:     claims.AccessKey,
   696  				Action:          iampolicy.DeleteObjectAction,
   697  				BucketName:      args.BucketName,
   698  				ConditionValues: getConditionValues(r, "", claims.AccessKey, claims.Map()),
   699  				IsOwner:         owner,
   700  				ObjectName:      objectName,
   701  				Claims:          claims.Map(),
   702  			}) {
   703  				return toJSONError(ctx, errAccessDenied)
   704  			}
   705  		}
   706  
   707  		// Allocate new results channel to receive ObjectInfo.
   708  		objInfoCh := make(chan ObjectInfo)
   709  
   710  		// Walk through all objects
   711  		if err = objectAPI.Walk(ctx, args.BucketName, objectName, objInfoCh, ObjectOptions{}); err != nil {
   712  			break next
   713  		}
   714  
   715  		for {
   716  			var objects []ObjectToDelete
   717  			for obj := range objInfoCh {
   718  				if len(objects) == maxDeleteList {
   719  					// Reached maximum delete requests, attempt a delete for now.
   720  					break
   721  				}
   722  				if obj.ReplicationStatus == replication.Replica {
   723  					if authErr == errNoAuthToken {
   724  						// Check if object is allowed to be deleted anonymously
   725  						if !globalPolicySys.IsAllowed(policy.Args{
   726  							Action:          iampolicy.ReplicateDeleteAction,
   727  							BucketName:      args.BucketName,
   728  							ConditionValues: getConditionValues(r, "", "", nil),
   729  							IsOwner:         false,
   730  							ObjectName:      objectName,
   731  						}) {
   732  							return toJSONError(ctx, errAccessDenied)
   733  						}
   734  					} else {
   735  						if !GlobalIAMSys.IsAllowed(iampolicy.Args{
   736  							AccountName:     claims.AccessKey,
   737  							Action:          iampolicy.ReplicateDeleteAction,
   738  							BucketName:      args.BucketName,
   739  							ConditionValues: getConditionValues(r, "", claims.AccessKey, claims.Map()),
   740  							IsOwner:         owner,
   741  							ObjectName:      objectName,
   742  							Claims:          claims.Map(),
   743  						}) {
   744  							return toJSONError(ctx, errAccessDenied)
   745  						}
   746  					}
   747  				}
   748  				replicateDel, _ := checkReplicateDelete(ctx, args.BucketName, ObjectToDelete{ObjectName: obj.Name, VersionID: obj.VersionID}, obj, nil)
   749  				// since versioned delete is not available on web browser, yet - this is a simple DeleteMarker replication
   750  				objToDel := ObjectToDelete{ObjectName: obj.Name}
   751  				if replicateDel {
   752  					objToDel.DeleteMarkerReplicationStatus = string(replication.Pending)
   753  				}
   754  
   755  				objects = append(objects, objToDel)
   756  			}
   757  
   758  			// Nothing to do.
   759  			if len(objects) == 0 {
   760  				break next
   761  			}
   762  
   763  			// Deletes a list of objects.
   764  			deletedObjects, errs := deleteObjects(ctx, args.BucketName, objects, opts)
   765  			for i, err := range errs {
   766  				if err != nil && !isErrObjectNotFound(err) {
   767  					deletedObjects[i].DeleteMarkerReplicationStatus = objects[i].DeleteMarkerReplicationStatus
   768  					deletedObjects[i].VersionPurgeStatus = objects[i].VersionPurgeStatus
   769  				}
   770  				if err != nil {
   771  					logger.LogIf(ctx, err)
   772  					break next
   773  				}
   774  			}
   775  			// Notify deleted event for objects.
   776  			for _, dobj := range deletedObjects {
   777  				objInfo := ObjectInfo{
   778  					Name:      dobj.ObjectName,
   779  					VersionID: dobj.VersionID,
   780  				}
   781  				if dobj.DeleteMarker {
   782  					objInfo = ObjectInfo{
   783  						Name:         dobj.ObjectName,
   784  						DeleteMarker: dobj.DeleteMarker,
   785  						VersionID:    dobj.DeleteMarkerVersionID,
   786  					}
   787  				}
   788  				sendEvent(eventArgs{
   789  					EventName:  event.ObjectRemovedDelete,
   790  					BucketName: args.BucketName,
   791  					Object:     objInfo,
   792  					ReqParams:  reqParams,
   793  					UserAgent:  r.UserAgent(),
   794  					Host:       sourceIP,
   795  				})
   796  				if dobj.DeleteMarkerReplicationStatus == string(replication.Pending) || dobj.VersionPurgeStatus == Pending {
   797  					dv := DeletedObjectVersionInfo{
   798  						DeletedObject: dobj,
   799  						Bucket:        args.BucketName,
   800  					}
   801  					scheduleReplicationDelete(ctx, dv, objectAPI, replicateSync)
   802  				}
   803  			}
   804  		}
   805  	}
   806  
   807  	if err != nil && !isErrObjectNotFound(err) && !isErrVersionNotFound(err) {
   808  		// Ignore object not found error.
   809  		return toJSONError(ctx, err, args.BucketName, "")
   810  	}
   811  
   812  	return nil
   813  }
   814  
   815  // LoginArgs - login arguments.
   816  type LoginArgs struct {
   817  	Username string `json:"username" form:"username"`
   818  	Password string `json:"password" form:"password"`
   819  }
   820  
   821  // LoginRep - login reply.
   822  type LoginRep struct {
   823  	Token     string `json:"token"`
   824  	UIVersion string `json:"uiVersion"`
   825  }
   826  
   827  // Login - user login handler.
   828  func (web *webAPIHandlers) Login(r *http.Request, args *LoginArgs, reply *LoginRep) error {
   829  	ctx := newWebContext(r, args, "WebLogin")
   830  	token, err := authenticateWeb(args.Username, args.Password)
   831  	if err != nil {
   832  		return toJSONError(ctx, err)
   833  	}
   834  
   835  	reply.Token = token
   836  	reply.UIVersion = Version
   837  	return nil
   838  }
   839  
   840  // SetAuthArgs - argument for SetAuth
   841  type SetAuthArgs struct {
   842  	CurrentAccessKey string `json:"currentAccessKey"`
   843  	CurrentSecretKey string `json:"currentSecretKey"`
   844  	NewAccessKey     string `json:"newAccessKey"`
   845  	NewSecretKey     string `json:"newSecretKey"`
   846  }
   847  
   848  // SetAuthReply - reply for SetAuth
   849  type SetAuthReply struct {
   850  	Token       string            `json:"token"`
   851  	UIVersion   string            `json:"uiVersion"`
   852  	PeerErrMsgs map[string]string `json:"peerErrMsgs"`
   853  }
   854  
   855  // SetAuth - Set accessKey and secretKey credentials.
   856  func (web *webAPIHandlers) SetAuth(r *http.Request, args *SetAuthArgs, reply *SetAuthReply) error {
   857  	ctx := newWebContext(r, args, "WebSetAuth")
   858  	claims, owner, authErr := webRequestAuthenticate(r)
   859  	if authErr != nil {
   860  		return toJSONError(ctx, authErr)
   861  	}
   862  
   863  	if owner {
   864  		// Owner is not allowed to change credentials through browser.
   865  		return toJSONError(ctx, errChangeCredNotAllowed)
   866  	}
   867  
   868  	if !GlobalIAMSys.IsAllowed(iampolicy.Args{
   869  		AccountName:     claims.AccessKey,
   870  		Action:          iampolicy.CreateUserAdminAction,
   871  		IsOwner:         false,
   872  		ConditionValues: getConditionValues(r, "", claims.AccessKey, claims.Map()),
   873  		Claims:          claims.Map(),
   874  		DenyOnly:        true,
   875  	}) {
   876  		return toJSONError(ctx, errChangeCredNotAllowed)
   877  	}
   878  
   879  	// for IAM users, access key cannot be updated
   880  	// claims.AccessKey is used instead of accesskey from args
   881  	prevCred, ok := GlobalIAMSys.GetUser(ctx, claims.AccessKey)
   882  	if !ok {
   883  		return errInvalidAccessKeyID
   884  	}
   885  
   886  	// Throw error when wrong secret key is provided
   887  	if subtle.ConstantTimeCompare([]byte(prevCred.SecretKey), []byte(args.CurrentSecretKey)) != 1 {
   888  		return errIncorrectCreds
   889  	}
   890  
   891  	creds, err := auth.CreateCredentials(claims.AccessKey, args.NewSecretKey)
   892  	if err != nil {
   893  		return toJSONError(ctx, err)
   894  	}
   895  
   896  	err = GlobalIAMSys.SetUserSecretKey(creds.AccessKey, creds.SecretKey)
   897  	if err != nil {
   898  		return toJSONError(ctx, err)
   899  	}
   900  
   901  	reply.Token, err = authenticateWeb(creds.AccessKey, creds.SecretKey)
   902  	if err != nil {
   903  		return toJSONError(ctx, err)
   904  	}
   905  
   906  	reply.UIVersion = Version
   907  
   908  	return nil
   909  }
   910  
   911  // URLTokenReply contains the reply for CreateURLToken.
   912  type URLTokenReply struct {
   913  	Token     string `json:"token"`
   914  	UIVersion string `json:"uiVersion"`
   915  }
   916  
   917  // CreateURLToken creates a URL token (short-lived) for GET requests.
   918  func (web *webAPIHandlers) CreateURLToken(r *http.Request, args *WebGenericArgs, reply *URLTokenReply) error {
   919  	ctx := newWebContext(r, args, "WebCreateURLToken")
   920  	claims, owner, authErr := webRequestAuthenticate(r)
   921  	if authErr != nil {
   922  		return toJSONError(ctx, authErr)
   923  	}
   924  
   925  	creds := globalActiveCred
   926  	if !owner {
   927  		var ok bool
   928  		creds, ok = GlobalIAMSys.GetUser(ctx, claims.AccessKey)
   929  		if !ok {
   930  			return toJSONError(ctx, errInvalidAccessKeyID)
   931  		}
   932  	}
   933  
   934  	if creds.SessionToken != "" {
   935  		// Use the same session token for URL token.
   936  		reply.Token = creds.SessionToken
   937  	} else {
   938  		token, err := authenticateURL(creds.AccessKey, creds.SecretKey)
   939  		if err != nil {
   940  			return toJSONError(ctx, err)
   941  		}
   942  		reply.Token = token
   943  	}
   944  
   945  	reply.UIVersion = Version
   946  	return nil
   947  }
   948  
   949  // Upload - file upload handler.
   950  func (web *webAPIHandlers) Upload(w http.ResponseWriter, r *http.Request) {
   951  	ctx := NewContext(r, w, "WebUpload")
   952  
   953  	// obtain the claims here if possible, for audit logging.
   954  	claims, owner, authErr := webRequestAuthenticate(r)
   955  
   956  	defer logger.AuditLog(ctx, w, r, claims.Map())
   957  
   958  	objectAPI := web.ObjectAPI()
   959  	if objectAPI == nil {
   960  		writeWebErrorResponse(w, errServerNotInitialized)
   961  		return
   962  	}
   963  
   964  	vars := mux.Vars(r)
   965  	bucket := vars["bucket"]
   966  	object, err := unescapePath(vars["object"])
   967  	if err != nil {
   968  		writeWebErrorResponse(w, err)
   969  		return
   970  	}
   971  
   972  	retPerms := ErrAccessDenied
   973  	holdPerms := ErrAccessDenied
   974  	replPerms := ErrAccessDenied
   975  	if authErr != nil {
   976  		if authErr == errNoAuthToken {
   977  			// Check if anonymous (non-owner) has access to upload objects.
   978  			if !globalPolicySys.IsAllowed(policy.Args{
   979  				Action:          policy.PutObjectAction,
   980  				BucketName:      bucket,
   981  				ConditionValues: getConditionValues(r, "", "", nil),
   982  				IsOwner:         false,
   983  				ObjectName:      object,
   984  			}) {
   985  				writeWebErrorResponse(w, errAuthentication)
   986  				return
   987  			}
   988  		} else {
   989  			writeWebErrorResponse(w, authErr)
   990  			return
   991  		}
   992  	}
   993  
   994  	// For authenticated users apply IAM policy.
   995  	if authErr == nil {
   996  		if !GlobalIAMSys.IsAllowed(iampolicy.Args{
   997  			AccountName:     claims.AccessKey,
   998  			Action:          iampolicy.PutObjectAction,
   999  			BucketName:      bucket,
  1000  			ConditionValues: getConditionValues(r, "", claims.AccessKey, claims.Map()),
  1001  			IsOwner:         owner,
  1002  			ObjectName:      object,
  1003  			Claims:          claims.Map(),
  1004  		}) {
  1005  			writeWebErrorResponse(w, errAuthentication)
  1006  			return
  1007  		}
  1008  		if GlobalIAMSys.IsAllowed(iampolicy.Args{
  1009  			AccountName:     claims.AccessKey,
  1010  			Action:          iampolicy.PutObjectRetentionAction,
  1011  			BucketName:      bucket,
  1012  			ConditionValues: getConditionValues(r, "", claims.AccessKey, claims.Map()),
  1013  			IsOwner:         owner,
  1014  			ObjectName:      object,
  1015  			Claims:          claims.Map(),
  1016  		}) {
  1017  			retPerms = ErrNone
  1018  		}
  1019  		if GlobalIAMSys.IsAllowed(iampolicy.Args{
  1020  			AccountName:     claims.AccessKey,
  1021  			Action:          iampolicy.PutObjectLegalHoldAction,
  1022  			BucketName:      bucket,
  1023  			ConditionValues: getConditionValues(r, "", claims.AccessKey, claims.Map()),
  1024  			IsOwner:         owner,
  1025  			ObjectName:      object,
  1026  			Claims:          claims.Map(),
  1027  		}) {
  1028  			holdPerms = ErrNone
  1029  		}
  1030  		if GlobalIAMSys.IsAllowed(iampolicy.Args{
  1031  			AccountName:     claims.AccessKey,
  1032  			Action:          iampolicy.GetReplicationConfigurationAction,
  1033  			BucketName:      bucket,
  1034  			ConditionValues: getConditionValues(r, "", claims.AccessKey, claims.Map()),
  1035  			IsOwner:         owner,
  1036  			ObjectName:      "",
  1037  			Claims:          claims.Map(),
  1038  		}) {
  1039  			replPerms = ErrNone
  1040  		}
  1041  	}
  1042  
  1043  	// Check if bucket is a reserved bucket name or invalid.
  1044  	if isReservedOrInvalidBucket(bucket, false) {
  1045  		writeWebErrorResponse(w, errInvalidBucketName)
  1046  		return
  1047  	}
  1048  
  1049  	// Check if bucket encryption is enabled
  1050  	_, err = globalBucketSSEConfigSys.Get(bucket)
  1051  	if (globalAutoEncryption || err == nil) && !crypto.SSEC.IsRequested(r.Header) {
  1052  		r.Header.Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES)
  1053  	}
  1054  
  1055  	// Require Content-Length to be set in the request
  1056  	size := r.ContentLength
  1057  	if size < 0 {
  1058  		writeWebErrorResponse(w, errSizeUnspecified)
  1059  		return
  1060  	}
  1061  
  1062  	if err := enforceBucketQuota(ctx, bucket, size); err != nil {
  1063  		writeWebErrorResponse(w, err)
  1064  		return
  1065  	}
  1066  
  1067  	// Extract incoming metadata if any.
  1068  	metadata, err := extractMetadata(ctx, r)
  1069  	if err != nil {
  1070  		WriteErrorResponse(ctx, w, ToAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
  1071  		return
  1072  	}
  1073  
  1074  	var pReader *PutObjReader
  1075  	var reader io.Reader = r.Body
  1076  	actualSize := size
  1077  
  1078  	hashReader, err := hash.NewReader(reader, size, "", "", actualSize)
  1079  	if err != nil {
  1080  		writeWebErrorResponse(w, err)
  1081  		return
  1082  	}
  1083  
  1084  	if objectAPI.IsCompressionSupported() && isCompressible(r.Header, object) && size > 0 {
  1085  		// Storing the compression metadata.
  1086  		metadata[ReservedMetadataPrefix+"compression"] = compressionAlgorithmV2
  1087  		metadata[ReservedMetadataPrefix+"actual-size"] = strconv.FormatInt(actualSize, 10)
  1088  
  1089  		actualReader, err := hash.NewReader(reader, actualSize, "", "", actualSize)
  1090  		if err != nil {
  1091  			writeWebErrorResponse(w, err)
  1092  			return
  1093  		}
  1094  
  1095  		// Set compression metrics.
  1096  		size = -1 // Since compressed size is un-predictable.
  1097  		s2c := newS2CompressReader(actualReader, actualSize)
  1098  		defer s2c.Close()
  1099  		reader = etag.Wrap(s2c, actualReader)
  1100  		hashReader, err = hash.NewReader(reader, size, "", "", actualSize)
  1101  		if err != nil {
  1102  			writeWebErrorResponse(w, err)
  1103  			return
  1104  		}
  1105  	}
  1106  
  1107  	mustReplicate, sync := mustReplicateWeb(ctx, r, bucket, object, metadata, "", replPerms)
  1108  	if mustReplicate {
  1109  		metadata[xhttp.AmzBucketReplicationStatus] = string(replication.Pending)
  1110  	}
  1111  	pReader = NewPutObjReader(hashReader)
  1112  	// get gateway encryption options
  1113  	opts, err := putOpts(ctx, r, bucket, object, metadata)
  1114  	if err != nil {
  1115  		writeErrorResponseHeadersOnly(w, ToAPIError(ctx, err))
  1116  		return
  1117  	}
  1118  
  1119  	if objectAPI.IsEncryptionSupported() {
  1120  		if _, ok := crypto.IsRequested(r.Header); ok && !HasSuffix(object, SlashSeparator) { // handle SSE requests
  1121  			var (
  1122  				objectEncryptionKey crypto.ObjectKey
  1123  				encReader           io.Reader
  1124  			)
  1125  			encReader, objectEncryptionKey, err = EncryptRequest(hashReader, r, bucket, object, metadata)
  1126  			if err != nil {
  1127  				WriteErrorResponse(ctx, w, ToAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
  1128  				return
  1129  			}
  1130  			info := ObjectInfo{Size: size}
  1131  			// do not try to verify encrypted content
  1132  			hashReader, err = hash.NewReader(etag.Wrap(encReader, hashReader), info.EncryptedSize(), "", "", size)
  1133  			if err != nil {
  1134  				WriteErrorResponse(ctx, w, ToAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
  1135  				return
  1136  			}
  1137  			pReader, err = pReader.WithEncryption(hashReader, &objectEncryptionKey)
  1138  			if err != nil {
  1139  				WriteErrorResponse(ctx, w, ToAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
  1140  				return
  1141  			}
  1142  		}
  1143  	}
  1144  
  1145  	// Ensure that metadata does not contain sensitive information
  1146  	crypto.RemoveSensitiveEntries(metadata)
  1147  
  1148  	putObject := objectAPI.PutObject
  1149  	getObjectInfo := objectAPI.GetObjectInfo
  1150  	if web.CacheAPI() != nil {
  1151  		putObject = web.CacheAPI().PutObject
  1152  		getObjectInfo = web.CacheAPI().GetObjectInfo
  1153  	}
  1154  
  1155  	// enforce object retention rules
  1156  	retentionMode, retentionDate, _, s3Err := checkPutObjectLockAllowed(ctx, r, bucket, object, getObjectInfo, retPerms, holdPerms)
  1157  	if s3Err != ErrNone {
  1158  		WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Err), r.URL, guessIsBrowserReq(r))
  1159  		return
  1160  	}
  1161  	if retentionMode != "" {
  1162  		opts.UserDefined[strings.ToLower(xhttp.AmzObjectLockMode)] = string(retentionMode)
  1163  		opts.UserDefined[strings.ToLower(xhttp.AmzObjectLockRetainUntilDate)] = retentionDate.UTC().Format(iso8601TimeFormat)
  1164  	}
  1165  
  1166  	objInfo, err := putObject(GlobalContext, bucket, object, pReader, opts)
  1167  	if err != nil {
  1168  		writeWebErrorResponse(w, err)
  1169  		return
  1170  	}
  1171  	if objectAPI.IsEncryptionSupported() {
  1172  		switch kind, _ := crypto.IsEncrypted(objInfo.UserDefined); kind {
  1173  		case crypto.S3:
  1174  			w.Header().Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES)
  1175  		case crypto.SSEC:
  1176  			w.Header().Set(xhttp.AmzServerSideEncryptionCustomerAlgorithm, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerAlgorithm))
  1177  			w.Header().Set(xhttp.AmzServerSideEncryptionCustomerKeyMD5, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerKeyMD5))
  1178  		}
  1179  	}
  1180  	if mustReplicate {
  1181  		scheduleReplication(ctx, objInfo.Clone(), objectAPI, sync, replication.ObjectReplicationType)
  1182  	}
  1183  
  1184  	reqParams := extractReqParams(r)
  1185  	reqParams["accessKey"] = claims.GetAccessKey()
  1186  
  1187  	// Notify object created event.
  1188  	sendEvent(eventArgs{
  1189  		EventName:    event.ObjectCreatedPut,
  1190  		BucketName:   bucket,
  1191  		Object:       objInfo,
  1192  		ReqParams:    reqParams,
  1193  		RespElements: extractRespElements(w),
  1194  		UserAgent:    r.UserAgent(),
  1195  		Host:         handlers.GetSourceIP(r),
  1196  	})
  1197  }
  1198  
  1199  // Download - file download handler.
  1200  func (web *webAPIHandlers) Download(w http.ResponseWriter, r *http.Request) {
  1201  	ctx := NewContext(r, w, "WebDownload")
  1202  
  1203  	claims, owner, authErr := webTokenAuthenticate(r.URL.Query().Get("token"))
  1204  	defer logger.AuditLog(ctx, w, r, claims.Map())
  1205  
  1206  	objectAPI := web.ObjectAPI()
  1207  	if objectAPI == nil {
  1208  		writeWebErrorResponse(w, errServerNotInitialized)
  1209  		return
  1210  	}
  1211  
  1212  	vars := mux.Vars(r)
  1213  
  1214  	bucket := vars["bucket"]
  1215  	object, err := unescapePath(vars["object"])
  1216  	if err != nil {
  1217  		writeWebErrorResponse(w, err)
  1218  		return
  1219  	}
  1220  
  1221  	getRetPerms := ErrAccessDenied
  1222  	legalHoldPerms := ErrAccessDenied
  1223  
  1224  	if authErr != nil {
  1225  		if authErr == errNoAuthToken {
  1226  			// Check if anonymous (non-owner) has access to download objects.
  1227  			if !globalPolicySys.IsAllowed(policy.Args{
  1228  				Action:          policy.GetObjectAction,
  1229  				BucketName:      bucket,
  1230  				ConditionValues: getConditionValues(r, "", "", nil),
  1231  				IsOwner:         false,
  1232  				ObjectName:      object,
  1233  			}) {
  1234  				writeWebErrorResponse(w, errAuthentication)
  1235  				return
  1236  			}
  1237  			if globalPolicySys.IsAllowed(policy.Args{
  1238  				Action:          policy.GetObjectRetentionAction,
  1239  				BucketName:      bucket,
  1240  				ConditionValues: getConditionValues(r, "", "", nil),
  1241  				IsOwner:         false,
  1242  				ObjectName:      object,
  1243  			}) {
  1244  				getRetPerms = ErrNone
  1245  			}
  1246  			if globalPolicySys.IsAllowed(policy.Args{
  1247  				Action:          policy.GetObjectLegalHoldAction,
  1248  				BucketName:      bucket,
  1249  				ConditionValues: getConditionValues(r, "", "", nil),
  1250  				IsOwner:         false,
  1251  				ObjectName:      object,
  1252  			}) {
  1253  				legalHoldPerms = ErrNone
  1254  			}
  1255  		} else {
  1256  			writeWebErrorResponse(w, authErr)
  1257  			return
  1258  		}
  1259  	}
  1260  
  1261  	// For authenticated users apply IAM policy.
  1262  	if authErr == nil {
  1263  		if !GlobalIAMSys.IsAllowed(iampolicy.Args{
  1264  			AccountName:     claims.AccessKey,
  1265  			Action:          iampolicy.GetObjectAction,
  1266  			BucketName:      bucket,
  1267  			ConditionValues: getConditionValues(r, "", claims.AccessKey, claims.Map()),
  1268  			IsOwner:         owner,
  1269  			ObjectName:      object,
  1270  			Claims:          claims.Map(),
  1271  		}) {
  1272  			writeWebErrorResponse(w, errAuthentication)
  1273  			return
  1274  		}
  1275  		if GlobalIAMSys.IsAllowed(iampolicy.Args{
  1276  			AccountName:     claims.AccessKey,
  1277  			Action:          iampolicy.GetObjectRetentionAction,
  1278  			BucketName:      bucket,
  1279  			ConditionValues: getConditionValues(r, "", claims.AccessKey, claims.Map()),
  1280  			IsOwner:         owner,
  1281  			ObjectName:      object,
  1282  			Claims:          claims.Map(),
  1283  		}) {
  1284  			getRetPerms = ErrNone
  1285  		}
  1286  		if GlobalIAMSys.IsAllowed(iampolicy.Args{
  1287  			AccountName:     claims.AccessKey,
  1288  			Action:          iampolicy.GetObjectLegalHoldAction,
  1289  			BucketName:      bucket,
  1290  			ConditionValues: getConditionValues(r, "", claims.AccessKey, claims.Map()),
  1291  			IsOwner:         owner,
  1292  			ObjectName:      object,
  1293  			Claims:          claims.Map(),
  1294  		}) {
  1295  			legalHoldPerms = ErrNone
  1296  		}
  1297  	}
  1298  
  1299  	// Check if bucket is a reserved bucket name or invalid.
  1300  	if isReservedOrInvalidBucket(bucket, false) {
  1301  		writeWebErrorResponse(w, errInvalidBucketName)
  1302  		return
  1303  	}
  1304  
  1305  	getObjectNInfo := objectAPI.GetObjectNInfo
  1306  	if web.CacheAPI() != nil {
  1307  		getObjectNInfo = web.CacheAPI().GetObjectNInfo
  1308  	}
  1309  
  1310  	var opts ObjectOptions
  1311  	gr, err := getObjectNInfo(ctx, bucket, object, nil, r.Header, readLock, opts)
  1312  	if err != nil {
  1313  		writeWebErrorResponse(w, err)
  1314  		return
  1315  	}
  1316  	defer gr.Close()
  1317  
  1318  	objInfo := gr.ObjInfo
  1319  
  1320  	// filter object lock metadata if permission does not permit
  1321  	objInfo.UserDefined = objectlock.FilterObjectLockMetadata(objInfo.UserDefined, getRetPerms != ErrNone, legalHoldPerms != ErrNone)
  1322  
  1323  	if objectAPI.IsEncryptionSupported() {
  1324  		if _, err = DecryptObjectInfo(&objInfo, r); err != nil {
  1325  			writeWebErrorResponse(w, err)
  1326  			return
  1327  		}
  1328  	}
  1329  
  1330  	// Set encryption response headers
  1331  	if objectAPI.IsEncryptionSupported() {
  1332  		switch kind, _ := crypto.IsEncrypted(objInfo.UserDefined); kind {
  1333  		case crypto.S3:
  1334  			w.Header().Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES)
  1335  		case crypto.SSEC:
  1336  			w.Header().Set(xhttp.AmzServerSideEncryptionCustomerAlgorithm, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerAlgorithm))
  1337  			w.Header().Set(xhttp.AmzServerSideEncryptionCustomerKeyMD5, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerKeyMD5))
  1338  		}
  1339  	}
  1340  
  1341  	// Set Parts Count Header
  1342  	if opts.PartNumber > 0 && len(objInfo.Parts) > 0 {
  1343  		setPartsCountHeaders(w, objInfo)
  1344  	}
  1345  
  1346  	if err = setObjectHeaders(w, objInfo, nil, opts); err != nil {
  1347  		writeWebErrorResponse(w, err)
  1348  		return
  1349  	}
  1350  
  1351  	// Add content disposition.
  1352  	w.Header().Set(xhttp.ContentDisposition, fmt.Sprintf("attachment; filename=\"%s\"", path.Base(objInfo.Name)))
  1353  
  1354  	setHeadGetRespHeaders(w, r.URL.Query())
  1355  
  1356  	httpWriter := ioutil.WriteOnClose(w)
  1357  
  1358  	// Write object content to response body
  1359  	if _, err = io.Copy(httpWriter, gr); err != nil {
  1360  		if !httpWriter.HasWritten() { // write error response only if no data or headers has been written to client yet
  1361  			writeWebErrorResponse(w, err)
  1362  		}
  1363  		return
  1364  	}
  1365  
  1366  	if err = httpWriter.Close(); err != nil {
  1367  		if !httpWriter.HasWritten() { // write error response only if no data or headers has been written to client yet
  1368  			writeWebErrorResponse(w, err)
  1369  			return
  1370  		}
  1371  	}
  1372  
  1373  	reqParams := extractReqParams(r)
  1374  	reqParams["accessKey"] = claims.GetAccessKey()
  1375  
  1376  	// Notify object accessed via a GET request.
  1377  	sendEvent(eventArgs{
  1378  		EventName:    event.ObjectAccessedGet,
  1379  		BucketName:   bucket,
  1380  		Object:       objInfo,
  1381  		ReqParams:    reqParams,
  1382  		RespElements: extractRespElements(w),
  1383  		UserAgent:    r.UserAgent(),
  1384  		Host:         handlers.GetSourceIP(r),
  1385  	})
  1386  }
  1387  
  1388  // DownloadZipArgs - Argument for downloading a bunch of files as a zip file.
  1389  // JSON will look like:
  1390  // '{"bucketname":"testbucket","prefix":"john/pics/","objects":["hawaii/","maldives/","sanjose.jpg"]}'
  1391  type DownloadZipArgs struct {
  1392  	Objects    []string `json:"objects"`    // can be files or sub-directories
  1393  	Prefix     string   `json:"prefix"`     // current directory in the browser-ui
  1394  	BucketName string   `json:"bucketname"` // bucket name.
  1395  }
  1396  
  1397  // Takes a list of objects and creates a zip file that sent as the response body.
  1398  func (web *webAPIHandlers) DownloadZip(w http.ResponseWriter, r *http.Request) {
  1399  	host := handlers.GetSourceIP(r)
  1400  
  1401  	claims, owner, authErr := webTokenAuthenticate(r.URL.Query().Get("token"))
  1402  
  1403  	ctx := NewContext(r, w, "WebDownloadZip")
  1404  	defer logger.AuditLog(ctx, w, r, claims.Map())
  1405  
  1406  	objectAPI := web.ObjectAPI()
  1407  	if objectAPI == nil {
  1408  		writeWebErrorResponse(w, errServerNotInitialized)
  1409  		return
  1410  	}
  1411  
  1412  	// Auth is done after reading the body to accommodate for anonymous requests
  1413  	// when bucket policy is enabled.
  1414  	var args DownloadZipArgs
  1415  	tenKB := 10 * 1024 // To limit r.Body to take care of misbehaving anonymous client.
  1416  	decodeErr := json.NewDecoder(io.LimitReader(r.Body, int64(tenKB))).Decode(&args)
  1417  	if decodeErr != nil {
  1418  		writeWebErrorResponse(w, decodeErr)
  1419  		return
  1420  	}
  1421  
  1422  	var getRetPerms []APIErrorCode
  1423  	var legalHoldPerms []APIErrorCode
  1424  
  1425  	if authErr != nil {
  1426  		if authErr == errNoAuthToken {
  1427  			for _, object := range args.Objects {
  1428  				// Check if anonymous (non-owner) has access to download objects.
  1429  				if !globalPolicySys.IsAllowed(policy.Args{
  1430  					Action:          policy.GetObjectAction,
  1431  					BucketName:      args.BucketName,
  1432  					ConditionValues: getConditionValues(r, "", "", nil),
  1433  					IsOwner:         false,
  1434  					ObjectName:      pathJoin(args.Prefix, object),
  1435  				}) {
  1436  					writeWebErrorResponse(w, errAuthentication)
  1437  					return
  1438  				}
  1439  				retentionPerm := ErrAccessDenied
  1440  				if globalPolicySys.IsAllowed(policy.Args{
  1441  					Action:          policy.GetObjectRetentionAction,
  1442  					BucketName:      args.BucketName,
  1443  					ConditionValues: getConditionValues(r, "", "", nil),
  1444  					IsOwner:         false,
  1445  					ObjectName:      pathJoin(args.Prefix, object),
  1446  				}) {
  1447  					retentionPerm = ErrNone
  1448  				}
  1449  				getRetPerms = append(getRetPerms, retentionPerm)
  1450  
  1451  				legalHoldPerm := ErrAccessDenied
  1452  				if globalPolicySys.IsAllowed(policy.Args{
  1453  					Action:          policy.GetObjectLegalHoldAction,
  1454  					BucketName:      args.BucketName,
  1455  					ConditionValues: getConditionValues(r, "", "", nil),
  1456  					IsOwner:         false,
  1457  					ObjectName:      pathJoin(args.Prefix, object),
  1458  				}) {
  1459  					legalHoldPerm = ErrNone
  1460  				}
  1461  				legalHoldPerms = append(legalHoldPerms, legalHoldPerm)
  1462  			}
  1463  		} else {
  1464  			writeWebErrorResponse(w, authErr)
  1465  			return
  1466  		}
  1467  	}
  1468  
  1469  	// For authenticated users apply IAM policy.
  1470  	if authErr == nil {
  1471  		for _, object := range args.Objects {
  1472  			if !GlobalIAMSys.IsAllowed(iampolicy.Args{
  1473  				AccountName:     claims.AccessKey,
  1474  				Action:          iampolicy.GetObjectAction,
  1475  				BucketName:      args.BucketName,
  1476  				ConditionValues: getConditionValues(r, "", claims.AccessKey, claims.Map()),
  1477  				IsOwner:         owner,
  1478  				ObjectName:      pathJoin(args.Prefix, object),
  1479  				Claims:          claims.Map(),
  1480  			}) {
  1481  				writeWebErrorResponse(w, errAuthentication)
  1482  				return
  1483  			}
  1484  			retentionPerm := ErrAccessDenied
  1485  			if GlobalIAMSys.IsAllowed(iampolicy.Args{
  1486  				AccountName:     claims.AccessKey,
  1487  				Action:          iampolicy.GetObjectRetentionAction,
  1488  				BucketName:      args.BucketName,
  1489  				ConditionValues: getConditionValues(r, "", claims.AccessKey, claims.Map()),
  1490  				IsOwner:         owner,
  1491  				ObjectName:      pathJoin(args.Prefix, object),
  1492  				Claims:          claims.Map(),
  1493  			}) {
  1494  				retentionPerm = ErrNone
  1495  			}
  1496  			getRetPerms = append(getRetPerms, retentionPerm)
  1497  
  1498  			legalHoldPerm := ErrAccessDenied
  1499  			if GlobalIAMSys.IsAllowed(iampolicy.Args{
  1500  				AccountName:     claims.AccessKey,
  1501  				Action:          iampolicy.GetObjectLegalHoldAction,
  1502  				BucketName:      args.BucketName,
  1503  				ConditionValues: getConditionValues(r, "", claims.AccessKey, claims.Map()),
  1504  				IsOwner:         owner,
  1505  				ObjectName:      pathJoin(args.Prefix, object),
  1506  				Claims:          claims.Map(),
  1507  			}) {
  1508  				legalHoldPerm = ErrNone
  1509  			}
  1510  			legalHoldPerms = append(legalHoldPerms, legalHoldPerm)
  1511  		}
  1512  	}
  1513  
  1514  	// Check if bucket is a reserved bucket name or invalid.
  1515  	if isReservedOrInvalidBucket(args.BucketName, false) {
  1516  		writeWebErrorResponse(w, errInvalidBucketName)
  1517  		return
  1518  	}
  1519  
  1520  	getObjectNInfo := objectAPI.GetObjectNInfo
  1521  	if web.CacheAPI() != nil {
  1522  		getObjectNInfo = web.CacheAPI().GetObjectNInfo
  1523  	}
  1524  
  1525  	archive := zip.NewWriter(w)
  1526  	defer archive.Close()
  1527  
  1528  	reqParams := extractReqParams(r)
  1529  	reqParams["accessKey"] = claims.GetAccessKey()
  1530  	respElements := extractRespElements(w)
  1531  
  1532  	for i, object := range args.Objects {
  1533  		if contextCanceled(ctx) {
  1534  			return
  1535  		}
  1536  		// Writes compressed object file to the response.
  1537  		zipit := func(objectName string) error {
  1538  			var opts ObjectOptions
  1539  			gr, err := getObjectNInfo(ctx, args.BucketName, objectName, nil, r.Header, readLock, opts)
  1540  			if err != nil {
  1541  				return err
  1542  			}
  1543  			defer gr.Close()
  1544  
  1545  			info := gr.ObjInfo
  1546  			// filter object lock metadata if permission does not permit
  1547  			info.UserDefined = objectlock.FilterObjectLockMetadata(info.UserDefined, getRetPerms[i] != ErrNone, legalHoldPerms[i] != ErrNone)
  1548  			// For reporting, set the file size to the uncompressed size.
  1549  			info.Size, err = info.GetActualSize()
  1550  			if err != nil {
  1551  				return err
  1552  			}
  1553  			header := &zip.FileHeader{
  1554  				Name:               strings.TrimPrefix(objectName, args.Prefix),
  1555  				Method:             zip.Deflate,
  1556  				Flags:              1 << 11,
  1557  				Modified:           info.ModTime,
  1558  				UncompressedSize64: uint64(info.Size),
  1559  			}
  1560  			if info.Size < 20 || hasStringSuffixInSlice(info.Name, standardExcludeCompressExtensions) || hasPattern(standardExcludeCompressContentTypes, info.ContentType) {
  1561  				// We strictly disable compression for standard extensions/content-types.
  1562  				header.Method = zip.Store
  1563  			}
  1564  			writer, err := archive.CreateHeader(header)
  1565  			if err != nil {
  1566  				return err
  1567  			}
  1568  
  1569  			// Write object content to response body
  1570  			if _, err = io.Copy(writer, gr); err != nil {
  1571  				return err
  1572  			}
  1573  
  1574  			// Notify object accessed via a GET request.
  1575  			sendEvent(eventArgs{
  1576  				EventName:    event.ObjectAccessedGet,
  1577  				BucketName:   args.BucketName,
  1578  				Object:       info,
  1579  				ReqParams:    reqParams,
  1580  				RespElements: respElements,
  1581  				UserAgent:    r.UserAgent(),
  1582  				Host:         host,
  1583  			})
  1584  
  1585  			return nil
  1586  		}
  1587  
  1588  		if !HasSuffix(object, SlashSeparator) {
  1589  			// If not a directory, compress the file and write it to response.
  1590  			err := zipit(pathJoin(args.Prefix, object))
  1591  			if err != nil {
  1592  				logger.LogIf(ctx, err)
  1593  				return
  1594  			}
  1595  			continue
  1596  		}
  1597  
  1598  		objInfoCh := make(chan ObjectInfo)
  1599  
  1600  		// Walk through all objects
  1601  		if err := objectAPI.Walk(ctx, args.BucketName, pathJoin(args.Prefix, object), objInfoCh, ObjectOptions{}); err != nil {
  1602  			logger.LogIf(ctx, err)
  1603  			continue
  1604  		}
  1605  
  1606  		for obj := range objInfoCh {
  1607  			if err := zipit(obj.Name); err != nil {
  1608  				logger.LogIf(ctx, err)
  1609  				continue
  1610  			}
  1611  		}
  1612  	}
  1613  }
  1614  
  1615  // GetBucketPolicyArgs - get bucket policy args.
  1616  type GetBucketPolicyArgs struct {
  1617  	BucketName string `json:"bucketName"`
  1618  	Prefix     string `json:"prefix"`
  1619  }
  1620  
  1621  // GetBucketPolicyRep - get bucket policy reply.
  1622  type GetBucketPolicyRep struct {
  1623  	UIVersion string                     `json:"uiVersion"`
  1624  	Policy    miniogopolicy.BucketPolicy `json:"policy"`
  1625  }
  1626  
  1627  // GetBucketPolicy - get bucket policy for the requested prefix.
  1628  func (web *webAPIHandlers) GetBucketPolicy(r *http.Request, args *GetBucketPolicyArgs, reply *GetBucketPolicyRep) error {
  1629  	ctx := newWebContext(r, args, "WebGetBucketPolicy")
  1630  
  1631  	objectAPI := web.ObjectAPI()
  1632  	if objectAPI == nil {
  1633  		return toJSONError(ctx, errServerNotInitialized)
  1634  	}
  1635  
  1636  	claims, owner, authErr := webRequestAuthenticate(r)
  1637  	if authErr != nil {
  1638  		return toJSONError(ctx, authErr)
  1639  	}
  1640  
  1641  	// For authenticated users apply IAM policy.
  1642  	if !GlobalIAMSys.IsAllowed(iampolicy.Args{
  1643  		AccountName:     claims.AccessKey,
  1644  		Action:          iampolicy.GetBucketPolicyAction,
  1645  		BucketName:      args.BucketName,
  1646  		ConditionValues: getConditionValues(r, "", claims.AccessKey, claims.Map()),
  1647  		IsOwner:         owner,
  1648  		Claims:          claims.Map(),
  1649  	}) {
  1650  		return toJSONError(ctx, errAccessDenied)
  1651  	}
  1652  
  1653  	// Check if bucket is a reserved bucket name or invalid.
  1654  	if isReservedOrInvalidBucket(args.BucketName, false) {
  1655  		return toJSONError(ctx, errInvalidBucketName, args.BucketName)
  1656  	}
  1657  
  1658  	var policyInfo = &miniogopolicy.BucketAccessPolicy{Version: "2012-10-17"}
  1659  	bucketPolicy, err := globalPolicySys.Get(args.BucketName)
  1660  	if err != nil {
  1661  		if _, ok := err.(BucketPolicyNotFound); !ok {
  1662  			return toJSONError(ctx, err, args.BucketName)
  1663  		}
  1664  	}
  1665  
  1666  	policyInfo, err = PolicyToBucketAccessPolicy(bucketPolicy)
  1667  	if err != nil {
  1668  		// This should not happen.
  1669  		return toJSONError(ctx, err, args.BucketName)
  1670  	}
  1671  
  1672  	reply.UIVersion = Version
  1673  	reply.Policy = miniogopolicy.GetPolicy(policyInfo.Statements, args.BucketName, args.Prefix)
  1674  
  1675  	return nil
  1676  }
  1677  
  1678  // ListAllBucketPoliciesArgs - get all bucket policies.
  1679  type ListAllBucketPoliciesArgs struct {
  1680  	BucketName string `json:"bucketName"`
  1681  }
  1682  
  1683  // BucketAccessPolicy - Collection of canned bucket policy at a given prefix.
  1684  type BucketAccessPolicy struct {
  1685  	Bucket string                     `json:"bucket"`
  1686  	Prefix string                     `json:"prefix"`
  1687  	Policy miniogopolicy.BucketPolicy `json:"policy"`
  1688  }
  1689  
  1690  // ListAllBucketPoliciesRep - get all bucket policy reply.
  1691  type ListAllBucketPoliciesRep struct {
  1692  	UIVersion string               `json:"uiVersion"`
  1693  	Policies  []BucketAccessPolicy `json:"policies"`
  1694  }
  1695  
  1696  // ListAllBucketPolicies - get all bucket policy.
  1697  func (web *webAPIHandlers) ListAllBucketPolicies(r *http.Request, args *ListAllBucketPoliciesArgs, reply *ListAllBucketPoliciesRep) error {
  1698  	ctx := newWebContext(r, args, "WebListAllBucketPolicies")
  1699  	objectAPI := web.ObjectAPI()
  1700  	if objectAPI == nil {
  1701  		return toJSONError(ctx, errServerNotInitialized)
  1702  	}
  1703  
  1704  	claims, owner, authErr := webRequestAuthenticate(r)
  1705  	if authErr != nil {
  1706  		return toJSONError(ctx, authErr)
  1707  	}
  1708  
  1709  	// For authenticated users apply IAM policy.
  1710  	if !GlobalIAMSys.IsAllowed(iampolicy.Args{
  1711  		AccountName:     claims.AccessKey,
  1712  		Action:          iampolicy.GetBucketPolicyAction,
  1713  		BucketName:      args.BucketName,
  1714  		ConditionValues: getConditionValues(r, "", claims.AccessKey, claims.Map()),
  1715  		IsOwner:         owner,
  1716  		Claims:          claims.Map(),
  1717  	}) {
  1718  		return toJSONError(ctx, errAccessDenied)
  1719  	}
  1720  
  1721  	// Check if bucket is a reserved bucket name or invalid.
  1722  	if isReservedOrInvalidBucket(args.BucketName, false) {
  1723  		return toJSONError(ctx, errInvalidBucketName, args.BucketName)
  1724  	}
  1725  
  1726  	var policyInfo = new(miniogopolicy.BucketAccessPolicy)
  1727  	bucketPolicy, err := globalPolicySys.Get(args.BucketName)
  1728  	if err != nil {
  1729  		if _, ok := err.(BucketPolicyNotFound); !ok {
  1730  			return toJSONError(ctx, err, args.BucketName)
  1731  		}
  1732  	}
  1733  	policyInfo, err = PolicyToBucketAccessPolicy(bucketPolicy)
  1734  	if err != nil {
  1735  		return toJSONError(ctx, err, args.BucketName)
  1736  	}
  1737  
  1738  	reply.UIVersion = Version
  1739  	for prefix, policy := range miniogopolicy.GetPolicies(policyInfo.Statements, args.BucketName, "") {
  1740  		bucketName, objectPrefix := path2BucketObject(prefix)
  1741  		objectPrefix = strings.TrimSuffix(objectPrefix, "*")
  1742  		reply.Policies = append(reply.Policies, BucketAccessPolicy{
  1743  			Bucket: bucketName,
  1744  			Prefix: objectPrefix,
  1745  			Policy: policy,
  1746  		})
  1747  	}
  1748  
  1749  	return nil
  1750  }
  1751  
  1752  // SetBucketPolicyWebArgs - set bucket policy args.
  1753  type SetBucketPolicyWebArgs struct {
  1754  	BucketName string `json:"bucketName"`
  1755  	Prefix     string `json:"prefix"`
  1756  	Policy     string `json:"policy"`
  1757  }
  1758  
  1759  // SetBucketPolicy - set bucket policy.
  1760  func (web *webAPIHandlers) SetBucketPolicy(r *http.Request, args *SetBucketPolicyWebArgs, reply *WebGenericRep) error {
  1761  	ctx := newWebContext(r, args, "WebSetBucketPolicy")
  1762  	objectAPI := web.ObjectAPI()
  1763  	reply.UIVersion = Version
  1764  
  1765  	if objectAPI == nil {
  1766  		return toJSONError(ctx, errServerNotInitialized)
  1767  	}
  1768  
  1769  	claims, owner, authErr := webRequestAuthenticate(r)
  1770  	if authErr != nil {
  1771  		return toJSONError(ctx, authErr)
  1772  	}
  1773  
  1774  	// For authenticated users apply IAM policy.
  1775  	if !GlobalIAMSys.IsAllowed(iampolicy.Args{
  1776  		AccountName:     claims.AccessKey,
  1777  		Action:          iampolicy.PutBucketPolicyAction,
  1778  		BucketName:      args.BucketName,
  1779  		ConditionValues: getConditionValues(r, "", claims.AccessKey, claims.Map()),
  1780  		IsOwner:         owner,
  1781  		Claims:          claims.Map(),
  1782  	}) {
  1783  		return toJSONError(ctx, errAccessDenied)
  1784  	}
  1785  
  1786  	// Check if bucket is a reserved bucket name or invalid.
  1787  	if isReservedOrInvalidBucket(args.BucketName, false) {
  1788  		return toJSONError(ctx, errInvalidBucketName, args.BucketName)
  1789  	}
  1790  
  1791  	policyType := miniogopolicy.BucketPolicy(args.Policy)
  1792  	if !policyType.IsValidBucketPolicy() {
  1793  		return &json2.Error{
  1794  			Message: "Invalid policy type " + args.Policy,
  1795  		}
  1796  	}
  1797  
  1798  	bucketPolicy, err := globalPolicySys.Get(args.BucketName)
  1799  	if err != nil {
  1800  		if _, ok := err.(BucketPolicyNotFound); !ok {
  1801  			return toJSONError(ctx, err, args.BucketName)
  1802  		}
  1803  	}
  1804  	policyInfo, err := PolicyToBucketAccessPolicy(bucketPolicy)
  1805  	if err != nil {
  1806  		// This should not happen.
  1807  		return toJSONError(ctx, err, args.BucketName)
  1808  	}
  1809  
  1810  	policyInfo.Statements = miniogopolicy.SetPolicy(policyInfo.Statements, policyType, args.BucketName, args.Prefix)
  1811  	if len(policyInfo.Statements) == 0 {
  1812  		if err = globalBucketMetadataSys.Update(args.BucketName, bucketPolicyConfig, nil); err != nil {
  1813  			return toJSONError(ctx, err, args.BucketName)
  1814  		}
  1815  
  1816  		return nil
  1817  	}
  1818  
  1819  	bucketPolicy, err = BucketAccessPolicyToPolicy(policyInfo)
  1820  	if err != nil {
  1821  		// This should not happen.
  1822  		return toJSONError(ctx, err, args.BucketName)
  1823  	}
  1824  
  1825  	configData, err := json.Marshal(bucketPolicy)
  1826  	if err != nil {
  1827  		return toJSONError(ctx, err, args.BucketName)
  1828  	}
  1829  
  1830  	// Parse validate and save bucket policy.
  1831  	if err = globalBucketMetadataSys.Update(args.BucketName, bucketPolicyConfig, configData); err != nil {
  1832  		return toJSONError(ctx, err, args.BucketName)
  1833  	}
  1834  
  1835  	return nil
  1836  }
  1837  
  1838  // PresignedGetArgs - presigned-get API args.
  1839  type PresignedGetArgs struct {
  1840  	// Host header required for signed headers.
  1841  	HostName string `json:"host"`
  1842  
  1843  	// Bucket name of the object to be presigned.
  1844  	BucketName string `json:"bucket"`
  1845  
  1846  	// Object name to be presigned.
  1847  	ObjectName string `json:"object"`
  1848  
  1849  	// Expiry in seconds.
  1850  	Expiry int64 `json:"expiry"`
  1851  }
  1852  
  1853  // PresignedGetRep - presigned-get URL reply.
  1854  type PresignedGetRep struct {
  1855  	UIVersion string `json:"uiVersion"`
  1856  	// Presigned URL of the object.
  1857  	URL string `json:"url"`
  1858  }
  1859  
  1860  // PresignedGET - returns presigned-Get url.
  1861  func (web *webAPIHandlers) PresignedGet(r *http.Request, args *PresignedGetArgs, reply *PresignedGetRep) error {
  1862  	ctx := newWebContext(r, args, "WebPresignedGet")
  1863  	claims, owner, authErr := webRequestAuthenticate(r)
  1864  	if authErr != nil {
  1865  		return toJSONError(ctx, authErr)
  1866  	}
  1867  	var creds auth.Credentials
  1868  	if !owner {
  1869  		var ok bool
  1870  		creds, ok = GlobalIAMSys.GetUser(ctx, claims.AccessKey)
  1871  		if !ok {
  1872  			return toJSONError(ctx, errInvalidAccessKeyID)
  1873  		}
  1874  	} else {
  1875  		creds = globalActiveCred
  1876  	}
  1877  
  1878  	region := globalServerRegion
  1879  	if args.BucketName == "" || args.ObjectName == "" {
  1880  		return &json2.Error{
  1881  			Message: "Bucket and Object are mandatory arguments.",
  1882  		}
  1883  	}
  1884  
  1885  	// Check if bucket is a reserved bucket name or invalid.
  1886  	if isReservedOrInvalidBucket(args.BucketName, false) {
  1887  		return toJSONError(ctx, errInvalidBucketName, args.BucketName)
  1888  	}
  1889  
  1890  	// Check if the user indeed has GetObject access,
  1891  	// if not we do not need to generate presigned URLs
  1892  	if !GlobalIAMSys.IsAllowed(iampolicy.Args{
  1893  		AccountName:     claims.AccessKey,
  1894  		Action:          iampolicy.GetObjectAction,
  1895  		BucketName:      args.BucketName,
  1896  		ConditionValues: getConditionValues(r, "", claims.AccessKey, claims.Map()),
  1897  		IsOwner:         owner,
  1898  		ObjectName:      args.ObjectName,
  1899  		Claims:          claims.Map(),
  1900  	}) {
  1901  		return toJSONError(ctx, errPresignedNotAllowed)
  1902  	}
  1903  
  1904  	reply.UIVersion = Version
  1905  	reply.URL = presignedGet(args.HostName, args.BucketName, args.ObjectName, args.Expiry, creds, region)
  1906  	return nil
  1907  }
  1908  
  1909  // Returns presigned url for GET method.
  1910  func presignedGet(host, bucket, object string, expiry int64, creds auth.Credentials, region string) string {
  1911  	accessKey := creds.AccessKey
  1912  	secretKey := creds.SecretKey
  1913  	sessionToken := creds.SessionToken
  1914  
  1915  	date := UTCNow()
  1916  	dateStr := date.Format(iso8601Format)
  1917  	credential := fmt.Sprintf("%s/%s", accessKey, getScope(date, region))
  1918  
  1919  	var expiryStr = "604800" // Default set to be expire in 7days.
  1920  	if expiry < 604800 && expiry > 0 {
  1921  		expiryStr = strconv.FormatInt(expiry, 10)
  1922  	}
  1923  
  1924  	query := url.Values{}
  1925  	query.Set(xhttp.AmzAlgorithm, signV4Algorithm)
  1926  	query.Set(xhttp.AmzCredential, credential)
  1927  	query.Set(xhttp.AmzDate, dateStr)
  1928  	query.Set(xhttp.AmzExpires, expiryStr)
  1929  	query.Set(xhttp.ContentDisposition, fmt.Sprintf("attachment; filename=\"%s\"", object))
  1930  	// Set session token if available.
  1931  	if sessionToken != "" {
  1932  		query.Set(xhttp.AmzSecurityToken, sessionToken)
  1933  	}
  1934  	query.Set(xhttp.AmzSignedHeaders, "host")
  1935  	queryStr := s3utils.QueryEncode(query)
  1936  
  1937  	path := SlashSeparator + path.Join(bucket, object)
  1938  
  1939  	// "host" is the only header required to be signed for Presigned URLs.
  1940  	extractedSignedHeaders := make(http.Header)
  1941  	extractedSignedHeaders.Set("host", host)
  1942  	canonicalRequest := getCanonicalRequest(extractedSignedHeaders, unsignedPayload, queryStr, path, http.MethodGet)
  1943  	stringToSign := getStringToSign(canonicalRequest, date, getScope(date, region))
  1944  	signingKey := getSigningKey(secretKey, date, region, serviceS3)
  1945  	signature := getSignature(signingKey, stringToSign)
  1946  
  1947  	return host + s3utils.EncodePath(path) + "?" + queryStr + "&" + xhttp.AmzSignature + "=" + signature
  1948  }
  1949  
  1950  // DiscoveryDocResp - OpenID discovery document reply.
  1951  type DiscoveryDocResp struct {
  1952  	DiscoveryDoc openid.DiscoveryDoc
  1953  	UIVersion    string `json:"uiVersion"`
  1954  	ClientID     string `json:"clientId"`
  1955  }
  1956  
  1957  // GetDiscoveryDoc - returns parsed value of OpenID discovery document
  1958  func (web *webAPIHandlers) GetDiscoveryDoc(r *http.Request, args *WebGenericArgs, reply *DiscoveryDocResp) error {
  1959  	if globalOpenIDConfig.DiscoveryDoc.AuthEndpoint != "" {
  1960  		reply.DiscoveryDoc = globalOpenIDConfig.DiscoveryDoc
  1961  		reply.ClientID = globalOpenIDConfig.ClientID
  1962  	}
  1963  	reply.UIVersion = Version
  1964  	return nil
  1965  }
  1966  
  1967  // LoginSTSArgs - login arguments.
  1968  type LoginSTSArgs struct {
  1969  	Token string `json:"token" form:"token"`
  1970  }
  1971  
  1972  var errSTSNotInitialized = errors.New("STS API not initialized, please configure STS support")
  1973  
  1974  // LoginSTS - STS user login handler.
  1975  func (web *webAPIHandlers) LoginSTS(r *http.Request, args *LoginSTSArgs, reply *LoginRep) error {
  1976  	ctx := newWebContext(r, args, "WebLoginSTS")
  1977  
  1978  	if globalOpenIDValidators == nil {
  1979  		return toJSONError(ctx, errSTSNotInitialized)
  1980  	}
  1981  
  1982  	v, err := globalOpenIDValidators.Get("jwt")
  1983  	if err != nil {
  1984  		logger.LogIf(ctx, err)
  1985  		return toJSONError(ctx, errSTSNotInitialized)
  1986  	}
  1987  
  1988  	m, err := v.Validate(args.Token, "")
  1989  	if err != nil {
  1990  		return toJSONError(ctx, err)
  1991  	}
  1992  
  1993  	// JWT has requested a custom claim with policy value set.
  1994  	// This is a MinIO STS API specific value, this value should
  1995  	// be set and configured on your identity provider as part of
  1996  	// JWT custom claims.
  1997  	var policyName string
  1998  	policySet, ok := iampolicy.GetPoliciesFromClaims(m, iamPolicyClaimNameOpenID())
  1999  	if ok {
  2000  		policyName = GlobalIAMSys.CurrentPolicies(strings.Join(policySet.ToSlice(), ","))
  2001  	}
  2002  	if policyName == "" && GlobalPolicyOPA == nil {
  2003  		return toJSONError(ctx, fmt.Errorf("%s claim missing from the JWT token, credentials will not be generated", iamPolicyClaimNameOpenID()))
  2004  	}
  2005  	m[iamPolicyClaimNameOpenID()] = policyName
  2006  
  2007  	secret := globalActiveCred.SecretKey
  2008  	cred, err := auth.GetNewCredentialsWithMetadata(m, secret)
  2009  	if err != nil {
  2010  		return toJSONError(ctx, err)
  2011  	}
  2012  
  2013  	// Set the newly generated credentials.
  2014  	if err = GlobalIAMSys.SetTempUser(cred.AccessKey, cred, policyName); err != nil {
  2015  		return toJSONError(ctx, err)
  2016  	}
  2017  
  2018  	// Notify all other MinIO peers to reload temp users
  2019  	for _, nerr := range GlobalNotificationSys.LoadUser(cred.AccessKey, true) {
  2020  		if nerr.Err != nil {
  2021  			logger.GetReqInfo(ctx).SetTags("peerAddress", nerr.Host.String())
  2022  			logger.LogIf(ctx, nerr.Err)
  2023  		}
  2024  	}
  2025  
  2026  	reply.Token = cred.SessionToken
  2027  	reply.UIVersion = Version
  2028  	return nil
  2029  }
  2030  
  2031  // toJSONError converts regular errors into more user friendly
  2032  // and consumable error message for the browser UI.
  2033  func toJSONError(ctx context.Context, err error, params ...string) (jerr *json2.Error) {
  2034  	apiErr := toWebAPIError(ctx, err)
  2035  	jerr = &json2.Error{
  2036  		Message: apiErr.Description,
  2037  	}
  2038  	switch apiErr.Code {
  2039  	// Reserved bucket name provided.
  2040  	case "AllAccessDisabled":
  2041  		if len(params) > 0 {
  2042  			jerr = &json2.Error{
  2043  				Message: fmt.Sprintf("All access to this bucket %s has been disabled.", params[0]),
  2044  			}
  2045  		}
  2046  	// Bucket name invalid with custom error message.
  2047  	case "InvalidBucketName":
  2048  		if len(params) > 0 {
  2049  			jerr = &json2.Error{
  2050  				Message: fmt.Sprintf("Bucket Name %s is invalid. Lowercase letters, period, hyphen, numerals are the only allowed characters and should be minimum 3 characters in length.", params[0]),
  2051  			}
  2052  		}
  2053  	// Bucket not found custom error message.
  2054  	case "NoSuchBucket":
  2055  		if len(params) > 0 {
  2056  			jerr = &json2.Error{
  2057  				Message: fmt.Sprintf("The specified bucket %s does not exist.", params[0]),
  2058  			}
  2059  		}
  2060  	// Object not found custom error message.
  2061  	case "NoSuchKey":
  2062  		if len(params) > 1 {
  2063  			jerr = &json2.Error{
  2064  				Message: fmt.Sprintf("The specified key %s does not exist", params[1]),
  2065  			}
  2066  		}
  2067  		// Add more custom error messages here with more context.
  2068  	}
  2069  	return jerr
  2070  }
  2071  
  2072  // toWebAPIError - convert into error into APIError.
  2073  func toWebAPIError(ctx context.Context, err error) APIError {
  2074  	switch err {
  2075  	case errNoAuthToken:
  2076  		return APIError{
  2077  			Code:           "WebTokenMissing",
  2078  			HTTPStatusCode: http.StatusBadRequest,
  2079  			Description:    err.Error(),
  2080  		}
  2081  	case errSTSNotInitialized:
  2082  		return APIError(stsErrCodes.ToSTSErr(ErrSTSNotInitialized))
  2083  	case errServerNotInitialized:
  2084  		return APIError{
  2085  			Code:           "XMinioServerNotInitialized",
  2086  			HTTPStatusCode: http.StatusServiceUnavailable,
  2087  			Description:    err.Error(),
  2088  		}
  2089  	case errAuthentication, auth.ErrInvalidAccessKeyLength,
  2090  		auth.ErrInvalidSecretKeyLength, errInvalidAccessKeyID, errAccessDenied, errLockedObject:
  2091  		return APIError{
  2092  			Code:           "AccessDenied",
  2093  			HTTPStatusCode: http.StatusForbidden,
  2094  			Description:    err.Error(),
  2095  		}
  2096  	case errSizeUnspecified:
  2097  		return APIError{
  2098  			Code:           "InvalidRequest",
  2099  			HTTPStatusCode: http.StatusBadRequest,
  2100  			Description:    err.Error(),
  2101  		}
  2102  	case errChangeCredNotAllowed:
  2103  		return APIError{
  2104  			Code:           "MethodNotAllowed",
  2105  			HTTPStatusCode: http.StatusMethodNotAllowed,
  2106  			Description:    err.Error(),
  2107  		}
  2108  	case errInvalidBucketName:
  2109  		return APIError{
  2110  			Code:           "InvalidBucketName",
  2111  			HTTPStatusCode: http.StatusBadRequest,
  2112  			Description:    err.Error(),
  2113  		}
  2114  	case errInvalidArgument:
  2115  		return APIError{
  2116  			Code:           "InvalidArgument",
  2117  			HTTPStatusCode: http.StatusBadRequest,
  2118  			Description:    err.Error(),
  2119  		}
  2120  	case errEncryptedObject:
  2121  		return GetAPIError(ErrSSEEncryptedObject)
  2122  	case errInvalidEncryptionParameters:
  2123  		return GetAPIError(ErrInvalidEncryptionParameters)
  2124  	case errObjectTampered:
  2125  		return GetAPIError(ErrObjectTampered)
  2126  	case errMethodNotAllowed:
  2127  		return GetAPIError(ErrMethodNotAllowed)
  2128  	}
  2129  
  2130  	// Convert error type to api error code.
  2131  	switch err.(type) {
  2132  	case StorageFull:
  2133  		return GetAPIError(ErrStorageFull)
  2134  	case BucketQuotaExceeded:
  2135  		return GetAPIError(ErrAdminBucketQuotaExceeded)
  2136  	case BucketNotFound:
  2137  		return GetAPIError(ErrNoSuchBucket)
  2138  	case BucketNotEmpty:
  2139  		return GetAPIError(ErrBucketNotEmpty)
  2140  	case BucketExists:
  2141  		return GetAPIError(ErrBucketAlreadyOwnedByYou)
  2142  	case BucketNameInvalid:
  2143  		return GetAPIError(ErrInvalidBucketName)
  2144  	case hash.BadDigest:
  2145  		return GetAPIError(ErrBadDigest)
  2146  	case IncompleteBody:
  2147  		return GetAPIError(ErrIncompleteBody)
  2148  	case ObjectExistsAsDirectory:
  2149  		return GetAPIError(ErrObjectExistsAsDirectory)
  2150  	case ObjectNotFound:
  2151  		return GetAPIError(ErrNoSuchKey)
  2152  	case ObjectNameInvalid:
  2153  		return GetAPIError(ErrNoSuchKey)
  2154  	case InsufficientWriteQuorum:
  2155  		return GetAPIError(ErrWriteQuorum)
  2156  	case InsufficientReadQuorum:
  2157  		return GetAPIError(ErrReadQuorum)
  2158  	case NotImplemented:
  2159  		return APIError{
  2160  			Code:           "NotImplemented",
  2161  			HTTPStatusCode: http.StatusBadRequest,
  2162  			Description:    "Functionality not implemented",
  2163  		}
  2164  	}
  2165  
  2166  	// Log unexpected and unhandled errors.
  2167  	logger.LogIf(ctx, err)
  2168  	return ToAPIError(ctx, err)
  2169  }
  2170  
  2171  // writeWebErrorResponse - set HTTP status code and write error description to the body.
  2172  func writeWebErrorResponse(w http.ResponseWriter, err error) {
  2173  	reqInfo := &logger.ReqInfo{
  2174  		DeploymentID: globalDeploymentID,
  2175  	}
  2176  	ctx := logger.SetReqInfo(GlobalContext, reqInfo)
  2177  	apiErr := toWebAPIError(ctx, err)
  2178  	w.WriteHeader(apiErr.HTTPStatusCode)
  2179  	w.Write([]byte(apiErr.Description))
  2180  }