cubefs/objectnode/acl.go
yhjiango c40e78cd83 refactor(objectnode): refactor acl structure json tag
Signed-off-by: yhjiango <jiangyunhua@oppo.com>
2023-08-09 14:01:52 +08:00

239 lines
7.0 KiB
Go

// Copyright 2019 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
// https://docs.aws.amazon.com/zh_cn/AmazonS3/latest/dev/S3_ACLs_UsingACLs.html
import (
"encoding/json"
"encoding/xml"
"net/http"
"github.com/cubefs/cubefs/proto"
"github.com/cubefs/cubefs/util/log"
)
const (
AnonymousUser = ""
bucketRootPath = "/"
maxGrantCount = 100 // an acl can have up to 100 grants
)
var (
ErrMissingSecurityHeader = &ErrorCode{ErrorCode: "MissingSecurityHeader", ErrorMessage: "Your request is missing a required header.", StatusCode: http.StatusBadRequest}
ErrTooManyGrants = &ErrorCode{ErrorCode: "ErrTooManyGrants", ErrorMessage: "Grant number exceed limits.", StatusCode: http.StatusBadRequest}
ErrMissingGrants = &ErrorCode{ErrorCode: "ErrMissingGrants", ErrorMessage: "Missing grant in request or grant info not supported.", StatusCode: http.StatusBadRequest}
ErrInvalidCannedACL = &ErrorCode{ErrorCode: "InvalidArgument", ErrorMessage: "Invalid canned acl.", StatusCode: http.StatusBadRequest}
ErrInvalidGroupUri = &ErrorCode{ErrorCode: "InvalidArgument", ErrorMessage: "Invalid group uri.", StatusCode: http.StatusBadRequest}
ErrInvalidPermission = &ErrorCode{ErrorCode: "InvalidArgument", ErrorMessage: "Invalid permission, it is case sensitive.", StatusCode: http.StatusBadRequest}
ErrMalformedACL = &ErrorCode{ErrorCode: "MalformedACLError", ErrorMessage: "The XML you provided was not well-formed or did not validate against our published schema.", StatusCode: http.StatusBadRequest}
ErrConflictAclHeader = &ErrorCode{ErrorCode: "InvalidRequest", ErrorMessage: "Specifying both Canned ACLs and Header Grants is not allowed.", StatusCode: http.StatusBadRequest}
ErrUnexpectedContent = &ErrorCode{ErrorCode: "UnexpectedContent", ErrorMessage: "This request does not support content.", StatusCode: http.StatusBadRequest}
)
type Grantee struct {
Xmlxsi string `xml:"xmlns:xsi,attr" json:"-"`
XsiType string `xml:"xsi:type,attr" json:"-"`
Type string `xml:"type,attr" json:"t"`
Id string `xml:"ID,omitempty" json:"i,omitempty"`
URI string `xml:"URI,omitempty" json:"u,omitempty"`
DisplayName string `xml:"DisplayName,omitempty" json:"d,omitempty"`
}
type Grant struct {
Grantee Grantee `xml:"Grantee,omitempty" json:"g"`
Permission string `xml:"Permission,omitempty" json:"p"`
}
type AccessControlList struct {
Grants []Grant `xml:"Grant,omitempty" json:"gs"`
}
type Owner struct {
Id string `xml:"ID" json:"i"`
DisplayName string `xml:"DisplayName" json:"d,omitempty"`
}
type AccessControlPolicy struct {
Xmlns string `xml:"xmlns,attr" json:"-"`
Owner Owner `xml:"Owner,omitempty" json:"o,omitempty"`
Acl AccessControlList `xml:"AccessControlList,omitempty" json:"a,omitempty"`
}
func (g *Grantee) isValid() error {
if g.Id == "" && g.URI == "" {
return ErrMalformedACL
}
if g.Id != "" && g.URI != "" {
return ErrMalformedACL
}
switch g.Type {
case TypeGroup:
if g.URI != GroupAllUser && g.URI != GroupAuthenticated {
return ErrInvalidGroupUri
}
case TypeCanonicalUser:
if g.URI != "" {
return ErrMalformedACL
}
default:
return ErrMalformedACL
}
return nil
}
func (g *Grant) isValid() error {
if !g.isPermissionValid() {
return ErrInvalidPermission
}
return g.Grantee.isValid()
}
func (g *Grant) isPermissionValid() bool {
for _, v := range XAmzGrantToPermission {
if g.Permission == v {
return true
}
}
return false
}
func (g *Grant) isAllowed(reqId string, apiName proto.Action) bool {
permNeed, ok := apiToPermission[apiName]
if !ok {
return false
}
if !(g.Permission == PermissionFullControl || g.Permission == permNeed) {
return false
}
switch g.Grantee.Type {
case TypeCanonicalUser:
return g.Grantee.Id == reqId
case TypeGroup:
if g.Grantee.URI == GroupAllUser {
return true
}
return reqId != AnonymousUser // Group_Authenticated
default:
return false
}
}
func (acp *AccessControlPolicy) IsValid() error {
if len(acp.Acl.Grants) == 0 {
return ErrMissingGrants
}
if len(acp.Acl.Grants) > maxGrantCount {
return ErrTooManyGrants
}
for _, g := range acp.Acl.Grants {
if err := g.isValid(); err != nil {
return err
}
}
return nil
}
func (acp *AccessControlPolicy) IsAllowed(reqUid string, apiName proto.Action) bool {
for _, g := range acp.Acl.Grants {
if g.isAllowed(reqUid, apiName) {
return true
}
}
if len(acp.Acl.Grants) == 0 && acp.Owner.Id == reqUid {
return true
}
if isACLApi(apiName) {
return acp.Owner.Id == reqUid
}
return false
}
func (acp *AccessControlPolicy) IsEmpty() bool {
return len(acp.Acl.Grants) == 0
}
func (acp *AccessControlPolicy) SetOwner(owner string) {
acp.Owner.Id = owner
}
func (acp *AccessControlPolicy) GetOwner() string {
return acp.Owner.Id
}
func (acp *AccessControlPolicy) AddGrant(idUri, granteeType, permission string) {
var g Grant
switch granteeType {
case TypeCanonicalUser:
g.Grantee.Id = idUri
g.Grantee.Type = TypeCanonicalUser
case TypeGroup:
g.Grantee.URI = idUri
g.Grantee.Type = TypeGroup
default:
return
}
g.Permission = permission
acp.Acl.Grants = append(acp.Acl.Grants, g)
}
func (acp *AccessControlPolicy) SetPrivate(ownerId string) {
acp.SetOwner(ownerId)
acp.AddGrant(ownerId, TypeCanonicalUser, PermissionFullControl)
}
func (acp *AccessControlPolicy) SetPublicRead(ownerId string) {
acp.SetOwner(ownerId)
acp.AddGrant(ownerId, TypeCanonicalUser, PermissionFullControl)
acp.AddGrant(GroupAllUser, TypeGroup, PermissionRead)
}
func (acp *AccessControlPolicy) SetPublicReadWrite(ownerId string) {
acp.SetOwner(ownerId)
acp.AddGrant(ownerId, TypeCanonicalUser, PermissionFullControl)
acp.AddGrant(GroupAllUser, TypeGroup, PermissionRead)
acp.AddGrant(GroupAllUser, TypeGroup, PermissionWrite)
}
func (acp *AccessControlPolicy) SetAuthenticatedRead(ownerId string) {
acp.SetOwner(ownerId)
acp.AddGrant(ownerId, TypeCanonicalUser, PermissionFullControl)
acp.AddGrant(GroupAuthenticated, TypeGroup, PermissionRead)
}
func (acp *AccessControlPolicy) XmlMarshal() ([]byte, error) {
var grants []Grant
for _, g := range acp.Acl.Grants {
g.Grantee.Xmlxsi = XMLSI
g.Grantee.XsiType = g.Grantee.Type
grants = append(grants, g)
}
acp.Acl.Grants = grants
acp.Xmlns = XMLNS
data, err := xml.Marshal(acp)
if err != nil {
return nil, err
}
return append([]byte(xml.Header), data...), nil
}
func (acp *AccessControlPolicy) Encode() string {
data, err := json.Marshal(acp)
if err != nil {
log.LogWarnf("acl json marshal failed: %v", err)
}
return string(data)
}