mirror of
https://github.com/minio/minio.git
synced 2025-02-09 04:38:09 -05:00
Merge pull request #908 from harshavardhana/bucket-acl-support
Implement Bucket ACL support
This commit is contained in:
commit
f825a32b53
@ -52,9 +52,3 @@ func getObjectResources(values url.Values) (v fs.ObjectResourcesMetadata) {
|
|||||||
v.EncodingType = values.Get("encoding-type")
|
v.EncodingType = values.Get("encoding-type")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if req quere values carry uploads resource
|
|
||||||
func isRequestUploads(values url.Values) bool {
|
|
||||||
_, ok := values["uploads"]
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
@ -26,8 +26,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/minio/minio/pkg/fs"
|
|
||||||
"github.com/minio/minio-xl/pkg/probe"
|
"github.com/minio/minio-xl/pkg/probe"
|
||||||
|
"github.com/minio/minio/pkg/fs"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -31,10 +31,23 @@ import (
|
|||||||
// -------------------------
|
// -------------------------
|
||||||
// This operation lists in-progress multipart uploads. An in-progress
|
// This operation lists in-progress multipart uploads. An in-progress
|
||||||
// multipart upload is a multipart upload that has been initiated,
|
// multipart upload is a multipart upload that has been initiated,
|
||||||
// using the Initiate Multipart Upload request, but has not yet been completed or aborted.
|
// using the Initiate Multipart Upload request, but has not yet been
|
||||||
// This operation returns at most 1,000 multipart uploads in the response.
|
// completed or aborted. This operation returns at most 1,000 multipart
|
||||||
|
// uploads in the response.
|
||||||
//
|
//
|
||||||
func (api API) ListMultipartUploadsHandler(w http.ResponseWriter, req *http.Request) {
|
func (api API) ListMultipartUploadsHandler(w http.ResponseWriter, req *http.Request) {
|
||||||
|
vars := mux.Vars(req)
|
||||||
|
bucket := vars["bucket"]
|
||||||
|
|
||||||
|
if !api.Anonymous {
|
||||||
|
if isRequestRequiresACLCheck(req) {
|
||||||
|
if api.Filesystem.IsPrivateBucket(bucket) {
|
||||||
|
writeErrorResponse(w, req, AccessDenied, req.URL.Path)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
resources := getBucketMultipartResources(req.URL.Query())
|
resources := getBucketMultipartResources(req.URL.Query())
|
||||||
if resources.MaxUploads < 0 {
|
if resources.MaxUploads < 0 {
|
||||||
writeErrorResponse(w, req, InvalidMaxUploads, req.URL.Path)
|
writeErrorResponse(w, req, InvalidMaxUploads, req.URL.Path)
|
||||||
@ -44,9 +57,6 @@ func (api API) ListMultipartUploadsHandler(w http.ResponseWriter, req *http.Requ
|
|||||||
resources.MaxUploads = maxObjectList
|
resources.MaxUploads = maxObjectList
|
||||||
}
|
}
|
||||||
|
|
||||||
vars := mux.Vars(req)
|
|
||||||
bucket := vars["bucket"]
|
|
||||||
|
|
||||||
resources, err := api.Filesystem.ListMultipartUploads(bucket, resources)
|
resources, err := api.Filesystem.ListMultipartUploads(bucket, resources)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorIf(err.Trace(), "ListMultipartUploads failed.", nil)
|
errorIf(err.Trace(), "ListMultipartUploads failed.", nil)
|
||||||
@ -74,11 +84,17 @@ func (api API) ListMultipartUploadsHandler(w http.ResponseWriter, req *http.Requ
|
|||||||
// criteria to return a subset of the objects in a bucket.
|
// criteria to return a subset of the objects in a bucket.
|
||||||
//
|
//
|
||||||
func (api API) ListObjectsHandler(w http.ResponseWriter, req *http.Request) {
|
func (api API) ListObjectsHandler(w http.ResponseWriter, req *http.Request) {
|
||||||
if isRequestUploads(req.URL.Query()) {
|
vars := mux.Vars(req)
|
||||||
api.ListMultipartUploadsHandler(w, req)
|
bucket := vars["bucket"]
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if !api.Anonymous {
|
||||||
|
if isRequestRequiresACLCheck(req) {
|
||||||
|
if api.Filesystem.IsPrivateBucket(bucket) {
|
||||||
|
writeErrorResponse(w, req, AccessDenied, req.URL.Path)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
resources := getBucketResources(req.URL.Query())
|
resources := getBucketResources(req.URL.Query())
|
||||||
if resources.Maxkeys < 0 {
|
if resources.Maxkeys < 0 {
|
||||||
writeErrorResponse(w, req, InvalidMaxKeys, req.URL.Path)
|
writeErrorResponse(w, req, InvalidMaxKeys, req.URL.Path)
|
||||||
@ -88,9 +104,6 @@ func (api API) ListObjectsHandler(w http.ResponseWriter, req *http.Request) {
|
|||||||
resources.Maxkeys = maxObjectList
|
resources.Maxkeys = maxObjectList
|
||||||
}
|
}
|
||||||
|
|
||||||
vars := mux.Vars(req)
|
|
||||||
bucket := vars["bucket"]
|
|
||||||
|
|
||||||
objects, resources, err := api.Filesystem.ListObjects(bucket, resources)
|
objects, resources, err := api.Filesystem.ListObjects(bucket, resources)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// generate response
|
// generate response
|
||||||
@ -122,6 +135,12 @@ func (api API) ListObjectsHandler(w http.ResponseWriter, req *http.Request) {
|
|||||||
// This implementation of the GET operation returns a list of all buckets
|
// This implementation of the GET operation returns a list of all buckets
|
||||||
// owned by the authenticated sender of the request.
|
// owned by the authenticated sender of the request.
|
||||||
func (api API) ListBucketsHandler(w http.ResponseWriter, req *http.Request) {
|
func (api API) ListBucketsHandler(w http.ResponseWriter, req *http.Request) {
|
||||||
|
if !api.Anonymous {
|
||||||
|
if isRequestRequiresACLCheck(req) {
|
||||||
|
writeErrorResponse(w, req, AccessDenied, req.URL.Path)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
buckets, err := api.Filesystem.ListBuckets()
|
buckets, err := api.Filesystem.ListBuckets()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// generate response
|
// generate response
|
||||||
@ -141,6 +160,16 @@ func (api API) ListBucketsHandler(w http.ResponseWriter, req *http.Request) {
|
|||||||
// ----------
|
// ----------
|
||||||
// This implementation of the PUT operation creates a new bucket for authenticated request
|
// This implementation of the PUT operation creates a new bucket for authenticated request
|
||||||
func (api API) PutBucketHandler(w http.ResponseWriter, req *http.Request) {
|
func (api API) PutBucketHandler(w http.ResponseWriter, req *http.Request) {
|
||||||
|
vars := mux.Vars(req)
|
||||||
|
bucket := vars["bucket"]
|
||||||
|
|
||||||
|
if !api.Anonymous {
|
||||||
|
if isRequestRequiresACLCheck(req) {
|
||||||
|
writeErrorResponse(w, req, AccessDenied, req.URL.Path)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// read from 'x-amz-acl'
|
// read from 'x-amz-acl'
|
||||||
aclType := getACLType(req)
|
aclType := getACLType(req)
|
||||||
if aclType == unsupportedACLType {
|
if aclType == unsupportedACLType {
|
||||||
@ -148,13 +177,10 @@ func (api API) PutBucketHandler(w http.ResponseWriter, req *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
vars := mux.Vars(req)
|
|
||||||
bucket := vars["bucket"]
|
|
||||||
|
|
||||||
var signature *fs.Signature
|
var signature *fs.Signature
|
||||||
if !api.Anonymous {
|
if !api.Anonymous {
|
||||||
if _, ok := req.Header["Authorization"]; ok {
|
// Init signature V4 verification
|
||||||
// Init signature V4 verification
|
if isRequestSignatureV4(req) {
|
||||||
var err *probe.Error
|
var err *probe.Error
|
||||||
signature, err = initSignatureV4(req)
|
signature, err = initSignatureV4(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -295,16 +321,22 @@ func (api API) PostPolicyBucketHandler(w http.ResponseWriter, req *http.Request)
|
|||||||
// ----------
|
// ----------
|
||||||
// This implementation of the PUT operation modifies the bucketACL for authenticated request
|
// This implementation of the PUT operation modifies the bucketACL for authenticated request
|
||||||
func (api API) PutBucketACLHandler(w http.ResponseWriter, req *http.Request) {
|
func (api API) PutBucketACLHandler(w http.ResponseWriter, req *http.Request) {
|
||||||
|
vars := mux.Vars(req)
|
||||||
|
bucket := vars["bucket"]
|
||||||
|
|
||||||
|
if !api.Anonymous {
|
||||||
|
if isRequestRequiresACLCheck(req) {
|
||||||
|
writeErrorResponse(w, req, AccessDenied, req.URL.Path)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// read from 'x-amz-acl'
|
// read from 'x-amz-acl'
|
||||||
aclType := getACLType(req)
|
aclType := getACLType(req)
|
||||||
if aclType == unsupportedACLType {
|
if aclType == unsupportedACLType {
|
||||||
writeErrorResponse(w, req, NotImplemented, req.URL.Path)
|
writeErrorResponse(w, req, NotImplemented, req.URL.Path)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
vars := mux.Vars(req)
|
|
||||||
bucket := vars["bucket"]
|
|
||||||
|
|
||||||
err := api.Filesystem.SetBucketMetadata(bucket, map[string]string{"acl": getACLTypeString(aclType)})
|
err := api.Filesystem.SetBucketMetadata(bucket, map[string]string{"acl": getACLTypeString(aclType)})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorIf(err.Trace(), "PutBucketACL failed.", nil)
|
errorIf(err.Trace(), "PutBucketACL failed.", nil)
|
||||||
@ -331,6 +363,13 @@ func (api API) GetBucketACLHandler(w http.ResponseWriter, req *http.Request) {
|
|||||||
vars := mux.Vars(req)
|
vars := mux.Vars(req)
|
||||||
bucket := vars["bucket"]
|
bucket := vars["bucket"]
|
||||||
|
|
||||||
|
if !api.Anonymous {
|
||||||
|
if isRequestRequiresACLCheck(req) {
|
||||||
|
writeErrorResponse(w, req, AccessDenied, req.URL.Path)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bucketMetadata, err := api.Filesystem.GetBucketMetadata(bucket)
|
bucketMetadata, err := api.Filesystem.GetBucketMetadata(bucket)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorIf(err.Trace(), "GetBucketMetadata failed.", nil)
|
errorIf(err.Trace(), "GetBucketMetadata failed.", nil)
|
||||||
@ -363,6 +402,15 @@ func (api API) HeadBucketHandler(w http.ResponseWriter, req *http.Request) {
|
|||||||
vars := mux.Vars(req)
|
vars := mux.Vars(req)
|
||||||
bucket := vars["bucket"]
|
bucket := vars["bucket"]
|
||||||
|
|
||||||
|
if !api.Anonymous {
|
||||||
|
if isRequestRequiresACLCheck(req) {
|
||||||
|
if api.Filesystem.IsPrivateBucket(bucket) {
|
||||||
|
writeErrorResponse(w, req, AccessDenied, req.URL.Path)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_, err := api.Filesystem.GetBucketMetadata(bucket)
|
_, err := api.Filesystem.GetBucketMetadata(bucket)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorIf(err.Trace(), "GetBucketMetadata failed.", nil)
|
errorIf(err.Trace(), "GetBucketMetadata failed.", nil)
|
||||||
@ -384,6 +432,13 @@ func (api API) DeleteBucketHandler(w http.ResponseWriter, req *http.Request) {
|
|||||||
vars := mux.Vars(req)
|
vars := mux.Vars(req)
|
||||||
bucket := vars["bucket"]
|
bucket := vars["bucket"]
|
||||||
|
|
||||||
|
if !api.Anonymous {
|
||||||
|
if isRequestRequiresACLCheck(req) {
|
||||||
|
writeErrorResponse(w, req, AccessDenied, req.URL.Path)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
err := api.Filesystem.DeleteBucket(bucket)
|
err := api.Filesystem.DeleteBucket(bucket)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorIf(err.Trace(), "DeleteBucket failed.", nil)
|
errorIf(err.Trace(), "DeleteBucket failed.", nil)
|
||||||
|
@ -22,8 +22,8 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/minio/minio/pkg/fs"
|
|
||||||
"github.com/minio/minio-xl/pkg/probe"
|
"github.com/minio/minio-xl/pkg/probe"
|
||||||
|
"github.com/minio/minio/pkg/fs"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -21,8 +21,8 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/minio/minio/pkg/fs"
|
|
||||||
"github.com/minio/minio-xl/pkg/probe"
|
"github.com/minio/minio-xl/pkg/probe"
|
||||||
|
"github.com/minio/minio/pkg/fs"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -39,6 +39,15 @@ func (api API) GetObjectHandler(w http.ResponseWriter, req *http.Request) {
|
|||||||
bucket = vars["bucket"]
|
bucket = vars["bucket"]
|
||||||
object = vars["object"]
|
object = vars["object"]
|
||||||
|
|
||||||
|
if !api.Anonymous {
|
||||||
|
if isRequestRequiresACLCheck(req) {
|
||||||
|
if api.Filesystem.IsPrivateBucket(bucket) {
|
||||||
|
writeErrorResponse(w, req, AccessDenied, req.URL.Path)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
metadata, err := api.Filesystem.GetObjectMetadata(bucket, object)
|
metadata, err := api.Filesystem.GetObjectMetadata(bucket, object)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorIf(err.Trace(), "GetObject failed.", nil)
|
errorIf(err.Trace(), "GetObject failed.", nil)
|
||||||
@ -78,6 +87,15 @@ func (api API) HeadObjectHandler(w http.ResponseWriter, req *http.Request) {
|
|||||||
bucket = vars["bucket"]
|
bucket = vars["bucket"]
|
||||||
object = vars["object"]
|
object = vars["object"]
|
||||||
|
|
||||||
|
if !api.Anonymous {
|
||||||
|
if isRequestRequiresACLCheck(req) {
|
||||||
|
if api.Filesystem.IsPrivateBucket(bucket) {
|
||||||
|
writeErrorResponse(w, req, AccessDenied, req.URL.Path)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
metadata, err := api.Filesystem.GetObjectMetadata(bucket, object)
|
metadata, err := api.Filesystem.GetObjectMetadata(bucket, object)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
switch err.ToGoError().(type) {
|
switch err.ToGoError().(type) {
|
||||||
@ -107,6 +125,15 @@ func (api API) PutObjectHandler(w http.ResponseWriter, req *http.Request) {
|
|||||||
bucket = vars["bucket"]
|
bucket = vars["bucket"]
|
||||||
object = vars["object"]
|
object = vars["object"]
|
||||||
|
|
||||||
|
if !api.Anonymous {
|
||||||
|
if isRequestRequiresACLCheck(req) {
|
||||||
|
if api.Filesystem.IsPrivateBucket(bucket) {
|
||||||
|
writeErrorResponse(w, req, AccessDenied, req.URL.Path)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// get Content-MD5 sent by client and verify if valid
|
// get Content-MD5 sent by client and verify if valid
|
||||||
md5 := req.Header.Get("Content-MD5")
|
md5 := req.Header.Get("Content-MD5")
|
||||||
if !isValidMD5(md5) {
|
if !isValidMD5(md5) {
|
||||||
@ -136,7 +163,7 @@ func (api API) PutObjectHandler(w http.ResponseWriter, req *http.Request) {
|
|||||||
|
|
||||||
var signature *fs.Signature
|
var signature *fs.Signature
|
||||||
if !api.Anonymous {
|
if !api.Anonymous {
|
||||||
if _, ok := req.Header["Authorization"]; ok {
|
if isRequestSignatureV4(req) {
|
||||||
// Init signature V4 verification
|
// Init signature V4 verification
|
||||||
var err *probe.Error
|
var err *probe.Error
|
||||||
signature, err = initSignatureV4(req)
|
signature, err = initSignatureV4(req)
|
||||||
@ -181,16 +208,19 @@ func (api API) PutObjectHandler(w http.ResponseWriter, req *http.Request) {
|
|||||||
|
|
||||||
// NewMultipartUploadHandler - New multipart upload
|
// NewMultipartUploadHandler - New multipart upload
|
||||||
func (api API) NewMultipartUploadHandler(w http.ResponseWriter, req *http.Request) {
|
func (api API) NewMultipartUploadHandler(w http.ResponseWriter, req *http.Request) {
|
||||||
if !isRequestUploads(req.URL.Query()) {
|
|
||||||
writeErrorResponse(w, req, MethodNotAllowed, req.URL.Path)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var object, bucket string
|
var object, bucket string
|
||||||
vars := mux.Vars(req)
|
vars := mux.Vars(req)
|
||||||
bucket = vars["bucket"]
|
bucket = vars["bucket"]
|
||||||
object = vars["object"]
|
object = vars["object"]
|
||||||
|
|
||||||
|
if !api.Anonymous {
|
||||||
|
// Unauthorized multipart uploads are not supported
|
||||||
|
if isRequestRequiresACLCheck(req) {
|
||||||
|
writeErrorResponse(w, req, AccessDenied, req.URL.Path)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
uploadID, err := api.Filesystem.NewMultipartUpload(bucket, object)
|
uploadID, err := api.Filesystem.NewMultipartUpload(bucket, object)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorIf(err.Trace(), "NewMultipartUpload failed.", nil)
|
errorIf(err.Trace(), "NewMultipartUpload failed.", nil)
|
||||||
@ -219,11 +249,15 @@ func (api API) NewMultipartUploadHandler(w http.ResponseWriter, req *http.Reques
|
|||||||
|
|
||||||
// PutObjectPartHandler - Upload part
|
// PutObjectPartHandler - Upload part
|
||||||
func (api API) PutObjectPartHandler(w http.ResponseWriter, req *http.Request) {
|
func (api API) PutObjectPartHandler(w http.ResponseWriter, req *http.Request) {
|
||||||
// get Content-MD5 sent by client and verify if valid
|
vars := mux.Vars(req)
|
||||||
md5 := req.Header.Get("Content-MD5")
|
bucket := vars["bucket"]
|
||||||
if !isValidMD5(md5) {
|
object := vars["object"]
|
||||||
writeErrorResponse(w, req, InvalidDigest, req.URL.Path)
|
|
||||||
return
|
if !api.Anonymous {
|
||||||
|
if isRequestRequiresACLCheck(req) {
|
||||||
|
writeErrorResponse(w, req, AccessDenied, req.URL.Path)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// if Content-Length missing, throw away
|
/// if Content-Length missing, throw away
|
||||||
@ -233,6 +267,13 @@ func (api API) PutObjectPartHandler(w http.ResponseWriter, req *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get Content-MD5 sent by client and verify if valid
|
||||||
|
md5 := req.Header.Get("Content-MD5")
|
||||||
|
if !isValidMD5(md5) {
|
||||||
|
writeErrorResponse(w, req, InvalidDigest, req.URL.Path)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
/// maximum Upload size for multipart objects in a single operation
|
/// maximum Upload size for multipart objects in a single operation
|
||||||
if isMaxObjectSize(size) {
|
if isMaxObjectSize(size) {
|
||||||
writeErrorResponse(w, req, EntityTooLarge, req.URL.Path)
|
writeErrorResponse(w, req, EntityTooLarge, req.URL.Path)
|
||||||
@ -249,10 +290,6 @@ func (api API) PutObjectPartHandler(w http.ResponseWriter, req *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vars := mux.Vars(req)
|
|
||||||
bucket := vars["bucket"]
|
|
||||||
object := vars["object"]
|
|
||||||
|
|
||||||
uploadID := req.URL.Query().Get("uploadId")
|
uploadID := req.URL.Query().Get("uploadId")
|
||||||
partIDString := req.URL.Query().Get("partNumber")
|
partIDString := req.URL.Query().Get("partNumber")
|
||||||
|
|
||||||
@ -268,7 +305,7 @@ func (api API) PutObjectPartHandler(w http.ResponseWriter, req *http.Request) {
|
|||||||
|
|
||||||
var signature *fs.Signature
|
var signature *fs.Signature
|
||||||
if !api.Anonymous {
|
if !api.Anonymous {
|
||||||
if _, ok := req.Header["Authorization"]; ok {
|
if isRequestSignatureV4(req) {
|
||||||
// Init signature V4 verification
|
// Init signature V4 verification
|
||||||
var err *probe.Error
|
var err *probe.Error
|
||||||
signature, err = initSignatureV4(req)
|
signature, err = initSignatureV4(req)
|
||||||
@ -311,6 +348,13 @@ func (api API) AbortMultipartUploadHandler(w http.ResponseWriter, req *http.Requ
|
|||||||
bucket := vars["bucket"]
|
bucket := vars["bucket"]
|
||||||
object := vars["object"]
|
object := vars["object"]
|
||||||
|
|
||||||
|
if !api.Anonymous {
|
||||||
|
if isRequestRequiresACLCheck(req) {
|
||||||
|
writeErrorResponse(w, req, AccessDenied, req.URL.Path)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
objectResourcesMetadata := getObjectResources(req.URL.Query())
|
objectResourcesMetadata := getObjectResources(req.URL.Query())
|
||||||
|
|
||||||
err := api.Filesystem.AbortMultipartUpload(bucket, object, objectResourcesMetadata.UploadID)
|
err := api.Filesystem.AbortMultipartUpload(bucket, object, objectResourcesMetadata.UploadID)
|
||||||
@ -338,6 +382,17 @@ func (api API) AbortMultipartUploadHandler(w http.ResponseWriter, req *http.Requ
|
|||||||
|
|
||||||
// ListObjectPartsHandler - List object parts
|
// ListObjectPartsHandler - List object parts
|
||||||
func (api API) ListObjectPartsHandler(w http.ResponseWriter, req *http.Request) {
|
func (api API) ListObjectPartsHandler(w http.ResponseWriter, req *http.Request) {
|
||||||
|
vars := mux.Vars(req)
|
||||||
|
bucket := vars["bucket"]
|
||||||
|
object := vars["object"]
|
||||||
|
|
||||||
|
if !api.Anonymous {
|
||||||
|
if isRequestRequiresACLCheck(req) {
|
||||||
|
writeErrorResponse(w, req, AccessDenied, req.URL.Path)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
objectResourcesMetadata := getObjectResources(req.URL.Query())
|
objectResourcesMetadata := getObjectResources(req.URL.Query())
|
||||||
if objectResourcesMetadata.PartNumberMarker < 0 {
|
if objectResourcesMetadata.PartNumberMarker < 0 {
|
||||||
writeErrorResponse(w, req, InvalidPartNumberMarker, req.URL.Path)
|
writeErrorResponse(w, req, InvalidPartNumberMarker, req.URL.Path)
|
||||||
@ -351,10 +406,6 @@ func (api API) ListObjectPartsHandler(w http.ResponseWriter, req *http.Request)
|
|||||||
objectResourcesMetadata.MaxParts = maxPartsList
|
objectResourcesMetadata.MaxParts = maxPartsList
|
||||||
}
|
}
|
||||||
|
|
||||||
vars := mux.Vars(req)
|
|
||||||
bucket := vars["bucket"]
|
|
||||||
object := vars["object"]
|
|
||||||
|
|
||||||
objectResourcesMetadata, err := api.Filesystem.ListObjectParts(bucket, object, objectResourcesMetadata)
|
objectResourcesMetadata, err := api.Filesystem.ListObjectParts(bucket, object, objectResourcesMetadata)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorIf(err.Trace(), "ListObjectParts failed.", nil)
|
errorIf(err.Trace(), "ListObjectParts failed.", nil)
|
||||||
@ -388,11 +439,17 @@ func (api API) CompleteMultipartUploadHandler(w http.ResponseWriter, req *http.R
|
|||||||
bucket := vars["bucket"]
|
bucket := vars["bucket"]
|
||||||
object := vars["object"]
|
object := vars["object"]
|
||||||
|
|
||||||
objectResourcesMetadata := getObjectResources(req.URL.Query())
|
if !api.Anonymous {
|
||||||
|
if isRequestRequiresACLCheck(req) {
|
||||||
|
writeErrorResponse(w, req, AccessDenied, req.URL.Path)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
objectResourcesMetadata := getObjectResources(req.URL.Query())
|
||||||
var signature *fs.Signature
|
var signature *fs.Signature
|
||||||
if !api.Anonymous {
|
if !api.Anonymous {
|
||||||
if _, ok := req.Header["Authorization"]; ok {
|
if isRequestSignatureV4(req) {
|
||||||
// Init signature V4 verification
|
// Init signature V4 verification
|
||||||
var err *probe.Error
|
var err *probe.Error
|
||||||
signature, err = initSignatureV4(req)
|
signature, err = initSignatureV4(req)
|
||||||
@ -449,6 +506,15 @@ func (api API) DeleteObjectHandler(w http.ResponseWriter, req *http.Request) {
|
|||||||
bucket := vars["bucket"]
|
bucket := vars["bucket"]
|
||||||
object := vars["object"]
|
object := vars["object"]
|
||||||
|
|
||||||
|
if !api.Anonymous {
|
||||||
|
if isRequestRequiresACLCheck(req) {
|
||||||
|
if api.Filesystem.IsPrivateBucket(bucket) {
|
||||||
|
writeErrorResponse(w, req, AccessDenied, req.URL.Path)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
err := api.Filesystem.DeleteObject(bucket, object)
|
err := api.Filesystem.DeleteObject(bucket, object)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorIf(err.Trace(), "DeleteObject failed.", nil)
|
errorIf(err.Trace(), "DeleteObject failed.", nil)
|
||||||
|
45
pkg/fs/acl.go
Normal file
45
pkg/fs/acl.go
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
package fs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IsPrivateBucket - is private bucket
|
||||||
|
func (fs API) IsPrivateBucket(bucket string) bool {
|
||||||
|
fs.lock.Lock()
|
||||||
|
defer fs.lock.Unlock()
|
||||||
|
// get bucket path
|
||||||
|
bucketDir := filepath.Join(fs.path, bucket)
|
||||||
|
fi, err := os.Stat(bucketDir)
|
||||||
|
if err != nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return permToACL(fi.Mode()).IsPrivate()
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsPublicBucket - is public bucket
|
||||||
|
func (fs API) IsPublicBucket(bucket string) bool {
|
||||||
|
fs.lock.Lock()
|
||||||
|
defer fs.lock.Unlock()
|
||||||
|
// get bucket path
|
||||||
|
bucketDir := filepath.Join(fs.path, bucket)
|
||||||
|
fi, err := os.Stat(bucketDir)
|
||||||
|
if err != nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return permToACL(fi.Mode()).IsPublicReadWrite()
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsReadOnlyBucket - is read only bucket
|
||||||
|
func (fs API) IsReadOnlyBucket(bucket string) bool {
|
||||||
|
fs.lock.Lock()
|
||||||
|
defer fs.lock.Unlock()
|
||||||
|
// get bucket path
|
||||||
|
bucketDir := filepath.Join(fs.path, bucket)
|
||||||
|
fi, err := os.Stat(bucketDir)
|
||||||
|
if err != nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return permToACL(fi.Mode()).IsPublicRead()
|
||||||
|
}
|
@ -48,12 +48,10 @@ func (fs API) filterObjects(bucket string, content contentInfo, resources Bucket
|
|||||||
}
|
}
|
||||||
if metadata.Mode.IsDir() {
|
if metadata.Mode.IsDir() {
|
||||||
resources.CommonPrefixes = append(resources.CommonPrefixes, name+resources.Delimiter)
|
resources.CommonPrefixes = append(resources.CommonPrefixes, name+resources.Delimiter)
|
||||||
sortUnique(resources.CommonPrefixes)
|
|
||||||
return ObjectMetadata{}, resources, nil
|
return ObjectMetadata{}, resources, nil
|
||||||
}
|
}
|
||||||
case delimitedName != "":
|
case delimitedName != "":
|
||||||
resources.CommonPrefixes = append(resources.CommonPrefixes, resources.Prefix+delimitedName)
|
resources.CommonPrefixes = append(resources.CommonPrefixes, resources.Prefix+delimitedName)
|
||||||
sortUnique(resources.CommonPrefixes)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Delimiter present and Prefix is absent
|
// Delimiter present and Prefix is absent
|
||||||
@ -72,12 +70,10 @@ func (fs API) filterObjects(bucket string, content contentInfo, resources Bucket
|
|||||||
}
|
}
|
||||||
if metadata.Mode.IsDir() {
|
if metadata.Mode.IsDir() {
|
||||||
resources.CommonPrefixes = append(resources.CommonPrefixes, name+resources.Delimiter)
|
resources.CommonPrefixes = append(resources.CommonPrefixes, name+resources.Delimiter)
|
||||||
sortUnique(resources.CommonPrefixes)
|
|
||||||
return ObjectMetadata{}, resources, nil
|
return ObjectMetadata{}, resources, nil
|
||||||
}
|
}
|
||||||
case delimitedName != "":
|
case delimitedName != "":
|
||||||
resources.CommonPrefixes = append(resources.CommonPrefixes, delimitedName)
|
resources.CommonPrefixes = append(resources.CommonPrefixes, delimitedName)
|
||||||
sortUnique(resources.CommonPrefixes)
|
|
||||||
}
|
}
|
||||||
// Delimiter is absent and only Prefix is present
|
// Delimiter is absent and only Prefix is present
|
||||||
case resources.Delimiter == "" && resources.Prefix != "":
|
case resources.Delimiter == "" && resources.Prefix != "":
|
||||||
@ -94,6 +90,6 @@ func (fs API) filterObjects(bucket string, content contentInfo, resources Bucket
|
|||||||
return ObjectMetadata{}, resources, err.Trace()
|
return ObjectMetadata{}, resources, err.Trace()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
sortUnique(resources.CommonPrefixes)
|
||||||
return metadata, resources, nil
|
return metadata, resources, nil
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,11 @@ type CloudStorage interface {
|
|||||||
CreateObject(bucket, object, md5sum string, size int64, data io.Reader, signature *Signature) (ObjectMetadata, *probe.Error)
|
CreateObject(bucket, object, md5sum string, size int64, data io.Reader, signature *Signature) (ObjectMetadata, *probe.Error)
|
||||||
DeleteObject(bucket, object string) *probe.Error
|
DeleteObject(bucket, object string) *probe.Error
|
||||||
|
|
||||||
|
// Multipart API
|
||||||
Multipart
|
Multipart
|
||||||
|
|
||||||
|
// ACL API
|
||||||
|
ACL
|
||||||
}
|
}
|
||||||
|
|
||||||
// Multipart API
|
// Multipart API
|
||||||
@ -53,3 +57,10 @@ type Multipart interface {
|
|||||||
ListMultipartUploads(bucket string, resources BucketMultipartResourcesMetadata) (BucketMultipartResourcesMetadata, *probe.Error)
|
ListMultipartUploads(bucket string, resources BucketMultipartResourcesMetadata) (BucketMultipartResourcesMetadata, *probe.Error)
|
||||||
ListObjectParts(bucket, object string, objectResources ObjectResourcesMetadata) (ObjectResourcesMetadata, *probe.Error)
|
ListObjectParts(bucket, object string, objectResources ObjectResourcesMetadata) (ObjectResourcesMetadata, *probe.Error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ACL API
|
||||||
|
type ACL interface {
|
||||||
|
IsPublicBucket(bucket string) bool
|
||||||
|
IsPrivateBucket(bucket string) bool
|
||||||
|
IsReadOnlyBucket(bucket string) bool
|
||||||
|
}
|
||||||
|
@ -27,6 +27,7 @@ import (
|
|||||||
func registerAPI(mux *router.Router, a API) {
|
func registerAPI(mux *router.Router, a API) {
|
||||||
mux.HandleFunc("/", a.ListBucketsHandler).Methods("GET")
|
mux.HandleFunc("/", a.ListBucketsHandler).Methods("GET")
|
||||||
mux.HandleFunc("/{bucket}", a.GetBucketACLHandler).Queries("acl", "").Methods("GET")
|
mux.HandleFunc("/{bucket}", a.GetBucketACLHandler).Queries("acl", "").Methods("GET")
|
||||||
|
mux.HandleFunc("/{bucket}", a.ListMultipartUploadsHandler).Queries("uploads", "").Methods("GET")
|
||||||
mux.HandleFunc("/{bucket}", a.ListObjectsHandler).Methods("GET")
|
mux.HandleFunc("/{bucket}", a.ListObjectsHandler).Methods("GET")
|
||||||
mux.HandleFunc("/{bucket}", a.PutBucketACLHandler).Queries("acl", "").Methods("PUT")
|
mux.HandleFunc("/{bucket}", a.PutBucketACLHandler).Queries("acl", "").Methods("PUT")
|
||||||
mux.HandleFunc("/{bucket}", a.PutBucketHandler).Methods("PUT")
|
mux.HandleFunc("/{bucket}", a.PutBucketHandler).Methods("PUT")
|
||||||
@ -36,15 +37,12 @@ func registerAPI(mux *router.Router, a API) {
|
|||||||
mux.HandleFunc("/{bucket}/{object:.*}", a.PutObjectPartHandler).Queries("partNumber", "{partNumber:[0-9]+}", "uploadId", "{uploadId:.*}").Methods("PUT")
|
mux.HandleFunc("/{bucket}/{object:.*}", a.PutObjectPartHandler).Queries("partNumber", "{partNumber:[0-9]+}", "uploadId", "{uploadId:.*}").Methods("PUT")
|
||||||
mux.HandleFunc("/{bucket}/{object:.*}", a.ListObjectPartsHandler).Queries("uploadId", "{uploadId:.*}").Methods("GET")
|
mux.HandleFunc("/{bucket}/{object:.*}", a.ListObjectPartsHandler).Queries("uploadId", "{uploadId:.*}").Methods("GET")
|
||||||
mux.HandleFunc("/{bucket}/{object:.*}", a.CompleteMultipartUploadHandler).Queries("uploadId", "{uploadId:.*}").Methods("POST")
|
mux.HandleFunc("/{bucket}/{object:.*}", a.CompleteMultipartUploadHandler).Queries("uploadId", "{uploadId:.*}").Methods("POST")
|
||||||
mux.HandleFunc("/{bucket}/{object:.*}", a.NewMultipartUploadHandler).Methods("POST")
|
mux.HandleFunc("/{bucket}/{object:.*}", a.NewMultipartUploadHandler).Queries("uploads", "").Methods("POST")
|
||||||
mux.HandleFunc("/{bucket}/{object:.*}", a.AbortMultipartUploadHandler).Queries("uploadId", "{uploadId:.*}").Methods("DELETE")
|
mux.HandleFunc("/{bucket}/{object:.*}", a.AbortMultipartUploadHandler).Queries("uploadId", "{uploadId:.*}").Methods("DELETE")
|
||||||
mux.HandleFunc("/{bucket}/{object:.*}", a.GetObjectHandler).Methods("GET")
|
mux.HandleFunc("/{bucket}/{object:.*}", a.GetObjectHandler).Methods("GET")
|
||||||
mux.HandleFunc("/{bucket}/{object:.*}", a.PutObjectHandler).Methods("PUT")
|
mux.HandleFunc("/{bucket}/{object:.*}", a.PutObjectHandler).Methods("PUT")
|
||||||
|
|
||||||
// not implemented yet
|
|
||||||
mux.HandleFunc("/{bucket}", a.DeleteBucketHandler).Methods("DELETE")
|
mux.HandleFunc("/{bucket}", a.DeleteBucketHandler).Methods("DELETE")
|
||||||
|
|
||||||
// unsupported API
|
|
||||||
mux.HandleFunc("/{bucket}/{object:.*}", a.DeleteObjectHandler).Methods("DELETE")
|
mux.HandleFunc("/{bucket}/{object:.*}", a.DeleteObjectHandler).Methods("DELETE")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,9 +21,9 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/minio/minio/pkg/fs"
|
|
||||||
"github.com/minio/minio-xl/pkg/crypto/sha256"
|
"github.com/minio/minio-xl/pkg/crypto/sha256"
|
||||||
"github.com/minio/minio-xl/pkg/probe"
|
"github.com/minio/minio-xl/pkg/probe"
|
||||||
|
"github.com/minio/minio/pkg/fs"
|
||||||
)
|
)
|
||||||
|
|
||||||
type signatureHandler struct {
|
type signatureHandler struct {
|
||||||
@ -58,6 +58,13 @@ func isRequestPostPolicySignatureV4(req *http.Request) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isRequestRequiresACLCheck(req *http.Request) bool {
|
||||||
|
if isRequestSignatureV4(req) || isRequestPresignedSignatureV4(req) || isRequestPostPolicySignatureV4(req) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func (s signatureHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
func (s signatureHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
if isRequestPostPolicySignatureV4(r) && r.Method == "POST" {
|
if isRequestPostPolicySignatureV4(r) && r.Method == "POST" {
|
||||||
s.handler.ServeHTTP(w, r)
|
s.handler.ServeHTTP(w, r)
|
||||||
@ -129,5 +136,6 @@ func (s signatureHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
s.handler.ServeHTTP(w, r)
|
s.handler.ServeHTTP(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
writeErrorResponse(w, r, AccessDenied, r.URL.Path)
|
// call goes up from here, let ACL's verify the validity of the request
|
||||||
|
s.handler.ServeHTTP(w, r)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user