mirror of
https://github.com/cubefs/cubefs.git
synced 2026-08-02 02:00:56 +00:00
239 lines
7.0 KiB
Go
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)
|
|
}
|