diff --git a/weed/s3api/s3api_acp.go b/weed/s3api/s3api_acp.go index 7a76c2a67..6d2a62965 100644 --- a/weed/s3api/s3api_acp.go +++ b/weed/s3api/s3api_acp.go @@ -1,8 +1,11 @@ package s3api import ( + "github.com/seaweedfs/seaweedfs/weed/glog" + "github.com/seaweedfs/seaweedfs/weed/pb/filer_pb" "github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants" "github.com/seaweedfs/seaweedfs/weed/s3api/s3account" + "github.com/seaweedfs/seaweedfs/weed/s3api/s3acl" "github.com/seaweedfs/seaweedfs/weed/s3api/s3err" "net/http" ) @@ -27,3 +30,36 @@ func (s3a *S3ApiServer) checkAccessByOwnership(r *http.Request, bucket string) s } return s3err.ErrAccessDenied } + +//Check access for PutBucketAclHandler +func (s3a *S3ApiServer) checkAccessForPutBucketAcl(accountId, bucket string) (*BucketMetaData, s3err.ErrorCode) { + bucketMetadata, errCode := s3a.bucketRegistry.GetBucketMetadata(bucket) + if errCode != s3err.ErrNone { + return nil, errCode + } + + if bucketMetadata.ObjectOwnership == s3_constants.OwnershipBucketOwnerEnforced { + return nil, s3err.AccessControlListNotSupported + } + + if accountId == s3account.AccountAdmin.Id || accountId == *bucketMetadata.Owner.ID { + return bucketMetadata, s3err.ErrNone + } + + if len(bucketMetadata.Acl) > 0 { + reqGrants := s3acl.DetermineReqGrants(accountId, s3_constants.PermissionWriteAcp) + for _, bucketGrant := range bucketMetadata.Acl { + for _, reqGrant := range reqGrants { + if s3acl.GrantEquals(bucketGrant, reqGrant) { + return bucketMetadata, s3err.ErrNone + } + } + } + } + glog.V(3).Infof("acl denied! request account id: %s", accountId) + return nil, s3err.ErrAccessDenied +} + +func updateBucketEntry(s3a *S3ApiServer, entry *filer_pb.Entry) error { + return s3a.updateEntry(s3a.option.BucketsPath, entry) +} diff --git a/weed/s3api/s3api_bucket_handlers.go b/weed/s3api/s3api_bucket_handlers.go index 9e215db9e..7aa5ce16f 100644 --- a/weed/s3api/s3api_bucket_handlers.go +++ b/weed/s3api/s3api_bucket_handlers.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil" + "github.com/seaweedfs/seaweedfs/weed/s3api/s3acl" "github.com/seaweedfs/seaweedfs/weed/util" "math" "net/http" @@ -238,6 +239,46 @@ func (s3a *S3ApiServer) hasAccess(r *http.Request, entry *filer_pb.Entry) bool { return true } +// PutBucketAclHandler Put bucket ACL +// https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketAcl.html +func (s3a *S3ApiServer) PutBucketAclHandler(w http.ResponseWriter, r *http.Request) { + bucket, _ := s3_constants.GetBucketAndObject(r) + glog.V(3).Infof("PutBucketAclHandler %s", bucket) + + accountId := s3acl.GetAccountId(r) + bucketMetadata, errorCode := s3a.checkAccessForPutBucketAcl(accountId, bucket) + if errorCode != s3err.ErrNone { + s3err.WriteErrorResponse(w, r, errorCode) + return + } + + grants, errCode := s3acl.ExtractAcl(r, s3a.accountManager, bucketMetadata.ObjectOwnership, "", *bucketMetadata.Owner.ID, accountId) + if errCode != s3err.ErrNone { + s3err.WriteErrorResponse(w, r, errCode) + return + } + + bucketEntry, err := s3a.getEntry(s3a.option.BucketsPath, bucket) + if err != nil { + glog.Warning(err) + s3err.WriteErrorResponse(w, r, s3err.ErrInternalError) + return + } + + errCode = s3acl.AssembleEntryWithAcp(bucketEntry, *bucketMetadata.Owner.ID, grants) + if errCode != s3err.ErrNone { + s3err.WriteErrorResponse(w, r, errCode) + return + } + + err = updateBucketEntry(s3a, bucketEntry) + if err != nil { + s3err.WriteErrorResponse(w, r, s3err.ErrInternalError) + return + } + s3err.WriteEmptyResponse(w, r, http.StatusOK) +} + // GetBucketAclHandler Get Bucket ACL // https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketAcl.html func (s3a *S3ApiServer) GetBucketAclHandler(w http.ResponseWriter, r *http.Request) { diff --git a/weed/s3api/s3api_bucket_skip_handlers.go b/weed/s3api/s3api_bucket_skip_handlers.go index 70fd38424..62d5b8ce7 100644 --- a/weed/s3api/s3api_bucket_skip_handlers.go +++ b/weed/s3api/s3api_bucket_skip_handlers.go @@ -41,9 +41,3 @@ func (s3a *S3ApiServer) PutBucketPolicyHandler(w http.ResponseWriter, r *http.Re func (s3a *S3ApiServer) DeleteBucketPolicyHandler(w http.ResponseWriter, r *http.Request) { s3err.WriteErrorResponse(w, r, http.StatusNoContent) } - -// PutBucketAclHandler Put bucket ACL -// https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketAcl.html -func (s3a *S3ApiServer) PutBucketAclHandler(w http.ResponseWriter, r *http.Request) { - s3err.WriteErrorResponse(w, r, s3err.ErrNotImplemented) -} diff --git a/weed/s3api/s3err/s3api_errors.go b/weed/s3api/s3err/s3api_errors.go index 0348d4ddc..cdda16e87 100644 --- a/weed/s3api/s3err/s3api_errors.go +++ b/weed/s3api/s3err/s3api_errors.go @@ -109,6 +109,7 @@ const ( ErrRequestBytesExceed OwnershipControlsNotFoundError + AccessControlListNotSupported ) // error code to APIError structure, these fields carry respective @@ -416,12 +417,16 @@ var errorCodeResponse = map[ErrorCode]APIError{ Description: "Simultaneous request bytes exceed limitations", HTTPStatusCode: http.StatusTooManyRequests, }, - OwnershipControlsNotFoundError: { Code: "OwnershipControlsNotFoundError", Description: "The bucket ownership controls were not found", HTTPStatusCode: http.StatusNotFound, }, + AccessControlListNotSupported: { + Code: "AccessControlListNotSupported", + Description: "The bucket does not allow ACLs", + HTTPStatusCode: http.StatusBadRequest, + }, } // GetAPIError provides API Error for input API error code.