forked from TrueCloudLab/frostfs-sdk-go
e82a2d86ef
Replace basic ACL functionality from `acl` package to the `container` one. Create `BasicACL` type and provide convenient interface to work with it. Signed-off-by: Leonard Lyubich <leonard@nspcc.ru>
272 lines
7.1 KiB
Go
272 lines
7.1 KiB
Go
package container
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// BasicACL represents basic part of the NeoFS container's ACL. It includes
|
|
// common (pretty simple) access rules for operations inside the container.
|
|
// See NeoFS Specification for details.
|
|
//
|
|
// One can find some similarities with the traditional Unix permission, such as
|
|
// division into scopes: user, group, others
|
|
// op-permissions: read, write, etc.
|
|
// sticky bit
|
|
// However, these similarities should only be used for better understanding,
|
|
// in general these mechanisms are different.
|
|
//
|
|
// Instances can be created using built-in var declaration, but look carefully
|
|
// at the default values, and how individual permissions are regulated.
|
|
// Some frequently used values are presented in BasicACL* values.
|
|
//
|
|
// BasicACL instances are comparable: values can be compared directly using
|
|
// == operator.
|
|
type BasicACL struct {
|
|
bits uint32
|
|
}
|
|
|
|
func (x *BasicACL) fromUint32(num uint32) {
|
|
x.bits = num
|
|
}
|
|
|
|
func (x BasicACL) toUint32() uint32 {
|
|
return x.bits
|
|
}
|
|
|
|
// common bit sections.
|
|
const (
|
|
opAmount = 7
|
|
bitsPerOp = 4
|
|
|
|
bitPosFinal = opAmount * bitsPerOp
|
|
bitPosSticky = bitPosFinal + 1
|
|
)
|
|
|
|
// per-op bit order.
|
|
const (
|
|
opBitPosBearer uint8 = iota
|
|
opBitPosOthers
|
|
opBitPosContainer
|
|
opBitPosOwner
|
|
)
|
|
|
|
// DisableExtension makes BasicACL FINAL. FINAL indicates the ACL non-extendability
|
|
// in the related container.
|
|
//
|
|
// See also Extendable.
|
|
func (x *BasicACL) DisableExtension() {
|
|
setBit(&x.bits, bitPosFinal)
|
|
}
|
|
|
|
// Extendable checks if BasicACL is NOT made FINAL using DisableExtension.
|
|
//
|
|
// Zero BasicACL is NOT FINAL or extendable.
|
|
func (x BasicACL) Extendable() bool {
|
|
return !isBitSet(x.bits, bitPosFinal)
|
|
}
|
|
|
|
// MakeSticky makes BasicACL STICKY. STICKY indicates that only the owner of any
|
|
// particular object is allowed to operate on it.
|
|
//
|
|
// See also Sticky.
|
|
func (x *BasicACL) MakeSticky() {
|
|
setBit(&x.bits, bitPosSticky)
|
|
}
|
|
|
|
// Sticky checks if BasicACL is made STICKY using MakeSticky.
|
|
//
|
|
// Zero BasicACL is NOT STICKY.
|
|
func (x BasicACL) Sticky() bool {
|
|
return isBitSet(x.bits, bitPosSticky)
|
|
}
|
|
|
|
// checks if op is used by the storage nodes within replication mechanism.
|
|
func isReplicationOp(op ACLOp) bool {
|
|
//nolint:exhaustive
|
|
switch op {
|
|
case
|
|
ACLOpObjectGet,
|
|
ACLOpObjectHead,
|
|
ACLOpObjectPut,
|
|
ACLOpObjectSearch,
|
|
ACLOpObjectHash:
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// AllowOp allows the parties with the given role to the given operation.
|
|
// Op MUST be one of the ACLOp enumeration. Role MUST be one of:
|
|
// ACLRoleOwner
|
|
// ACLRoleContainer
|
|
// ACLRoleOthers
|
|
// and if role is ACLRoleContainer, op MUST NOT be:
|
|
// ACLOpObjectGet
|
|
// ACLOpObjectHead
|
|
// ACLOpObjectPut
|
|
// ACLOpObjectSearch
|
|
// ACLOpObjectHash
|
|
//
|
|
// See also IsOpAllowed.
|
|
func (x *BasicACL) AllowOp(op ACLOp, role ACLRole) {
|
|
var bitPos uint8
|
|
|
|
switch role {
|
|
default:
|
|
panic(fmt.Sprintf("unable to set rules for unsupported role %v", role))
|
|
case ACLRoleInnerRing:
|
|
panic("basic ACL MUST NOT be modified for Inner Ring")
|
|
case ACLRoleOwner:
|
|
bitPos = opBitPosOwner
|
|
case ACLRoleContainer:
|
|
if isReplicationOp(op) {
|
|
panic("basic ACL for container replication ops MUST NOT be modified")
|
|
}
|
|
|
|
bitPos = opBitPosContainer
|
|
case ACLRoleOthers:
|
|
bitPos = opBitPosOthers
|
|
}
|
|
|
|
setOpBit(&x.bits, op, bitPos)
|
|
}
|
|
|
|
// IsOpAllowed checks if parties with the given role are allowed to the given op
|
|
// according to the BasicACL rules. Op MUST be one of the ACLOp enumeration.
|
|
// Role MUST be one of the ACLRole enumeration.
|
|
//
|
|
// Members with ACLRoleContainer role have exclusive default access to the
|
|
// operations of the data replication mechanism:
|
|
// ACLOpObjectGet
|
|
// ACLOpObjectHead
|
|
// ACLOpObjectPut
|
|
// ACLOpObjectSearch
|
|
// ACLOpObjectHash
|
|
//
|
|
// ACLRoleInnerRing members are allowed to data audit ops only:
|
|
// ACLOpObjectGet
|
|
// ACLOpObjectHead
|
|
// ACLOpObjectHash
|
|
// ACLOpObjectSearch
|
|
//
|
|
// Zero BasicACL prevents any role from accessing any operation in the absence
|
|
// of default rights.
|
|
//
|
|
// See also AllowOp.
|
|
func (x BasicACL) IsOpAllowed(op ACLOp, role ACLRole) bool {
|
|
var bitPos uint8
|
|
|
|
switch role {
|
|
default:
|
|
panic(fmt.Sprintf("role is unsupported %v", role))
|
|
case ACLRoleInnerRing:
|
|
switch op {
|
|
case
|
|
ACLOpObjectGet,
|
|
ACLOpObjectHead,
|
|
ACLOpObjectHash,
|
|
ACLOpObjectSearch:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
case ACLRoleOwner:
|
|
bitPos = opBitPosOwner
|
|
case ACLRoleContainer:
|
|
if isReplicationOp(op) {
|
|
return true
|
|
}
|
|
|
|
bitPos = opBitPosContainer
|
|
case ACLRoleOthers:
|
|
bitPos = opBitPosOthers
|
|
}
|
|
|
|
return isOpBitSet(x.bits, op, bitPos)
|
|
}
|
|
|
|
// AllowBearerRules allows bearer to provide extended ACL rules for the given
|
|
// operation. Bearer rules doesn't depend on container ACL
|
|
// // extensibility.
|
|
//
|
|
// See also AllowedBearerRules.
|
|
func (x *BasicACL) AllowBearerRules(op ACLOp) {
|
|
setOpBit(&x.bits, op, opBitPosBearer)
|
|
}
|
|
|
|
// AllowedBearerRules checks if bearer rules are allowed using AllowBearerRules.
|
|
// Op MUST be one of the ACLOp enumeration.
|
|
//
|
|
// Zero BasicACL disallows bearer rules for any op.
|
|
func (x BasicACL) AllowedBearerRules(op ACLOp) bool {
|
|
return isOpBitSet(x.bits, op, opBitPosBearer)
|
|
}
|
|
|
|
// EncodeToString encodes BasicACL into hexadecimal string.
|
|
//
|
|
// See also DecodeString.
|
|
func (x BasicACL) EncodeToString() string {
|
|
return strconv.FormatUint(uint64(x.bits), 16)
|
|
}
|
|
|
|
// Names of the frequently used BasicACL values.
|
|
const (
|
|
BasicACLNamePrivate = "private"
|
|
BasicACLNamePrivateExtended = "eacl-private"
|
|
BasicACLNamePublicRO = "public-read"
|
|
BasicACLNamePublicROExtended = "eacl-public-read"
|
|
BasicACLNamePublicRW = "public-read-write"
|
|
BasicACLNamePublicRWExtended = "eacl-public-read-write"
|
|
BasicACLNamePublicAppend = "public-append"
|
|
BasicACLNamePublicAppendExtended = "eacl-public-append"
|
|
)
|
|
|
|
// Frequently used BasicACL values (each value MUST NOT be modified, make a
|
|
// copy instead).
|
|
var (
|
|
BasicACLPrivate BasicACL // private
|
|
BasicACLPrivateExtended BasicACL // eacl-private
|
|
BasicACLPublicRO BasicACL // public-read
|
|
BasicACLPublicROExtended BasicACL // eacl-public-read
|
|
BasicACLPublicRW BasicACL // public-read-write
|
|
BasicACLPublicRWExtended BasicACL // eacl-public-read-write
|
|
BasicACLPublicAppend BasicACL // public-append
|
|
BasicACLPublicAppendExtended BasicACL // eacl-public-append
|
|
)
|
|
|
|
// DecodeString decodes string calculated using EncodeToString. Also supports
|
|
// human-readable names (BasicACLName* constants).
|
|
func (x *BasicACL) DecodeString(s string) error {
|
|
switch s {
|
|
case BasicACLNamePrivate:
|
|
*x = BasicACLPrivate
|
|
case BasicACLNamePrivateExtended:
|
|
*x = BasicACLPrivateExtended
|
|
case BasicACLNamePublicRO:
|
|
*x = BasicACLPublicRO
|
|
case BasicACLNamePublicROExtended:
|
|
*x = BasicACLPublicROExtended
|
|
case BasicACLNamePublicRW:
|
|
*x = BasicACLPublicRW
|
|
case BasicACLNamePublicRWExtended:
|
|
*x = BasicACLPublicRWExtended
|
|
case BasicACLNamePublicAppend:
|
|
*x = BasicACLPublicAppend
|
|
case BasicACLNamePublicAppendExtended:
|
|
*x = BasicACLPublicAppendExtended
|
|
default:
|
|
s = strings.TrimPrefix(strings.ToLower(s), "0x")
|
|
|
|
v, err := strconv.ParseUint(s, 16, 32)
|
|
if err != nil {
|
|
return fmt.Errorf("parse hex: %w", err)
|
|
}
|
|
|
|
x.bits = uint32(v)
|
|
}
|
|
|
|
return nil
|
|
}
|