mirror of
https://github.com/minio/minio.git
synced 2024-12-25 06:35:56 -05:00
426c902b87
This PR changes the handling of bucket deletes for site replicated setups to hold on to deleted bucket state until it syncs to all the clusters participating in site replication.
281 lines
8.4 KiB
Go
281 lines
8.4 KiB
Go
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
//
|
|
// This file is part of MinIO Object Storage stack
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Affero General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Affero General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
package cmd
|
|
|
|
import (
|
|
"encoding/xml"
|
|
"io"
|
|
"net/http"
|
|
|
|
"github.com/gorilla/mux"
|
|
xhttp "github.com/minio/minio/internal/http"
|
|
"github.com/minio/minio/internal/logger"
|
|
"github.com/minio/pkg/bucket/policy"
|
|
)
|
|
|
|
// Data types used for returning dummy access control
|
|
// policy XML, these variables shouldn't be used elsewhere
|
|
// they are only defined to be used in this file alone.
|
|
type grantee struct {
|
|
XMLNS string `xml:"xmlns:xsi,attr"`
|
|
XMLXSI string `xml:"xsi:type,attr"`
|
|
Type string `xml:"Type"`
|
|
ID string `xml:"ID,omitempty"`
|
|
DisplayName string `xml:"DisplayName,omitempty"`
|
|
URI string `xml:"URI,omitempty"`
|
|
}
|
|
|
|
type grant struct {
|
|
Grantee grantee `xml:"Grantee"`
|
|
Permission string `xml:"Permission"`
|
|
}
|
|
|
|
type accessControlPolicy struct {
|
|
XMLName xml.Name `xml:"AccessControlPolicy"`
|
|
Owner Owner `xml:"Owner"`
|
|
AccessControlList struct {
|
|
Grants []grant `xml:"Grant"`
|
|
} `xml:"AccessControlList"`
|
|
}
|
|
|
|
// PutBucketACLHandler - PUT Bucket ACL
|
|
// -----------------
|
|
// This operation uses the ACL subresource
|
|
// to set ACL for a bucket, this is a dummy call
|
|
// only responds success if the ACL is private.
|
|
func (api objectAPIHandlers) PutBucketACLHandler(w http.ResponseWriter, r *http.Request) {
|
|
ctx := newContext(r, w, "PutBucketACL")
|
|
|
|
defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
|
|
|
|
vars := mux.Vars(r)
|
|
bucket := vars["bucket"]
|
|
|
|
objAPI := api.ObjectAPI()
|
|
if objAPI == nil {
|
|
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL)
|
|
return
|
|
}
|
|
|
|
// Allow putBucketACL if policy action is set, since this is a dummy call
|
|
// we are simply re-purposing the bucketPolicyAction.
|
|
if s3Error := checkRequestAuthType(ctx, r, policy.PutBucketPolicyAction, bucket, ""); s3Error != ErrNone {
|
|
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL)
|
|
return
|
|
}
|
|
|
|
// Before proceeding validate if bucket exists.
|
|
_, err := objAPI.GetBucketInfo(ctx, bucket, BucketOptions{})
|
|
if err != nil {
|
|
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL)
|
|
return
|
|
}
|
|
|
|
aclHeader := r.Header.Get(xhttp.AmzACL)
|
|
if aclHeader == "" {
|
|
acl := &accessControlPolicy{}
|
|
if err = xmlDecoder(r.Body, acl, r.ContentLength); err != nil {
|
|
if err == io.EOF {
|
|
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrMissingSecurityHeader),
|
|
r.URL)
|
|
return
|
|
}
|
|
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL)
|
|
return
|
|
}
|
|
|
|
if len(acl.AccessControlList.Grants) == 0 {
|
|
writeErrorResponse(ctx, w, toAPIError(ctx, NotImplemented{}), r.URL)
|
|
return
|
|
}
|
|
|
|
if acl.AccessControlList.Grants[0].Permission != "FULL_CONTROL" {
|
|
writeErrorResponse(ctx, w, toAPIError(ctx, NotImplemented{}), r.URL)
|
|
return
|
|
}
|
|
}
|
|
|
|
if aclHeader != "" && aclHeader != "private" {
|
|
writeErrorResponse(ctx, w, toAPIError(ctx, NotImplemented{}), r.URL)
|
|
return
|
|
}
|
|
}
|
|
|
|
// GetBucketACLHandler - GET Bucket ACL
|
|
// -----------------
|
|
// This operation uses the ACL
|
|
// subresource to return the ACL of a specified bucket.
|
|
func (api objectAPIHandlers) GetBucketACLHandler(w http.ResponseWriter, r *http.Request) {
|
|
ctx := newContext(r, w, "GetBucketACL")
|
|
|
|
defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
|
|
|
|
vars := mux.Vars(r)
|
|
bucket := vars["bucket"]
|
|
|
|
objAPI := api.ObjectAPI()
|
|
if objAPI == nil {
|
|
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL)
|
|
return
|
|
}
|
|
|
|
// Allow getBucketACL if policy action is set, since this is a dummy call
|
|
// we are simply re-purposing the bucketPolicyAction.
|
|
if s3Error := checkRequestAuthType(ctx, r, policy.GetBucketPolicyAction, bucket, ""); s3Error != ErrNone {
|
|
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL)
|
|
return
|
|
}
|
|
|
|
// Before proceeding validate if bucket exists.
|
|
_, err := objAPI.GetBucketInfo(ctx, bucket, BucketOptions{})
|
|
if err != nil {
|
|
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL)
|
|
return
|
|
}
|
|
|
|
acl := &accessControlPolicy{}
|
|
acl.AccessControlList.Grants = append(acl.AccessControlList.Grants, grant{
|
|
Grantee: grantee{
|
|
XMLNS: "http://www.w3.org/2001/XMLSchema-instance",
|
|
XMLXSI: "CanonicalUser",
|
|
Type: "CanonicalUser",
|
|
},
|
|
Permission: "FULL_CONTROL",
|
|
})
|
|
|
|
if err := xml.NewEncoder(w).Encode(acl); err != nil {
|
|
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL)
|
|
return
|
|
}
|
|
}
|
|
|
|
// PutObjectACLHandler - PUT Object ACL
|
|
// -----------------
|
|
// This operation uses the ACL subresource
|
|
// to set ACL for a bucket, this is a dummy call
|
|
// only responds success if the ACL is private.
|
|
func (api objectAPIHandlers) PutObjectACLHandler(w http.ResponseWriter, r *http.Request) {
|
|
ctx := newContext(r, w, "PutObjectACL")
|
|
|
|
defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
|
|
|
|
vars := mux.Vars(r)
|
|
bucket := vars["bucket"]
|
|
object, err := unescapePath(vars["object"])
|
|
if err != nil {
|
|
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL)
|
|
return
|
|
}
|
|
|
|
objAPI := api.ObjectAPI()
|
|
if objAPI == nil {
|
|
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL)
|
|
return
|
|
}
|
|
|
|
// Allow putObjectACL if policy action is set, since this is a dummy call
|
|
// we are simply re-purposing the bucketPolicyAction.
|
|
if s3Error := checkRequestAuthType(ctx, r, policy.PutBucketPolicyAction, bucket, ""); s3Error != ErrNone {
|
|
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL)
|
|
return
|
|
}
|
|
|
|
// Before proceeding validate if object exists.
|
|
_, err = objAPI.GetObjectInfo(ctx, bucket, object, ObjectOptions{})
|
|
if err != nil {
|
|
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL)
|
|
return
|
|
}
|
|
|
|
aclHeader := r.Header.Get(xhttp.AmzACL)
|
|
if aclHeader == "" {
|
|
acl := &accessControlPolicy{}
|
|
if err = xmlDecoder(r.Body, acl, r.ContentLength); err != nil {
|
|
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL)
|
|
return
|
|
}
|
|
|
|
if len(acl.AccessControlList.Grants) == 0 {
|
|
writeErrorResponse(ctx, w, toAPIError(ctx, NotImplemented{}), r.URL)
|
|
return
|
|
}
|
|
|
|
if acl.AccessControlList.Grants[0].Permission != "FULL_CONTROL" {
|
|
writeErrorResponse(ctx, w, toAPIError(ctx, NotImplemented{}), r.URL)
|
|
return
|
|
}
|
|
}
|
|
|
|
if aclHeader != "" && aclHeader != "private" {
|
|
writeErrorResponse(ctx, w, toAPIError(ctx, NotImplemented{}), r.URL)
|
|
return
|
|
}
|
|
}
|
|
|
|
// GetObjectACLHandler - GET Object ACL
|
|
// -----------------
|
|
// This operation uses the ACL
|
|
// subresource to return the ACL of a specified object.
|
|
func (api objectAPIHandlers) GetObjectACLHandler(w http.ResponseWriter, r *http.Request) {
|
|
ctx := newContext(r, w, "GetObjectACL")
|
|
|
|
defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
|
|
|
|
vars := mux.Vars(r)
|
|
bucket := vars["bucket"]
|
|
object, err := unescapePath(vars["object"])
|
|
if err != nil {
|
|
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL)
|
|
return
|
|
}
|
|
|
|
objAPI := api.ObjectAPI()
|
|
if objAPI == nil {
|
|
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL)
|
|
return
|
|
}
|
|
|
|
// Allow getObjectACL if policy action is set, since this is a dummy call
|
|
// we are simply re-purposing the bucketPolicyAction.
|
|
if s3Error := checkRequestAuthType(ctx, r, policy.GetBucketPolicyAction, bucket, ""); s3Error != ErrNone {
|
|
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL)
|
|
return
|
|
}
|
|
|
|
// Before proceeding validate if object exists.
|
|
_, err = objAPI.GetObjectInfo(ctx, bucket, object, ObjectOptions{})
|
|
if err != nil {
|
|
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL)
|
|
return
|
|
}
|
|
|
|
acl := &accessControlPolicy{}
|
|
acl.AccessControlList.Grants = append(acl.AccessControlList.Grants, grant{
|
|
Grantee: grantee{
|
|
XMLNS: "http://www.w3.org/2001/XMLSchema-instance",
|
|
XMLXSI: "CanonicalUser",
|
|
Type: "CanonicalUser",
|
|
},
|
|
Permission: "FULL_CONTROL",
|
|
})
|
|
if err := xml.NewEncoder(w).Encode(acl); err != nil {
|
|
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL)
|
|
return
|
|
}
|
|
}
|