mirror of
https://github.com/cubefs/cubefs.git
synced 2026-08-02 10:06:14 +00:00
216 lines
5.3 KiB
Go
216 lines
5.3 KiB
Go
// Copyright 2023 The CubeFS Authors.
|
|
//
|
|
// 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.
|
|
|
|
package objectnode
|
|
|
|
import (
|
|
"encoding/json"
|
|
"encoding/xml"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/cubefs/cubefs/proto"
|
|
)
|
|
|
|
func HasAclInRequest(req *http.Request) bool {
|
|
_, hasCannedAcl := req.Header[XAmzAcl]
|
|
hasBodyAcl := req.ContentLength > 0
|
|
hasGrantAcl := hasGrantAclHeader(req.Header)
|
|
return hasCannedAcl || hasGrantAcl || hasBodyAcl
|
|
}
|
|
|
|
func IsApiSupportByACL(apiName proto.Action) bool {
|
|
_, ok := apiToPermission[apiName]
|
|
return ok
|
|
}
|
|
|
|
func IsApiSupportByObjectAcl(apiName proto.Action) bool {
|
|
for _, a := range objectACLSupportedApis {
|
|
if a == apiName {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func ParseACL(req *http.Request, owner string, hasBodyAcl, needDefault bool) (acl *AccessControlPolicy, err error) {
|
|
cannedAcl, hasCannedAcl := req.Header[XAmzAcl]
|
|
hasGrantAcl := hasGrantAclHeader(req.Header)
|
|
if err = hasConflictAcl(hasCannedAcl, hasGrantAcl, hasBodyAcl); err != nil {
|
|
return nil, err
|
|
}
|
|
switch {
|
|
case hasCannedAcl:
|
|
acl, err = ParseCannedAcl(cannedAcl[0], owner)
|
|
case hasGrantAcl:
|
|
acl, err = ParseGrantAclWithinHeader(req.Header, owner)
|
|
case hasBodyAcl:
|
|
acl, err = ParseAclFromRequestBody(req.Body, owner)
|
|
default:
|
|
if needDefault {
|
|
return CreateDefaultACL(owner), nil
|
|
}
|
|
return nil, nil
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return acl, acl.IsValid()
|
|
}
|
|
|
|
func ParseCannedAcl(cannedAcl, owner string) (*AccessControlPolicy, error) {
|
|
acl := &AccessControlPolicy{}
|
|
switch cannedAcl {
|
|
case CannedPrivate:
|
|
acl.SetPrivate(owner)
|
|
case CannedPublicRead:
|
|
acl.SetPublicRead(owner)
|
|
case CannedPublicReadWrite:
|
|
acl.SetPublicReadWrite(owner)
|
|
case CannedAuthenticatedRead:
|
|
acl.SetAuthenticatedRead(owner)
|
|
default:
|
|
return nil, ErrInvalidCannedACL
|
|
}
|
|
return acl, nil
|
|
}
|
|
|
|
func ParseGrantAclWithinHeader(reqHeader http.Header, owner string) (*AccessControlPolicy, error) {
|
|
acl := &AccessControlPolicy{}
|
|
for _, grantHeader := range XAmzGrantHeaders {
|
|
if grantValue := reqHeader.Get(grantHeader); grantValue != "" {
|
|
grants, err := parseGrantValue(grantValue)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
addGrants(acl, grants, XAmzGrantToPermission[grantHeader])
|
|
}
|
|
}
|
|
acl.SetOwner(owner)
|
|
return acl, nil
|
|
}
|
|
|
|
func ParseAclFromRequestBody(body io.Reader, owner string) (acl *AccessControlPolicy, err error) {
|
|
if err = xml.NewDecoder(body).Decode(&acl); err != nil {
|
|
return nil, ErrMalformedACL
|
|
}
|
|
if acl.GetOwner() != owner {
|
|
return nil, AccessDenied
|
|
}
|
|
return acl, nil
|
|
}
|
|
|
|
func CreateDefaultACL(owner string) *AccessControlPolicy {
|
|
acl := &AccessControlPolicy{}
|
|
acl.SetPrivate(owner)
|
|
return acl
|
|
}
|
|
|
|
func hasConflictAcl(hasCannedAcl, hasGrantAcl, hasBodyAcl bool) error {
|
|
if hasCannedAcl && hasGrantAcl {
|
|
return ErrConflictAclHeader
|
|
}
|
|
if hasBodyAcl && (hasCannedAcl || hasGrantAcl) {
|
|
return ErrUnexpectedContent
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func hasGrantAclHeader(h http.Header) bool {
|
|
for _, grantHeader := range XAmzGrantHeaders {
|
|
if _, ok := h[grantHeader]; ok {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func isACLApi(apiName proto.Action) bool {
|
|
for _, a := range aclApiList {
|
|
if a == apiName {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
type grant struct {
|
|
key string
|
|
value string
|
|
}
|
|
|
|
func parseGrantValue(grantValue string) (grants []grant, err error) {
|
|
s := strings.ReplaceAll(grantValue, " ", "")
|
|
pairs := strings.Split(s, ",")
|
|
for _, pair := range pairs {
|
|
kv := strings.Split(pair, "=")
|
|
if len(kv) != 2 || kv[0] == "" || kv[1] == "" {
|
|
return nil, InvalidArgument
|
|
}
|
|
grants = append(grants, grant{key: kv[0], value: kv[1]})
|
|
}
|
|
return
|
|
}
|
|
|
|
func addGrants(acl *AccessControlPolicy, grants []grant, permission string) {
|
|
for _, g := range grants {
|
|
switch strings.ToLower(g.key) {
|
|
case "id":
|
|
acl.AddGrant(g.value, TypeCanonicalUser, permission)
|
|
case "uri":
|
|
acl.AddGrant(g.value, TypeGroup, permission)
|
|
default:
|
|
// do nothing
|
|
}
|
|
}
|
|
}
|
|
|
|
func putBucketACL(vol *Volume, acp *AccessControlPolicy) error {
|
|
data, err := json.Marshal(acp)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return vol.store.Put(vol.name, bucketRootPath, XAttrKeyOSSACL, data)
|
|
}
|
|
|
|
func getObjectACL(vol *Volume, path string, needDefault bool) (*AccessControlPolicy, error) {
|
|
xAttr, err := vol.GetXAttr(path, XAttrKeyOSSACL)
|
|
if err != nil || xAttr == nil {
|
|
return nil, err
|
|
}
|
|
var acp *AccessControlPolicy
|
|
data := xAttr.Get(XAttrKeyOSSACL)
|
|
if len(data) > 0 {
|
|
if err = json.Unmarshal(data, &acp); err != nil {
|
|
err = xml.Unmarshal(data, &acp)
|
|
}
|
|
if err == nil && acp == nil {
|
|
err = fmt.Errorf("data unmarshal failed")
|
|
}
|
|
} else if needDefault {
|
|
acp = CreateDefaultACL(vol.owner)
|
|
}
|
|
return acp, err
|
|
}
|
|
|
|
func putObjectACL(vol *Volume, path string, acp *AccessControlPolicy) error {
|
|
data, err := json.Marshal(acp)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return vol.SetXAttr(path, XAttrKeyOSSACL, data, false)
|
|
}
|