2015-06-30 14:42:29 -07:00
|
|
|
/*
|
2018-03-16 01:33:41 +05:30
|
|
|
* Minio Cloud Storage, (C) 2015, 2016, 2017, 2018 Minio, Inc.
|
2015-06-30 14:42:29 -07:00
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
2016-08-18 16:23:42 -07:00
|
|
|
package cmd
|
2015-06-30 14:42:29 -07:00
|
|
|
|
|
|
|
import (
|
2018-03-15 13:27:16 -07:00
|
|
|
"context"
|
2016-10-25 12:17:03 +05:30
|
|
|
"encoding/base64"
|
2016-03-05 16:43:48 -08:00
|
|
|
"encoding/xml"
|
2016-02-15 17:42:39 -08:00
|
|
|
"io"
|
2017-03-23 07:14:35 +05:30
|
|
|
"net"
|
2015-06-30 14:42:29 -07:00
|
|
|
"net/http"
|
accessPolicy: Implement Put, Get, Delete access policy.
This patch implements Get,Put,Delete bucket policies
Supporting - http://docs.aws.amazon.com/AmazonS3/latest/dev/access-policy-language-overview.html
Currently supports following actions.
"*": true,
"s3:*": true,
"s3:GetObject": true,
"s3:ListBucket": true,
"s3:PutObject": true,
"s3:CreateBucket": true,
"s3:GetBucketLocation": true,
"s3:DeleteBucket": true,
"s3:DeleteObject": true,
"s3:AbortMultipartUpload": true,
"s3:ListBucketMultipartUploads": true,
"s3:ListMultipartUploadParts": true,
following conditions for "StringEquals" and "StringNotEquals"
"s3:prefix", "s3:max-keys"
2016-02-03 16:46:56 -08:00
|
|
|
"net/url"
|
2017-01-12 02:56:42 +05:30
|
|
|
"path"
|
2017-11-15 14:10:45 -08:00
|
|
|
"path/filepath"
|
2017-10-27 16:14:06 -07:00
|
|
|
"reflect"
|
accessPolicy: Implement Put, Get, Delete access policy.
This patch implements Get,Put,Delete bucket policies
Supporting - http://docs.aws.amazon.com/AmazonS3/latest/dev/access-policy-language-overview.html
Currently supports following actions.
"*": true,
"s3:*": true,
"s3:GetObject": true,
"s3:ListBucket": true,
"s3:PutObject": true,
"s3:CreateBucket": true,
"s3:GetBucketLocation": true,
"s3:DeleteBucket": true,
"s3:DeleteObject": true,
"s3:AbortMultipartUpload": true,
"s3:ListBucketMultipartUploads": true,
"s3:ListMultipartUploadParts": true,
following conditions for "StringEquals" and "StringNotEquals"
"s3:prefix", "s3:max-keys"
2016-02-03 16:46:56 -08:00
|
|
|
"strings"
|
2016-09-02 01:59:08 -07:00
|
|
|
"sync"
|
2015-06-30 14:42:29 -07:00
|
|
|
|
2016-02-21 17:57:05 -08:00
|
|
|
mux "github.com/gorilla/mux"
|
2017-10-27 16:14:06 -07:00
|
|
|
"github.com/minio/minio-go/pkg/policy"
|
2016-08-20 03:16:38 -07:00
|
|
|
"github.com/minio/minio-go/pkg/set"
|
2017-11-25 11:58:29 -08:00
|
|
|
"github.com/minio/minio/pkg/errors"
|
2018-03-16 01:33:41 +05:30
|
|
|
"github.com/minio/minio/pkg/event"
|
2017-10-21 22:30:34 -07:00
|
|
|
"github.com/minio/minio/pkg/hash"
|
2015-06-30 14:42:29 -07:00
|
|
|
)
|
|
|
|
|
2016-04-06 21:31:40 -04:00
|
|
|
// http://docs.aws.amazon.com/AmazonS3/latest/dev/using-with-s3-actions.html
|
2016-08-10 20:10:48 -07:00
|
|
|
// Enforces bucket policies for a bucket for a given tatusaction.
|
2017-08-05 04:00:05 -04:00
|
|
|
func enforceBucketPolicy(bucket, action, resource, referer, sourceIP string, queryParams url.Values) (s3Error APIErrorCode) {
|
2016-09-26 14:28:35 -07:00
|
|
|
// Verify if bucket actually exists
|
2018-02-09 15:19:30 -08:00
|
|
|
objAPI := newObjectLayerFn()
|
|
|
|
if err := checkBucketExist(bucket, objAPI); err != nil {
|
2017-11-25 11:58:29 -08:00
|
|
|
err = errors.Cause(err)
|
2016-09-22 23:47:48 -07:00
|
|
|
switch err.(type) {
|
|
|
|
case BucketNameInvalid:
|
|
|
|
// Return error for invalid bucket name.
|
|
|
|
return ErrInvalidBucketName
|
|
|
|
case BucketNotFound:
|
|
|
|
// For no bucket found we return NoSuchBucket instead.
|
|
|
|
return ErrNoSuchBucket
|
|
|
|
}
|
|
|
|
errorIf(err, "Unable to read bucket policy.")
|
|
|
|
// Return internal error for any other errors so that we can investigate.
|
|
|
|
return ErrInternalError
|
accessPolicy: Implement Put, Get, Delete access policy.
This patch implements Get,Put,Delete bucket policies
Supporting - http://docs.aws.amazon.com/AmazonS3/latest/dev/access-policy-language-overview.html
Currently supports following actions.
"*": true,
"s3:*": true,
"s3:GetObject": true,
"s3:ListBucket": true,
"s3:PutObject": true,
"s3:CreateBucket": true,
"s3:GetBucketLocation": true,
"s3:DeleteBucket": true,
"s3:DeleteObject": true,
"s3:AbortMultipartUpload": true,
"s3:ListBucketMultipartUploads": true,
"s3:ListMultipartUploadParts": true,
following conditions for "StringEquals" and "StringNotEquals"
"s3:prefix", "s3:max-keys"
2016-02-03 16:46:56 -08:00
|
|
|
}
|
|
|
|
|
2018-02-09 15:19:30 -08:00
|
|
|
// Fetch bucket policy, if policy is not set return access denied.
|
2018-03-15 13:27:16 -07:00
|
|
|
p, err := objAPI.GetBucketPolicy(context.Background(), bucket)
|
2018-02-09 15:19:30 -08:00
|
|
|
if err != nil {
|
2017-03-16 12:21:58 -07:00
|
|
|
return ErrAccessDenied
|
|
|
|
}
|
2017-10-27 16:14:06 -07:00
|
|
|
if reflect.DeepEqual(p, emptyBucketPolicy) {
|
2016-09-26 14:28:35 -07:00
|
|
|
return ErrAccessDenied
|
|
|
|
}
|
|
|
|
|
2016-05-05 05:26:57 +05:30
|
|
|
// Construct resource in 'arn:aws:s3:::examplebucket/object' format.
|
2017-01-30 09:15:11 +05:30
|
|
|
arn := bucketARNPrefix + strings.TrimSuffix(strings.TrimPrefix(resource, "/"), "/")
|
accessPolicy: Implement Put, Get, Delete access policy.
This patch implements Get,Put,Delete bucket policies
Supporting - http://docs.aws.amazon.com/AmazonS3/latest/dev/access-policy-language-overview.html
Currently supports following actions.
"*": true,
"s3:*": true,
"s3:GetObject": true,
"s3:ListBucket": true,
"s3:PutObject": true,
"s3:CreateBucket": true,
"s3:GetBucketLocation": true,
"s3:DeleteBucket": true,
"s3:DeleteObject": true,
"s3:AbortMultipartUpload": true,
"s3:ListBucketMultipartUploads": true,
"s3:ListMultipartUploadParts": true,
following conditions for "StringEquals" and "StringNotEquals"
"s3:prefix", "s3:max-keys"
2016-02-03 16:46:56 -08:00
|
|
|
|
|
|
|
// Get conditions for policy verification.
|
2017-10-27 16:14:06 -07:00
|
|
|
conditionKeyMap := make(policy.ConditionKeyMap)
|
2017-01-30 09:15:11 +05:30
|
|
|
for queryParam := range queryParams {
|
|
|
|
conditionKeyMap[queryParam] = set.CreateStringSet(queryParams.Get(queryParam))
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add request referer to conditionKeyMap if present.
|
|
|
|
if referer != "" {
|
|
|
|
conditionKeyMap["referer"] = set.CreateStringSet(referer)
|
accessPolicy: Implement Put, Get, Delete access policy.
This patch implements Get,Put,Delete bucket policies
Supporting - http://docs.aws.amazon.com/AmazonS3/latest/dev/access-policy-language-overview.html
Currently supports following actions.
"*": true,
"s3:*": true,
"s3:GetObject": true,
"s3:ListBucket": true,
"s3:PutObject": true,
"s3:CreateBucket": true,
"s3:GetBucketLocation": true,
"s3:DeleteBucket": true,
"s3:DeleteObject": true,
"s3:AbortMultipartUpload": true,
"s3:ListBucketMultipartUploads": true,
"s3:ListMultipartUploadParts": true,
following conditions for "StringEquals" and "StringNotEquals"
"s3:prefix", "s3:max-keys"
2016-02-03 16:46:56 -08:00
|
|
|
}
|
2017-08-05 04:00:05 -04:00
|
|
|
// Add request source Ip to conditionKeyMap.
|
|
|
|
conditionKeyMap["ip"] = set.CreateStringSet(sourceIP)
|
accessPolicy: Implement Put, Get, Delete access policy.
This patch implements Get,Put,Delete bucket policies
Supporting - http://docs.aws.amazon.com/AmazonS3/latest/dev/access-policy-language-overview.html
Currently supports following actions.
"*": true,
"s3:*": true,
"s3:GetObject": true,
"s3:ListBucket": true,
"s3:PutObject": true,
"s3:CreateBucket": true,
"s3:GetBucketLocation": true,
"s3:DeleteBucket": true,
"s3:DeleteObject": true,
"s3:AbortMultipartUpload": true,
"s3:ListBucketMultipartUploads": true,
"s3:ListMultipartUploadParts": true,
following conditions for "StringEquals" and "StringNotEquals"
"s3:prefix", "s3:max-keys"
2016-02-03 16:46:56 -08:00
|
|
|
|
|
|
|
// Validate action, resource and conditions with current policy statements.
|
2017-10-27 16:14:06 -07:00
|
|
|
if !bucketPolicyEvalStatements(action, arn, conditionKeyMap, p.Statements) {
|
2016-03-10 02:24:52 -08:00
|
|
|
return ErrAccessDenied
|
accessPolicy: Implement Put, Get, Delete access policy.
This patch implements Get,Put,Delete bucket policies
Supporting - http://docs.aws.amazon.com/AmazonS3/latest/dev/access-policy-language-overview.html
Currently supports following actions.
"*": true,
"s3:*": true,
"s3:GetObject": true,
"s3:ListBucket": true,
"s3:PutObject": true,
"s3:CreateBucket": true,
"s3:GetBucketLocation": true,
"s3:DeleteBucket": true,
"s3:DeleteObject": true,
"s3:AbortMultipartUpload": true,
"s3:ListBucketMultipartUploads": true,
"s3:ListMultipartUploadParts": true,
following conditions for "StringEquals" and "StringNotEquals"
"s3:prefix", "s3:max-keys"
2016-02-03 16:46:56 -08:00
|
|
|
}
|
2016-03-10 02:24:52 -08:00
|
|
|
return ErrNone
|
accessPolicy: Implement Put, Get, Delete access policy.
This patch implements Get,Put,Delete bucket policies
Supporting - http://docs.aws.amazon.com/AmazonS3/latest/dev/access-policy-language-overview.html
Currently supports following actions.
"*": true,
"s3:*": true,
"s3:GetObject": true,
"s3:ListBucket": true,
"s3:PutObject": true,
"s3:CreateBucket": true,
"s3:GetBucketLocation": true,
"s3:DeleteBucket": true,
"s3:DeleteObject": true,
"s3:AbortMultipartUpload": true,
"s3:ListBucketMultipartUploads": true,
"s3:ListMultipartUploadParts": true,
following conditions for "StringEquals" and "StringNotEquals"
"s3:prefix", "s3:max-keys"
2016-02-03 16:46:56 -08:00
|
|
|
}
|
|
|
|
|
2017-01-12 02:56:42 +05:30
|
|
|
// Check if the action is allowed on the bucket/prefix.
|
2018-02-09 15:19:30 -08:00
|
|
|
func isBucketActionAllowed(action, bucket, prefix string, objectAPI ObjectLayer) bool {
|
2018-03-15 13:27:16 -07:00
|
|
|
bp, err := objectAPI.GetBucketPolicy(context.Background(), bucket)
|
2018-02-09 15:19:30 -08:00
|
|
|
if err != nil {
|
2017-06-01 09:43:20 -07:00
|
|
|
return false
|
|
|
|
}
|
2017-10-27 16:14:06 -07:00
|
|
|
if reflect.DeepEqual(bp, emptyBucketPolicy) {
|
2017-01-12 02:56:42 +05:30
|
|
|
return false
|
|
|
|
}
|
|
|
|
resource := bucketARNPrefix + path.Join(bucket, prefix)
|
|
|
|
var conditionKeyMap map[string]set.StringSet
|
|
|
|
// Validate action, resource and conditions with current policy statements.
|
2017-10-27 16:14:06 -07:00
|
|
|
return bucketPolicyEvalStatements(action, resource, conditionKeyMap, bp.Statements)
|
2017-01-12 02:56:42 +05:30
|
|
|
}
|
|
|
|
|
2015-12-27 00:38:38 -07:00
|
|
|
// GetBucketLocationHandler - GET Bucket location.
|
|
|
|
// -------------------------
|
|
|
|
// This operation returns bucket location.
|
2016-04-12 12:45:15 -07:00
|
|
|
func (api objectAPIHandlers) GetBucketLocationHandler(w http.ResponseWriter, r *http.Request) {
|
2018-03-14 12:01:47 -07:00
|
|
|
ctx := newContext(r, "GetBucketLocation")
|
|
|
|
|
2016-02-15 17:42:39 -08:00
|
|
|
vars := mux.Vars(r)
|
2015-12-27 00:38:38 -07:00
|
|
|
bucket := vars["bucket"]
|
|
|
|
|
2016-08-10 18:47:49 -07:00
|
|
|
objectAPI := api.ObjectAPI()
|
|
|
|
if objectAPI == nil {
|
2017-01-06 00:37:00 -08:00
|
|
|
writeErrorResponse(w, ErrServerNotInitialized, r.URL)
|
2016-08-10 18:47:49 -07:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-01-19 20:31:51 +01:00
|
|
|
s3Error := checkRequestAuthType(r, bucket, "s3:GetBucketLocation", globalMinioDefaultRegion)
|
|
|
|
if s3Error == ErrInvalidRegion {
|
|
|
|
// Clients like boto3 send getBucketLocation() call signed with region that is configured.
|
2017-11-29 13:12:47 -08:00
|
|
|
s3Error = checkRequestAuthType(r, "", "s3:GetBucketLocation", globalServerConfig.GetRegion())
|
2017-01-19 20:31:51 +01:00
|
|
|
}
|
|
|
|
if s3Error != ErrNone {
|
2017-01-06 00:37:00 -08:00
|
|
|
writeErrorResponse(w, s3Error, r.URL)
|
2016-11-21 13:51:05 -08:00
|
|
|
return
|
2016-02-04 12:52:25 -08:00
|
|
|
}
|
|
|
|
|
2018-02-15 17:45:57 -08:00
|
|
|
bucketLock := globalNSMutex.NewNSLock(bucket, "")
|
|
|
|
if err := bucketLock.GetRLock(globalObjectTimeout); err != nil {
|
|
|
|
writeErrorResponse(w, toAPIErrorCode(err), r.URL)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
defer bucketLock.RUnlock()
|
2018-03-28 14:14:06 -07:00
|
|
|
getBucketInfo := objectAPI.GetBucketInfo
|
|
|
|
if api.CacheAPI() != nil {
|
|
|
|
getBucketInfo = api.CacheAPI().GetBucketInfo
|
|
|
|
}
|
|
|
|
if _, err := getBucketInfo(ctx, bucket); err != nil {
|
2017-01-06 00:37:00 -08:00
|
|
|
writeErrorResponse(w, toAPIErrorCode(err), r.URL)
|
2016-01-19 17:49:48 -08:00
|
|
|
return
|
2015-12-27 00:38:38 -07:00
|
|
|
}
|
|
|
|
|
2016-02-15 17:42:39 -08:00
|
|
|
// Generate response.
|
2016-03-06 12:16:22 -08:00
|
|
|
encodedSuccessResponse := encodeResponse(LocationResponse{})
|
config/main: Re-write config files - add to new config v3
- New config format.
```
{
"version": "3",
"address": ":9000",
"backend": {
"type": "fs",
"disk": "/path"
},
"credential": {
"accessKey": "WLGDGYAQYIGI833EV05A",
"secretKey": "BYvgJM101sHngl2uzjXS/OBF/aMxAN06JrJ3qJlF"
},
"region": "us-east-1",
"logger": {
"file": {
"enable": false,
"fileName": "",
"level": "error"
},
"syslog": {
"enable": false,
"address": "",
"level": "debug"
},
"console": {
"enable": true,
"level": "fatal"
}
}
}
```
New command lines in lieu of supporting XL.
Minio initialize filesystem backend.
~~~
$ minio init fs <path>
~~~
Minio initialize XL backend.
~~~
$ minio init xl <url1>...<url16>
~~~
For 'fs' backend it starts the server.
~~~
$ minio server
~~~
For 'xl' backend it waits for servers to join.
~~~
$ minio server
... [PROGRESS BAR] of servers connecting
~~~
Now on other servers execute 'join' and they connect.
~~~
....
minio join <url1> -- from <url2> && minio server
minio join <url1> -- from <url3> && minio server
...
...
minio join <url1> -- from <url16> && minio server
~~~
2016-02-12 15:27:10 -08:00
|
|
|
// Get current region.
|
2017-11-29 13:12:47 -08:00
|
|
|
region := globalServerConfig.GetRegion()
|
2017-01-18 12:24:34 -08:00
|
|
|
if region != globalMinioDefaultRegion {
|
2016-03-06 12:16:22 -08:00
|
|
|
encodedSuccessResponse = encodeResponse(LocationResponse{
|
config/main: Re-write config files - add to new config v3
- New config format.
```
{
"version": "3",
"address": ":9000",
"backend": {
"type": "fs",
"disk": "/path"
},
"credential": {
"accessKey": "WLGDGYAQYIGI833EV05A",
"secretKey": "BYvgJM101sHngl2uzjXS/OBF/aMxAN06JrJ3qJlF"
},
"region": "us-east-1",
"logger": {
"file": {
"enable": false,
"fileName": "",
"level": "error"
},
"syslog": {
"enable": false,
"address": "",
"level": "debug"
},
"console": {
"enable": true,
"level": "fatal"
}
}
}
```
New command lines in lieu of supporting XL.
Minio initialize filesystem backend.
~~~
$ minio init fs <path>
~~~
Minio initialize XL backend.
~~~
$ minio init xl <url1>...<url16>
~~~
For 'fs' backend it starts the server.
~~~
$ minio server
~~~
For 'xl' backend it waits for servers to join.
~~~
$ minio server
... [PROGRESS BAR] of servers connecting
~~~
Now on other servers execute 'join' and they connect.
~~~
....
minio join <url1> -- from <url2> && minio server
minio join <url1> -- from <url3> && minio server
...
...
minio join <url1> -- from <url16> && minio server
~~~
2016-02-12 15:27:10 -08:00
|
|
|
Location: region,
|
2016-02-15 17:42:39 -08:00
|
|
|
})
|
|
|
|
}
|
2017-01-06 00:37:00 -08:00
|
|
|
|
|
|
|
// Write success response.
|
|
|
|
writeSuccessResponseXML(w, encodedSuccessResponse)
|
2015-12-27 00:38:38 -07:00
|
|
|
}
|
|
|
|
|
2015-06-30 20:15:48 -07:00
|
|
|
// ListMultipartUploadsHandler - GET Bucket (List Multipart uploads)
|
2015-06-30 14:42:29 -07:00
|
|
|
// -------------------------
|
|
|
|
// This operation lists in-progress multipart uploads. An in-progress
|
|
|
|
// multipart upload is a multipart upload that has been initiated,
|
2015-10-16 19:09:35 -07:00
|
|
|
// using the Initiate Multipart Upload request, but has not yet been
|
|
|
|
// completed or aborted. This operation returns at most 1,000 multipart
|
|
|
|
// uploads in the response.
|
2015-06-30 14:42:29 -07:00
|
|
|
//
|
2016-04-12 12:45:15 -07:00
|
|
|
func (api objectAPIHandlers) ListMultipartUploadsHandler(w http.ResponseWriter, r *http.Request) {
|
2018-03-14 12:01:47 -07:00
|
|
|
ctx := newContext(r, "ListMultipartUploads")
|
|
|
|
|
2016-02-15 17:42:39 -08:00
|
|
|
vars := mux.Vars(r)
|
2015-10-16 19:09:35 -07:00
|
|
|
bucket := vars["bucket"]
|
|
|
|
|
2016-08-10 18:47:49 -07:00
|
|
|
objectAPI := api.ObjectAPI()
|
|
|
|
if objectAPI == nil {
|
2017-01-06 00:37:00 -08:00
|
|
|
writeErrorResponse(w, ErrServerNotInitialized, r.URL)
|
2016-08-10 18:47:49 -07:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-11-29 13:12:47 -08:00
|
|
|
if s3Error := checkRequestAuthType(r, bucket, "s3:ListBucketMultipartUploads", globalServerConfig.GetRegion()); s3Error != ErrNone {
|
2017-01-06 00:37:00 -08:00
|
|
|
writeErrorResponse(w, s3Error, r.URL)
|
2016-02-15 17:42:39 -08:00
|
|
|
return
|
2016-02-04 12:52:25 -08:00
|
|
|
}
|
|
|
|
|
objectAPI: Fix object API interface, remove unnecessary structs.
ObjectAPI changes.
```
ListObjects(bucket, prefix, marker, delimiter string, maxKeys int) (ListObjectsInfo, *probe.Error)
ListMultipartUploads(bucket, objectPrefix, keyMarker, uploadIDMarker, delimiter string, maxUploads int) (ListMultipartsInfo, *probe.Error)
ListObjectParts(bucket, object, uploadID string, partNumberMarker, maxParts int) (ListPartsInfo, *probe.Error)
CompleteMultipartUpload(bucket string, object string, uploadID string, parts []completePart) (ObjectInfo, *probe.Error)
```
2016-04-03 01:34:20 -07:00
|
|
|
prefix, keyMarker, uploadIDMarker, delimiter, maxUploads, _ := getBucketMultipartResources(r.URL.Query())
|
|
|
|
if maxUploads < 0 {
|
2017-01-06 00:37:00 -08:00
|
|
|
writeErrorResponse(w, ErrInvalidMaxUploads, r.URL)
|
2015-07-16 17:22:45 -07:00
|
|
|
return
|
|
|
|
}
|
2016-04-05 12:26:17 -07:00
|
|
|
if keyMarker != "" {
|
2016-04-29 14:24:10 -07:00
|
|
|
// Marker not common with prefix is not implemented.
|
2017-02-03 23:27:50 -08:00
|
|
|
if !hasPrefix(keyMarker, prefix) {
|
2017-01-06 00:37:00 -08:00
|
|
|
writeErrorResponse(w, ErrNotImplemented, r.URL)
|
2016-04-29 14:24:10 -07:00
|
|
|
return
|
2016-04-05 12:26:17 -07:00
|
|
|
}
|
2015-06-30 14:42:29 -07:00
|
|
|
}
|
|
|
|
|
2018-03-14 12:01:47 -07:00
|
|
|
listMultipartsInfo, err := objectAPI.ListMultipartUploads(ctx, bucket, prefix, keyMarker, uploadIDMarker, delimiter, maxUploads)
|
2015-09-19 03:20:07 -07:00
|
|
|
if err != nil {
|
2017-01-06 00:37:00 -08:00
|
|
|
writeErrorResponse(w, toAPIErrorCode(err), r.URL)
|
2015-08-03 16:17:21 -07:00
|
|
|
return
|
|
|
|
}
|
2015-09-19 03:20:07 -07:00
|
|
|
// generate response
|
objectAPI: Fix object API interface, remove unnecessary structs.
ObjectAPI changes.
```
ListObjects(bucket, prefix, marker, delimiter string, maxKeys int) (ListObjectsInfo, *probe.Error)
ListMultipartUploads(bucket, objectPrefix, keyMarker, uploadIDMarker, delimiter string, maxUploads int) (ListMultipartsInfo, *probe.Error)
ListObjectParts(bucket, object, uploadID string, partNumberMarker, maxParts int) (ListPartsInfo, *probe.Error)
CompleteMultipartUpload(bucket string, object string, uploadID string, parts []completePart) (ObjectInfo, *probe.Error)
```
2016-04-03 01:34:20 -07:00
|
|
|
response := generateListMultipartUploadsResponse(bucket, listMultipartsInfo)
|
2016-03-06 12:16:22 -08:00
|
|
|
encodedSuccessResponse := encodeResponse(response)
|
2017-01-06 00:37:00 -08:00
|
|
|
|
2016-01-08 00:40:06 -08:00
|
|
|
// write success response.
|
2017-01-06 00:37:00 -08:00
|
|
|
writeSuccessResponseXML(w, encodedSuccessResponse)
|
2015-06-30 14:42:29 -07:00
|
|
|
}
|
|
|
|
|
2016-10-09 21:51:37 +05:30
|
|
|
// ListBucketsHandler - GET Service.
|
2015-06-30 14:42:29 -07:00
|
|
|
// -----------
|
|
|
|
// This implementation of the GET operation returns a list of all buckets
|
|
|
|
// owned by the authenticated sender of the request.
|
2016-04-12 12:45:15 -07:00
|
|
|
func (api objectAPIHandlers) ListBucketsHandler(w http.ResponseWriter, r *http.Request) {
|
2018-03-14 12:01:47 -07:00
|
|
|
ctx := newContext(r, "ListBuckets")
|
|
|
|
|
2016-08-10 18:47:49 -07:00
|
|
|
objectAPI := api.ObjectAPI()
|
|
|
|
if objectAPI == nil {
|
2017-01-06 00:37:00 -08:00
|
|
|
writeErrorResponse(w, ErrServerNotInitialized, r.URL)
|
2016-08-10 18:47:49 -07:00
|
|
|
return
|
|
|
|
}
|
2018-03-28 14:14:06 -07:00
|
|
|
listBuckets := objectAPI.ListBuckets
|
2016-08-10 18:47:49 -07:00
|
|
|
|
2018-03-28 14:14:06 -07:00
|
|
|
if api.CacheAPI() != nil {
|
|
|
|
listBuckets = api.CacheAPI().ListBuckets
|
|
|
|
}
|
2016-11-21 13:51:05 -08:00
|
|
|
// ListBuckets does not have any bucket action.
|
2017-01-18 12:24:34 -08:00
|
|
|
s3Error := checkRequestAuthType(r, "", "", globalMinioDefaultRegion)
|
2016-12-01 03:25:56 +05:30
|
|
|
if s3Error == ErrInvalidRegion {
|
|
|
|
// Clients like boto3 send listBuckets() call signed with region that is configured.
|
2017-11-29 13:12:47 -08:00
|
|
|
s3Error = checkRequestAuthType(r, "", "", globalServerConfig.GetRegion())
|
2016-12-01 03:25:56 +05:30
|
|
|
}
|
|
|
|
if s3Error != ErrNone {
|
2017-01-06 00:37:00 -08:00
|
|
|
writeErrorResponse(w, s3Error, r.URL)
|
2016-02-15 17:42:39 -08:00
|
|
|
return
|
2016-02-04 12:52:25 -08:00
|
|
|
}
|
2016-08-10 18:47:49 -07:00
|
|
|
// Invoke the list buckets.
|
2018-03-28 14:14:06 -07:00
|
|
|
bucketsInfo, err := listBuckets(ctx)
|
2016-07-17 13:23:15 -07:00
|
|
|
if err != nil {
|
2017-01-06 00:37:00 -08:00
|
|
|
writeErrorResponse(w, toAPIErrorCode(err), r.URL)
|
2015-08-03 16:17:21 -07:00
|
|
|
return
|
|
|
|
}
|
2016-07-17 13:23:15 -07:00
|
|
|
|
|
|
|
// Generate response.
|
|
|
|
response := generateListBucketsResponse(bucketsInfo)
|
|
|
|
encodedSuccessResponse := encodeResponse(response)
|
2017-01-06 00:37:00 -08:00
|
|
|
|
2016-07-17 13:23:15 -07:00
|
|
|
// Write response.
|
2017-01-06 00:37:00 -08:00
|
|
|
writeSuccessResponseXML(w, encodedSuccessResponse)
|
2015-06-30 14:42:29 -07:00
|
|
|
}
|
|
|
|
|
2016-03-05 16:43:48 -08:00
|
|
|
// DeleteMultipleObjectsHandler - deletes multiple objects.
|
2016-04-12 12:45:15 -07:00
|
|
|
func (api objectAPIHandlers) DeleteMultipleObjectsHandler(w http.ResponseWriter, r *http.Request) {
|
2018-03-14 12:01:47 -07:00
|
|
|
ctx := newContext(r, "DeleteMultipleObjects")
|
|
|
|
|
2016-03-05 16:43:48 -08:00
|
|
|
vars := mux.Vars(r)
|
|
|
|
bucket := vars["bucket"]
|
|
|
|
|
2016-08-10 18:47:49 -07:00
|
|
|
objectAPI := api.ObjectAPI()
|
|
|
|
if objectAPI == nil {
|
2017-01-06 00:37:00 -08:00
|
|
|
writeErrorResponse(w, ErrServerNotInitialized, r.URL)
|
2016-08-10 18:47:49 -07:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-08-16 05:49:31 +10:00
|
|
|
var authError APIErrorCode
|
2017-11-29 13:12:47 -08:00
|
|
|
if authError = checkRequestAuthType(r, bucket, "s3:DeleteObject", globalServerConfig.GetRegion()); authError != ErrNone {
|
2017-08-16 05:49:31 +10:00
|
|
|
// In the event access is denied, a 200 response should still be returned
|
|
|
|
// http://docs.aws.amazon.com/AmazonS3/latest/API/multiobjectdeleteapi.html
|
|
|
|
if authError != ErrAccessDenied {
|
|
|
|
writeErrorResponse(w, authError, r.URL)
|
|
|
|
return
|
|
|
|
}
|
2016-03-12 16:08:15 -08:00
|
|
|
}
|
|
|
|
|
2016-03-05 16:43:48 -08:00
|
|
|
// Content-Length is required and should be non-zero
|
|
|
|
// http://docs.aws.amazon.com/AmazonS3/latest/API/multiobjectdeleteapi.html
|
|
|
|
if r.ContentLength <= 0 {
|
2017-01-06 00:37:00 -08:00
|
|
|
writeErrorResponse(w, ErrMissingContentLength, r.URL)
|
2016-03-05 16:43:48 -08:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Content-Md5 is requied should be set
|
|
|
|
// http://docs.aws.amazon.com/AmazonS3/latest/API/multiobjectdeleteapi.html
|
|
|
|
if _, ok := r.Header["Content-Md5"]; !ok {
|
2017-01-06 00:37:00 -08:00
|
|
|
writeErrorResponse(w, ErrMissingContentMD5, r.URL)
|
2016-03-05 16:43:48 -08:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Allocate incoming content length bytes.
|
|
|
|
deleteXMLBytes := make([]byte, r.ContentLength)
|
|
|
|
|
|
|
|
// Read incoming body XML bytes.
|
2016-04-29 14:24:10 -07:00
|
|
|
if _, err := io.ReadFull(r.Body, deleteXMLBytes); err != nil {
|
2016-05-16 14:31:28 -07:00
|
|
|
errorIf(err, "Unable to read HTTP body.")
|
2017-01-06 00:37:00 -08:00
|
|
|
writeErrorResponse(w, ErrInternalError, r.URL)
|
2016-03-05 16:43:48 -08:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Unmarshal list of keys to be deleted.
|
|
|
|
deleteObjects := &DeleteObjectsRequest{}
|
2016-04-29 14:24:10 -07:00
|
|
|
if err := xml.Unmarshal(deleteXMLBytes, deleteObjects); err != nil {
|
2016-05-16 14:31:28 -07:00
|
|
|
errorIf(err, "Unable to unmarshal delete objects request XML.")
|
2017-01-06 00:37:00 -08:00
|
|
|
writeErrorResponse(w, ErrMalformedXML, r.URL)
|
2016-03-05 16:43:48 -08:00
|
|
|
return
|
|
|
|
}
|
2018-03-28 05:14:45 +05:30
|
|
|
|
|
|
|
// Deny if WORM is enabled
|
|
|
|
if globalWORMEnabled {
|
|
|
|
// Not required to check whether given objects exist or not, because
|
|
|
|
// DeleteMultipleObject is always successful irrespective of object existence.
|
|
|
|
writeErrorResponse(w, ErrMethodNotAllowed, r.URL)
|
|
|
|
return
|
|
|
|
}
|
2016-03-05 16:43:48 -08:00
|
|
|
|
2016-09-02 01:59:08 -07:00
|
|
|
var wg = &sync.WaitGroup{} // Allocate a new wait group.
|
|
|
|
var dErrs = make([]error, len(deleteObjects.Objects))
|
|
|
|
|
|
|
|
// Delete all requested objects in parallel.
|
|
|
|
for index, object := range deleteObjects.Objects {
|
|
|
|
wg.Add(1)
|
|
|
|
go func(i int, obj ObjectIdentifier) {
|
2017-08-16 05:49:31 +10:00
|
|
|
defer wg.Done()
|
|
|
|
// If the request is denied access, each item
|
|
|
|
// should be marked as 'AccessDenied'
|
|
|
|
if authError == ErrAccessDenied {
|
|
|
|
dErrs[i] = PrefixAccessDenied{
|
|
|
|
Bucket: bucket,
|
|
|
|
Object: obj.ObjectName,
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
2018-03-28 14:14:06 -07:00
|
|
|
deleteObject := objectAPI.DeleteObject
|
|
|
|
if api.CacheAPI() != nil {
|
|
|
|
deleteObject = api.CacheAPI().DeleteObject
|
|
|
|
}
|
|
|
|
dErr := deleteObject(ctx, bucket, obj.ObjectName)
|
2018-01-12 20:34:52 -08:00
|
|
|
if dErr != nil {
|
|
|
|
dErrs[i] = dErr
|
2016-09-02 01:59:08 -07:00
|
|
|
}
|
|
|
|
}(index, object)
|
|
|
|
}
|
|
|
|
wg.Wait()
|
|
|
|
|
|
|
|
// Collect deleted objects and errors if any.
|
2016-03-05 16:43:48 -08:00
|
|
|
var deletedObjects []ObjectIdentifier
|
2016-09-02 01:59:08 -07:00
|
|
|
var deleteErrors []DeleteError
|
|
|
|
for index, err := range dErrs {
|
|
|
|
object := deleteObjects.Objects[index]
|
|
|
|
// Success deleted objects are collected separately.
|
2016-03-05 16:43:48 -08:00
|
|
|
if err == nil {
|
2016-09-02 01:59:08 -07:00
|
|
|
deletedObjects = append(deletedObjects, object)
|
|
|
|
continue
|
2016-03-05 16:43:48 -08:00
|
|
|
}
|
2017-11-25 11:58:29 -08:00
|
|
|
if _, ok := errors.Cause(err).(ObjectNotFound); ok {
|
2016-09-08 00:19:12 +05:30
|
|
|
// If the object is not found it should be
|
|
|
|
// accounted as deleted as per S3 spec.
|
|
|
|
deletedObjects = append(deletedObjects, object)
|
|
|
|
continue
|
|
|
|
}
|
2016-09-02 01:59:08 -07:00
|
|
|
// Error during delete should be collected separately.
|
|
|
|
deleteErrors = append(deleteErrors, DeleteError{
|
|
|
|
Code: errorCodeResponse[toAPIErrorCode(err)].Code,
|
|
|
|
Message: errorCodeResponse[toAPIErrorCode(err)].Description,
|
|
|
|
Key: object.ObjectName,
|
|
|
|
})
|
2016-03-05 16:43:48 -08:00
|
|
|
}
|
2016-09-02 01:59:08 -07:00
|
|
|
|
2016-03-05 16:43:48 -08:00
|
|
|
// Generate response
|
|
|
|
response := generateMultiDeleteResponse(deleteObjects.Quiet, deletedObjects, deleteErrors)
|
|
|
|
encodedSuccessResponse := encodeResponse(response)
|
2017-01-06 00:37:00 -08:00
|
|
|
|
2016-03-05 16:43:48 -08:00
|
|
|
// Write success response.
|
2017-01-06 00:37:00 -08:00
|
|
|
writeSuccessResponseXML(w, encodedSuccessResponse)
|
2016-09-02 01:59:08 -07:00
|
|
|
|
2017-03-23 07:14:35 +05:30
|
|
|
// Get host and port from Request.RemoteAddr failing which
|
|
|
|
// fill them with empty strings.
|
|
|
|
host, port, err := net.SplitHostPort(r.RemoteAddr)
|
|
|
|
if err != nil {
|
|
|
|
host, port = "", ""
|
|
|
|
}
|
|
|
|
|
2016-09-29 11:16:19 +05:30
|
|
|
// Notify deleted event for objects.
|
|
|
|
for _, dobj := range deletedObjects {
|
2018-03-16 01:33:41 +05:30
|
|
|
sendEvent(eventArgs{
|
|
|
|
EventName: event.ObjectRemovedDelete,
|
|
|
|
BucketName: bucket,
|
|
|
|
Object: ObjectInfo{
|
2016-09-29 11:16:19 +05:30
|
|
|
Name: dobj.ObjectName,
|
|
|
|
},
|
2017-03-13 14:41:13 -07:00
|
|
|
ReqParams: extractReqParams(r),
|
2017-03-23 07:14:35 +05:30
|
|
|
UserAgent: r.UserAgent(),
|
|
|
|
Host: host,
|
|
|
|
Port: port,
|
2016-09-29 11:16:19 +05:30
|
|
|
})
|
2016-09-02 01:59:08 -07:00
|
|
|
}
|
2016-03-05 16:43:48 -08:00
|
|
|
}
|
|
|
|
|
2015-06-30 20:15:48 -07:00
|
|
|
// PutBucketHandler - PUT Bucket
|
2015-06-30 14:42:29 -07:00
|
|
|
// ----------
|
|
|
|
// This implementation of the PUT operation creates a new bucket for authenticated request
|
2016-04-12 12:45:15 -07:00
|
|
|
func (api objectAPIHandlers) PutBucketHandler(w http.ResponseWriter, r *http.Request) {
|
2018-03-14 12:01:47 -07:00
|
|
|
ctx := newContext(r, "PutBucket")
|
|
|
|
|
2016-08-10 18:47:49 -07:00
|
|
|
objectAPI := api.ObjectAPI()
|
|
|
|
if objectAPI == nil {
|
2017-01-06 00:37:00 -08:00
|
|
|
writeErrorResponse(w, ErrServerNotInitialized, r.URL)
|
2016-08-10 18:47:49 -07:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2016-11-21 13:51:05 -08:00
|
|
|
// PutBucket does not have any bucket action.
|
2017-11-29 13:12:47 -08:00
|
|
|
s3Error := checkRequestAuthType(r, "", "", globalServerConfig.GetRegion())
|
2017-01-19 20:31:51 +01:00
|
|
|
if s3Error != ErrNone {
|
2017-01-06 00:37:00 -08:00
|
|
|
writeErrorResponse(w, s3Error, r.URL)
|
2016-02-04 12:52:25 -08:00
|
|
|
return
|
2015-07-14 09:17:30 -07:00
|
|
|
}
|
|
|
|
|
2016-07-18 23:56:27 -07:00
|
|
|
vars := mux.Vars(r)
|
|
|
|
bucket := vars["bucket"]
|
|
|
|
|
2017-04-03 14:50:09 -07:00
|
|
|
// Parse incoming location constraint.
|
|
|
|
location, s3Error := parseLocationConstraint(r)
|
|
|
|
if s3Error != ErrNone {
|
2017-01-06 00:37:00 -08:00
|
|
|
writeErrorResponse(w, s3Error, r.URL)
|
2016-08-25 20:00:47 -07:00
|
|
|
return
|
2016-04-21 06:05:38 +05:30
|
|
|
}
|
2016-07-23 22:51:12 -07:00
|
|
|
|
2017-04-03 14:50:09 -07:00
|
|
|
// Validate if location sent by the client is valid, reject
|
|
|
|
// requests which do not follow valid region requirements.
|
|
|
|
if !isValidLocation(location) {
|
|
|
|
writeErrorResponse(w, ErrInvalidRegion, r.URL)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-02-15 17:45:57 -08:00
|
|
|
bucketLock := globalNSMutex.NewNSLock(bucket, "")
|
|
|
|
if err := bucketLock.GetLock(globalObjectTimeout); err != nil {
|
|
|
|
writeErrorResponse(w, toAPIErrorCode(err), r.URL)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
defer bucketLock.Unlock()
|
|
|
|
|
2016-07-23 22:51:12 -07:00
|
|
|
// Proceed to creating a bucket.
|
2018-03-14 12:01:47 -07:00
|
|
|
err := objectAPI.MakeBucketWithLocation(ctx, bucket, location)
|
2015-09-19 03:20:07 -07:00
|
|
|
if err != nil {
|
2017-01-06 00:37:00 -08:00
|
|
|
writeErrorResponse(w, toAPIErrorCode(err), r.URL)
|
2015-08-03 16:17:21 -07:00
|
|
|
return
|
|
|
|
}
|
2017-01-06 00:37:00 -08:00
|
|
|
|
2015-09-19 03:20:07 -07:00
|
|
|
// Make sure to add Location information here only for bucket
|
2018-03-23 13:46:57 -07:00
|
|
|
w.Header().Set("Location", path.Clean(r.URL.Path)) // Clean any trailing slashes.
|
2017-01-06 00:37:00 -08:00
|
|
|
|
|
|
|
writeSuccessResponseHeadersOnly(w)
|
2015-06-30 14:42:29 -07:00
|
|
|
}
|
|
|
|
|
2015-10-01 23:51:17 -07:00
|
|
|
// PostPolicyBucketHandler - POST policy
|
|
|
|
// ----------
|
|
|
|
// This implementation of the POST operation handles object creation with a specified
|
|
|
|
// signature policy in multipart/form-data
|
2016-04-12 12:45:15 -07:00
|
|
|
func (api objectAPIHandlers) PostPolicyBucketHandler(w http.ResponseWriter, r *http.Request) {
|
2018-03-14 12:01:47 -07:00
|
|
|
ctx := newContext(r, "PostPolicyBucket")
|
|
|
|
|
2016-08-10 18:47:49 -07:00
|
|
|
objectAPI := api.ObjectAPI()
|
|
|
|
if objectAPI == nil {
|
2017-01-06 00:37:00 -08:00
|
|
|
writeErrorResponse(w, ErrServerNotInitialized, r.URL)
|
2016-08-10 18:47:49 -07:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-11-13 16:30:20 -08:00
|
|
|
bucket := mux.Vars(r)["bucket"]
|
|
|
|
|
2017-02-02 19:45:00 +01:00
|
|
|
// Require Content-Length to be set in the request
|
|
|
|
size := r.ContentLength
|
|
|
|
if size < 0 {
|
|
|
|
writeErrorResponse(w, ErrMissingContentLength, r.URL)
|
|
|
|
return
|
|
|
|
}
|
2017-11-15 14:10:45 -08:00
|
|
|
resource, err := getResource(r.URL.Path, r.Host, globalDomainName)
|
|
|
|
if err != nil {
|
|
|
|
writeErrorResponse(w, ErrInvalidRequest, r.URL)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
// Make sure that the URL does not contain object name.
|
|
|
|
if bucket != filepath.Clean(resource[1:]) {
|
|
|
|
writeErrorResponse(w, ErrMethodNotAllowed, r.URL)
|
|
|
|
return
|
|
|
|
}
|
2017-02-02 19:45:00 +01:00
|
|
|
|
2015-10-01 23:51:17 -07:00
|
|
|
// Here the parameter is the size of the form data that should
|
2016-03-22 17:54:31 -07:00
|
|
|
// be loaded in memory, the remaining being put in temporary files.
|
2016-04-29 14:24:10 -07:00
|
|
|
reader, err := r.MultipartReader()
|
|
|
|
if err != nil {
|
2016-05-16 14:31:28 -07:00
|
|
|
errorIf(err, "Unable to initialize multipart reader.")
|
2017-01-06 00:37:00 -08:00
|
|
|
writeErrorResponse(w, ErrMalformedPOSTRequest, r.URL)
|
2015-10-01 23:51:17 -07:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-02-02 19:45:00 +01:00
|
|
|
// Read multipart data and save in memory and in the disk if needed
|
|
|
|
form, err := reader.ReadForm(maxFormMemory)
|
|
|
|
if err != nil {
|
|
|
|
errorIf(err, "Unable to initialize multipart reader.")
|
|
|
|
writeErrorResponse(w, ErrMalformedPOSTRequest, r.URL)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove all tmp files creating during multipart upload
|
|
|
|
defer form.RemoveAll()
|
|
|
|
|
|
|
|
// Extract all form fields
|
|
|
|
fileBody, fileName, fileSize, formValues, err := extractPostPolicyFormValues(form)
|
2016-02-04 12:52:25 -08:00
|
|
|
if err != nil {
|
2016-05-16 14:31:28 -07:00
|
|
|
errorIf(err, "Unable to parse form values.")
|
2017-01-06 00:37:00 -08:00
|
|
|
writeErrorResponse(w, ErrMalformedPOSTRequest, r.URL)
|
2015-10-01 23:51:17 -07:00
|
|
|
return
|
|
|
|
}
|
2017-02-02 19:45:00 +01:00
|
|
|
|
2017-02-09 21:37:32 +01:00
|
|
|
// Check if file is provided, error out otherwise.
|
|
|
|
if fileBody == nil {
|
|
|
|
writeErrorResponse(w, ErrPOSTFileRequired, r.URL)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-02-02 19:45:00 +01:00
|
|
|
// Close multipart file
|
|
|
|
defer fileBody.Close()
|
|
|
|
|
2017-03-13 14:41:13 -07:00
|
|
|
formValues.Set("Bucket", bucket)
|
2016-02-15 17:42:39 -08:00
|
|
|
|
2017-03-13 14:41:13 -07:00
|
|
|
if fileName != "" && strings.Contains(formValues.Get("Key"), "${filename}") {
|
2016-07-28 02:51:55 +02:00
|
|
|
// S3 feature to replace ${filename} found in Key form field
|
|
|
|
// by the filename attribute passed in multipart
|
2017-03-13 14:41:13 -07:00
|
|
|
formValues.Set("Key", strings.Replace(formValues.Get("Key"), "${filename}", fileName, -1))
|
|
|
|
}
|
|
|
|
object := formValues.Get("Key")
|
|
|
|
|
|
|
|
successRedirect := formValues.Get("success_action_redirect")
|
|
|
|
successStatus := formValues.Get("success_action_status")
|
|
|
|
var redirectURL *url.URL
|
|
|
|
if successRedirect != "" {
|
|
|
|
redirectURL, err = url.Parse(successRedirect)
|
|
|
|
if err != nil {
|
|
|
|
writeErrorResponse(w, ErrMalformedPOSTRequest, r.URL)
|
|
|
|
return
|
|
|
|
}
|
2016-07-28 02:51:55 +02:00
|
|
|
}
|
|
|
|
|
2016-02-15 17:42:39 -08:00
|
|
|
// Verify policy signature.
|
2016-03-30 20:04:51 -07:00
|
|
|
apiErr := doesPolicySignatureMatch(formValues)
|
|
|
|
if apiErr != ErrNone {
|
2017-01-06 00:37:00 -08:00
|
|
|
writeErrorResponse(w, apiErr, r.URL)
|
2015-10-01 23:51:17 -07:00
|
|
|
return
|
|
|
|
}
|
2016-10-25 12:17:03 +05:30
|
|
|
|
2017-03-13 14:41:13 -07:00
|
|
|
policyBytes, err := base64.StdEncoding.DecodeString(formValues.Get("Policy"))
|
2016-10-25 12:17:03 +05:30
|
|
|
if err != nil {
|
2017-01-06 00:37:00 -08:00
|
|
|
writeErrorResponse(w, ErrMalformedPOSTRequest, r.URL)
|
2016-10-25 12:17:03 +05:30
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2016-10-26 22:45:57 +05:30
|
|
|
postPolicyForm, err := parsePostPolicyForm(string(policyBytes))
|
2016-10-25 12:17:03 +05:30
|
|
|
if err != nil {
|
2017-01-06 00:37:00 -08:00
|
|
|
writeErrorResponse(w, ErrMalformedPOSTRequest, r.URL)
|
2016-10-25 12:17:03 +05:30
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure formValues adhere to policy restrictions.
|
|
|
|
if apiErr = checkPostPolicy(formValues, postPolicyForm); apiErr != ErrNone {
|
2017-01-06 00:37:00 -08:00
|
|
|
writeErrorResponse(w, apiErr, r.URL)
|
2015-10-06 10:12:06 -07:00
|
|
|
return
|
|
|
|
}
|
2016-05-19 17:10:08 -07:00
|
|
|
|
2017-02-02 19:45:00 +01:00
|
|
|
// Ensure that the object size is within expected range, also the file size
|
|
|
|
// should not exceed the maximum single Put size (5 GiB)
|
2016-10-25 12:17:03 +05:30
|
|
|
lengthRange := postPolicyForm.Conditions.ContentLengthRange
|
|
|
|
if lengthRange.Valid {
|
2017-02-02 19:45:00 +01:00
|
|
|
if fileSize < lengthRange.Min {
|
|
|
|
writeErrorResponse(w, toAPIErrorCode(errDataTooSmall), r.URL)
|
|
|
|
return
|
2016-11-21 04:15:26 -08:00
|
|
|
}
|
2017-02-02 19:45:00 +01:00
|
|
|
|
2017-03-03 10:14:17 -08:00
|
|
|
if fileSize > lengthRange.Max || isMaxObjectSize(fileSize) {
|
2017-02-02 19:45:00 +01:00
|
|
|
writeErrorResponse(w, toAPIErrorCode(errDataTooLarge), r.URL)
|
|
|
|
return
|
2016-11-21 04:15:26 -08:00
|
|
|
}
|
2016-10-25 12:17:03 +05:30
|
|
|
}
|
|
|
|
|
2016-12-20 01:14:04 +01:00
|
|
|
// Extract metadata to be saved from received Form.
|
2017-07-05 16:56:10 -07:00
|
|
|
metadata, err := extractMetadataFromHeader(formValues)
|
|
|
|
if err != nil {
|
|
|
|
errorIf(err, "found invalid http request header")
|
|
|
|
writeErrorResponse(w, ErrInternalError, r.URL)
|
|
|
|
return
|
|
|
|
}
|
2016-10-03 04:21:49 +05:30
|
|
|
|
2017-10-21 22:30:34 -07:00
|
|
|
hashReader, err := hash.NewReader(fileBody, fileSize, "", "")
|
|
|
|
if err != nil {
|
|
|
|
errorIf(err, "Unable to initialize hashReader.")
|
|
|
|
writeErrorResponse(w, toAPIErrorCode(err), r.URL)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-03-05 17:02:56 +01:00
|
|
|
if objectAPI.IsEncryptionSupported() {
|
|
|
|
if hasSSECustomerHeader(formValues) && !hasSuffix(object, slashSeparator) { // handle SSE-C requests
|
|
|
|
var reader io.Reader
|
|
|
|
var key []byte
|
|
|
|
key, err = ParseSSECustomerHeader(formValues)
|
|
|
|
if err != nil {
|
|
|
|
writeErrorResponse(w, toAPIErrorCode(err), r.URL)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
reader, err = newEncryptReader(hashReader, key, metadata)
|
|
|
|
if err != nil {
|
|
|
|
writeErrorResponse(w, toAPIErrorCode(err), r.URL)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
info := ObjectInfo{Size: fileSize}
|
|
|
|
hashReader, err = hash.NewReader(reader, info.EncryptedSize(), "", "") // do not try to verify encrypted content
|
|
|
|
if err != nil {
|
|
|
|
writeErrorResponse(w, toAPIErrorCode(err), r.URL)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-14 12:01:47 -07:00
|
|
|
objInfo, err := objectAPI.PutObject(ctx, bucket, object, hashReader, metadata)
|
2016-02-04 12:52:25 -08:00
|
|
|
if err != nil {
|
2017-01-06 00:37:00 -08:00
|
|
|
writeErrorResponse(w, toAPIErrorCode(err), r.URL)
|
2015-10-01 23:51:17 -07:00
|
|
|
return
|
|
|
|
}
|
2017-03-13 14:41:13 -07:00
|
|
|
|
2018-03-02 15:23:04 -08:00
|
|
|
location := getObjectLocation(r, globalDomainName, bucket, object)
|
2017-05-14 12:05:51 -07:00
|
|
|
w.Header().Set("ETag", `"`+objInfo.ETag+`"`)
|
2017-09-24 16:43:21 -07:00
|
|
|
w.Header().Set("Location", location)
|
2016-08-04 22:01:58 -07:00
|
|
|
|
2017-03-23 07:14:35 +05:30
|
|
|
// Get host and port from Request.RemoteAddr.
|
|
|
|
host, port, err := net.SplitHostPort(r.RemoteAddr)
|
|
|
|
if err != nil {
|
|
|
|
host, port = "", ""
|
|
|
|
}
|
|
|
|
|
2017-03-13 14:41:13 -07:00
|
|
|
// Notify object created event.
|
2018-03-16 01:33:41 +05:30
|
|
|
defer sendEvent(eventArgs{
|
|
|
|
EventName: event.ObjectCreatedPost,
|
|
|
|
BucketName: objInfo.Bucket,
|
|
|
|
Object: objInfo,
|
|
|
|
ReqParams: extractReqParams(r),
|
|
|
|
UserAgent: r.UserAgent(),
|
|
|
|
Host: host,
|
|
|
|
Port: port,
|
2017-03-13 14:41:13 -07:00
|
|
|
})
|
|
|
|
|
|
|
|
if successRedirect != "" {
|
|
|
|
// Replace raw query params..
|
|
|
|
redirectURL.RawQuery = getRedirectPostRawQuery(objInfo)
|
|
|
|
writeRedirectSeeOther(w, redirectURL.String())
|
|
|
|
return
|
|
|
|
}
|
2016-07-23 22:51:12 -07:00
|
|
|
|
2017-03-13 14:41:13 -07:00
|
|
|
// Decide what http response to send depending on success_action_status parameter
|
|
|
|
switch successStatus {
|
|
|
|
case "201":
|
|
|
|
resp := encodeResponse(PostResponse{
|
|
|
|
Bucket: objInfo.Bucket,
|
|
|
|
Key: objInfo.Name,
|
2017-05-14 12:05:51 -07:00
|
|
|
ETag: `"` + objInfo.ETag + `"`,
|
2017-09-24 16:43:21 -07:00
|
|
|
Location: location,
|
2017-03-13 14:41:13 -07:00
|
|
|
})
|
|
|
|
writeResponse(w, http.StatusCreated, resp, "application/xml")
|
|
|
|
case "200":
|
|
|
|
writeSuccessResponseHeadersOnly(w)
|
|
|
|
default:
|
2016-12-18 22:39:56 +01:00
|
|
|
writeSuccessNoContent(w)
|
|
|
|
}
|
2015-10-01 23:51:17 -07:00
|
|
|
}
|
|
|
|
|
2015-06-30 20:15:48 -07:00
|
|
|
// HeadBucketHandler - HEAD Bucket
|
2015-06-30 14:42:29 -07:00
|
|
|
// ----------
|
|
|
|
// This operation is useful to determine if a bucket exists.
|
|
|
|
// The operation returns a 200 OK if the bucket exists and you
|
|
|
|
// have permission to access it. Otherwise, the operation might
|
|
|
|
// return responses such as 404 Not Found and 403 Forbidden.
|
2016-04-12 12:45:15 -07:00
|
|
|
func (api objectAPIHandlers) HeadBucketHandler(w http.ResponseWriter, r *http.Request) {
|
2018-03-14 12:01:47 -07:00
|
|
|
ctx := newContext(r, "HeadBucket")
|
|
|
|
|
2016-02-15 17:42:39 -08:00
|
|
|
vars := mux.Vars(r)
|
2015-06-30 14:42:29 -07:00
|
|
|
bucket := vars["bucket"]
|
2015-07-02 20:31:22 -07:00
|
|
|
|
2016-08-10 18:47:49 -07:00
|
|
|
objectAPI := api.ObjectAPI()
|
|
|
|
if objectAPI == nil {
|
2017-01-06 00:37:00 -08:00
|
|
|
writeErrorResponseHeadersOnly(w, ErrServerNotInitialized)
|
2016-08-10 18:47:49 -07:00
|
|
|
return
|
|
|
|
}
|
2016-11-21 13:51:05 -08:00
|
|
|
|
2017-11-29 13:12:47 -08:00
|
|
|
if s3Error := checkRequestAuthType(r, bucket, "s3:ListBucket", globalServerConfig.GetRegion()); s3Error != ErrNone {
|
2017-01-06 00:37:00 -08:00
|
|
|
writeErrorResponseHeadersOnly(w, s3Error)
|
accessPolicy: Implement Put, Get, Delete access policy.
This patch implements Get,Put,Delete bucket policies
Supporting - http://docs.aws.amazon.com/AmazonS3/latest/dev/access-policy-language-overview.html
Currently supports following actions.
"*": true,
"s3:*": true,
"s3:GetObject": true,
"s3:ListBucket": true,
"s3:PutObject": true,
"s3:CreateBucket": true,
"s3:GetBucketLocation": true,
"s3:DeleteBucket": true,
"s3:DeleteObject": true,
"s3:AbortMultipartUpload": true,
"s3:ListBucketMultipartUploads": true,
"s3:ListMultipartUploadParts": true,
following conditions for "StringEquals" and "StringNotEquals"
"s3:prefix", "s3:max-keys"
2016-02-03 16:46:56 -08:00
|
|
|
return
|
2016-02-04 12:52:25 -08:00
|
|
|
}
|
2018-03-28 14:14:06 -07:00
|
|
|
getBucketInfo := objectAPI.GetBucketInfo
|
|
|
|
if api.CacheAPI() != nil {
|
|
|
|
getBucketInfo = api.CacheAPI().GetBucketInfo
|
|
|
|
}
|
|
|
|
if _, err := getBucketInfo(ctx, bucket); err != nil {
|
2017-01-06 00:37:00 -08:00
|
|
|
writeErrorResponseHeadersOnly(w, toAPIErrorCode(err))
|
2015-08-03 16:17:21 -07:00
|
|
|
return
|
|
|
|
}
|
2017-01-06 00:37:00 -08:00
|
|
|
|
|
|
|
writeSuccessResponseHeadersOnly(w)
|
2015-06-30 14:42:29 -07:00
|
|
|
}
|
2015-10-16 11:26:01 -07:00
|
|
|
|
|
|
|
// DeleteBucketHandler - Delete bucket
|
2016-04-12 12:45:15 -07:00
|
|
|
func (api objectAPIHandlers) DeleteBucketHandler(w http.ResponseWriter, r *http.Request) {
|
2018-03-14 12:01:47 -07:00
|
|
|
ctx := newContext(r, "DeleteBucket")
|
|
|
|
|
2016-08-10 18:47:49 -07:00
|
|
|
objectAPI := api.ObjectAPI()
|
|
|
|
if objectAPI == nil {
|
2017-01-06 00:37:00 -08:00
|
|
|
writeErrorResponse(w, ErrServerNotInitialized, r.URL)
|
2016-08-10 18:47:49 -07:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2016-11-21 13:51:05 -08:00
|
|
|
// DeleteBucket does not have any bucket action.
|
2017-11-29 13:12:47 -08:00
|
|
|
if s3Error := checkRequestAuthType(r, "", "", globalServerConfig.GetRegion()); s3Error != ErrNone {
|
2017-01-06 00:37:00 -08:00
|
|
|
writeErrorResponse(w, s3Error, r.URL)
|
2016-02-15 17:42:39 -08:00
|
|
|
return
|
2016-02-04 12:52:25 -08:00
|
|
|
}
|
|
|
|
|
2016-07-18 23:56:27 -07:00
|
|
|
vars := mux.Vars(r)
|
|
|
|
bucket := vars["bucket"]
|
2018-03-28 14:14:06 -07:00
|
|
|
deleteBucket := objectAPI.DeleteBucket
|
|
|
|
if api.CacheAPI() != nil {
|
|
|
|
deleteBucket = api.CacheAPI().DeleteBucket
|
|
|
|
}
|
2016-07-23 22:51:12 -07:00
|
|
|
// Attempt to delete bucket.
|
2018-03-28 14:14:06 -07:00
|
|
|
if err := deleteBucket(ctx, bucket); err != nil {
|
2017-01-06 00:37:00 -08:00
|
|
|
writeErrorResponse(w, toAPIErrorCode(err), r.URL)
|
2015-10-16 11:26:01 -07:00
|
|
|
return
|
|
|
|
}
|
accessPolicy: Implement Put, Get, Delete access policy.
This patch implements Get,Put,Delete bucket policies
Supporting - http://docs.aws.amazon.com/AmazonS3/latest/dev/access-policy-language-overview.html
Currently supports following actions.
"*": true,
"s3:*": true,
"s3:GetObject": true,
"s3:ListBucket": true,
"s3:PutObject": true,
"s3:CreateBucket": true,
"s3:GetBucketLocation": true,
"s3:DeleteBucket": true,
"s3:DeleteObject": true,
"s3:AbortMultipartUpload": true,
"s3:ListBucketMultipartUploads": true,
"s3:ListMultipartUploadParts": true,
following conditions for "StringEquals" and "StringNotEquals"
"s3:prefix", "s3:max-keys"
2016-02-03 16:46:56 -08:00
|
|
|
|
2018-03-16 01:33:41 +05:30
|
|
|
// Notify all peers (including self) to update in-memory state
|
|
|
|
for addr, err := range globalNotificationSys.UpdateBucketPolicy(bucket) {
|
|
|
|
errorIf(err, "unable to update policy change in remote peer %v", addr)
|
|
|
|
}
|
|
|
|
|
|
|
|
globalNotificationSys.RemoveNotification(bucket)
|
|
|
|
for addr, err := range globalNotificationSys.DeleteBucket(bucket) {
|
|
|
|
errorIf(err, "unable to delete bucket in remote peer %v", addr)
|
|
|
|
}
|
|
|
|
|
accessPolicy: Implement Put, Get, Delete access policy.
This patch implements Get,Put,Delete bucket policies
Supporting - http://docs.aws.amazon.com/AmazonS3/latest/dev/access-policy-language-overview.html
Currently supports following actions.
"*": true,
"s3:*": true,
"s3:GetObject": true,
"s3:ListBucket": true,
"s3:PutObject": true,
"s3:CreateBucket": true,
"s3:GetBucketLocation": true,
"s3:DeleteBucket": true,
"s3:DeleteObject": true,
"s3:AbortMultipartUpload": true,
"s3:ListBucketMultipartUploads": true,
"s3:ListMultipartUploadParts": true,
following conditions for "StringEquals" and "StringNotEquals"
"s3:prefix", "s3:max-keys"
2016-02-03 16:46:56 -08:00
|
|
|
// Write success response.
|
2015-10-16 20:02:37 -07:00
|
|
|
writeSuccessNoContent(w)
|
2015-10-16 11:26:01 -07:00
|
|
|
}
|