[#225] container: Replace basic ACL code to a separate package
Create `acl` package inside `container` path. Replace basic ACL functionality into it. Signed-off-by: Leonard Lyubich <leonard@nspcc.ru>
This commit is contained in:
parent
e82a2d86ef
commit
c4ebe8d854
14 changed files with 524 additions and 499 deletions
|
@ -1,81 +0,0 @@
|
||||||
package container
|
|
||||||
|
|
||||||
import "strconv"
|
|
||||||
|
|
||||||
// ACLOp enumerates operations under access control inside container.
|
|
||||||
// Non-positive values are reserved and depend on context (e.g. unsupported op).
|
|
||||||
//
|
|
||||||
// Note that type conversion from- and to numerical types is not recommended,
|
|
||||||
// use corresponding constants and/or methods instead.
|
|
||||||
type ACLOp uint32
|
|
||||||
|
|
||||||
const (
|
|
||||||
aclOpZero ACLOp = iota // extreme value for testing
|
|
||||||
|
|
||||||
ACLOpObjectGet // Object.Get rpc
|
|
||||||
ACLOpObjectHead // Object.Head rpc
|
|
||||||
ACLOpObjectPut // Object.Put rpc
|
|
||||||
ACLOpObjectDelete // Object.Delete rpc
|
|
||||||
ACLOpObjectSearch // Object.Search rpc
|
|
||||||
ACLOpObjectRange // Object.GetRange rpc
|
|
||||||
ACLOpObjectHash // Object.GetRangeHash rpc
|
|
||||||
|
|
||||||
aclOpLast // extreme value for testing
|
|
||||||
)
|
|
||||||
|
|
||||||
// String implements fmt.Stringer.
|
|
||||||
func (x ACLOp) String() string {
|
|
||||||
switch x {
|
|
||||||
default:
|
|
||||||
return "UNKNOWN#" + strconv.FormatUint(uint64(x), 10)
|
|
||||||
case ACLOpObjectGet:
|
|
||||||
return "OBJECT_GET"
|
|
||||||
case ACLOpObjectHead:
|
|
||||||
return "OBJECT_HEAD"
|
|
||||||
case ACLOpObjectPut:
|
|
||||||
return "OBJECT_PUT"
|
|
||||||
case ACLOpObjectDelete:
|
|
||||||
return "OBJECT_DELETE"
|
|
||||||
case ACLOpObjectSearch:
|
|
||||||
return "OBJECT_SEARCH"
|
|
||||||
case ACLOpObjectRange:
|
|
||||||
return "OBJECT_RANGE"
|
|
||||||
case ACLOpObjectHash:
|
|
||||||
return "OBJECT_HASH"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ACLRole enumerates roles covered by container ACL. Each role represents
|
|
||||||
// some party which can be authenticated during container op execution.
|
|
||||||
// Non-positive values are reserved and depend on context (e.g. unsupported role).
|
|
||||||
//
|
|
||||||
// Note that type conversion from- and to numerical types is not recommended,
|
|
||||||
// use corresponding constants and/or methods instead.
|
|
||||||
type ACLRole uint32
|
|
||||||
|
|
||||||
const (
|
|
||||||
aclRoleZero ACLRole = iota // extreme value for testing
|
|
||||||
|
|
||||||
ACLRoleOwner // container owner
|
|
||||||
ACLRoleContainer // nodes of the related container
|
|
||||||
ACLRoleInnerRing // Inner Ring nodes
|
|
||||||
ACLRoleOthers // all others
|
|
||||||
|
|
||||||
aclRoleLast // extreme value for testing
|
|
||||||
)
|
|
||||||
|
|
||||||
// String implements fmt.Stringer.
|
|
||||||
func (x ACLRole) String() string {
|
|
||||||
switch x {
|
|
||||||
default:
|
|
||||||
return "UNKNOWN#" + strconv.FormatUint(uint64(x), 10)
|
|
||||||
case ACLRoleOwner:
|
|
||||||
return "OWNER"
|
|
||||||
case ACLRoleContainer:
|
|
||||||
return "CONTAINER"
|
|
||||||
case ACLRoleInnerRing:
|
|
||||||
return "INNER_RING"
|
|
||||||
case ACLRoleOthers:
|
|
||||||
return "OTHERS"
|
|
||||||
}
|
|
||||||
}
|
|
81
container/acl/acl.go
Normal file
81
container/acl/acl.go
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
package acl
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
// Op enumerates operations under access control inside container.
|
||||||
|
// Non-positive values are reserved and depend on context (e.g. unsupported op).
|
||||||
|
//
|
||||||
|
// Note that type conversion from- and to numerical types is not recommended,
|
||||||
|
// use corresponding constants and/or methods instead.
|
||||||
|
type Op uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
opZero Op = iota // extreme value for testing
|
||||||
|
|
||||||
|
OpObjectGet // Object.Get rpc
|
||||||
|
OpObjectHead // Object.Head rpc
|
||||||
|
OpObjectPut // Object.Put rpc
|
||||||
|
OpObjectDelete // Object.Delete rpc
|
||||||
|
OpObjectSearch // Object.Search rpc
|
||||||
|
OpObjectRange // Object.GetRange rpc
|
||||||
|
OpObjectHash // Object.GetRangeHash rpc
|
||||||
|
|
||||||
|
opLast // extreme value for testing
|
||||||
|
)
|
||||||
|
|
||||||
|
// String implements fmt.Stringer.
|
||||||
|
func (x Op) String() string {
|
||||||
|
switch x {
|
||||||
|
default:
|
||||||
|
return "UNKNOWN#" + strconv.FormatUint(uint64(x), 10)
|
||||||
|
case OpObjectGet:
|
||||||
|
return "OBJECT_GET"
|
||||||
|
case OpObjectHead:
|
||||||
|
return "OBJECT_HEAD"
|
||||||
|
case OpObjectPut:
|
||||||
|
return "OBJECT_PUT"
|
||||||
|
case OpObjectDelete:
|
||||||
|
return "OBJECT_DELETE"
|
||||||
|
case OpObjectSearch:
|
||||||
|
return "OBJECT_SEARCH"
|
||||||
|
case OpObjectRange:
|
||||||
|
return "OBJECT_RANGE"
|
||||||
|
case OpObjectHash:
|
||||||
|
return "OBJECT_HASH"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Role enumerates roles covered by container ACL. Each role represents
|
||||||
|
// some party which can be authenticated during container op execution.
|
||||||
|
// Non-positive values are reserved and depend on context (e.g. unsupported role).
|
||||||
|
//
|
||||||
|
// Note that type conversion from- and to numerical types is not recommended,
|
||||||
|
// use corresponding constants and/or methods instead.
|
||||||
|
type Role uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
roleZero Role = iota // extreme value for testing
|
||||||
|
|
||||||
|
RoleOwner // container owner
|
||||||
|
RoleContainer // nodes of the related container
|
||||||
|
RoleInnerRing // Inner Ring nodes
|
||||||
|
RoleOthers // all others
|
||||||
|
|
||||||
|
roleLast // extreme value for testing
|
||||||
|
)
|
||||||
|
|
||||||
|
// String implements fmt.Stringer.
|
||||||
|
func (x Role) String() string {
|
||||||
|
switch x {
|
||||||
|
default:
|
||||||
|
return "UNKNOWN#" + strconv.FormatUint(uint64(x), 10)
|
||||||
|
case RoleOwner:
|
||||||
|
return "OWNER"
|
||||||
|
case RoleContainer:
|
||||||
|
return "CONTAINER"
|
||||||
|
case RoleInnerRing:
|
||||||
|
return "INNER_RING"
|
||||||
|
case RoleOthers:
|
||||||
|
return "OTHERS"
|
||||||
|
}
|
||||||
|
}
|
278
container/acl/acl_basic.go
Normal file
278
container/acl/acl_basic.go
Normal file
|
@ -0,0 +1,278 @@
|
||||||
|
package acl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Basic 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 exported variables.
|
||||||
|
//
|
||||||
|
// Basic instances are comparable: values can be compared directly using
|
||||||
|
// == operator.
|
||||||
|
type Basic struct {
|
||||||
|
bits uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromBits decodes Basic from the numerical representation.
|
||||||
|
//
|
||||||
|
// See also Bits.
|
||||||
|
func (x *Basic) FromBits(bits uint32) {
|
||||||
|
x.bits = bits
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bits returns numerical encoding of Basic.
|
||||||
|
//
|
||||||
|
// See also FromBits.
|
||||||
|
func (x Basic) Bits() 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 Basic FINAL. FINAL indicates the ACL non-extendability
|
||||||
|
// in the related container.
|
||||||
|
//
|
||||||
|
// See also Extendable.
|
||||||
|
func (x *Basic) DisableExtension() {
|
||||||
|
setBit(&x.bits, bitPosFinal)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extendable checks if Basic is NOT made FINAL using DisableExtension.
|
||||||
|
//
|
||||||
|
// Zero Basic is NOT FINAL or extendable.
|
||||||
|
func (x Basic) Extendable() bool {
|
||||||
|
return !isBitSet(x.bits, bitPosFinal)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeSticky makes Basic STICKY. STICKY indicates that only the owner of any
|
||||||
|
// particular object is allowed to operate on it.
|
||||||
|
//
|
||||||
|
// See also Sticky.
|
||||||
|
func (x *Basic) MakeSticky() {
|
||||||
|
setBit(&x.bits, bitPosSticky)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sticky checks if Basic is made STICKY using MakeSticky.
|
||||||
|
//
|
||||||
|
// Zero Basic is NOT STICKY.
|
||||||
|
func (x Basic) Sticky() bool {
|
||||||
|
return isBitSet(x.bits, bitPosSticky)
|
||||||
|
}
|
||||||
|
|
||||||
|
// checks if op is used by the storage nodes within replication mechanism.
|
||||||
|
func isReplicationOp(op Op) bool {
|
||||||
|
//nolint:exhaustive
|
||||||
|
switch op {
|
||||||
|
case
|
||||||
|
OpObjectGet,
|
||||||
|
OpObjectHead,
|
||||||
|
OpObjectPut,
|
||||||
|
OpObjectSearch,
|
||||||
|
OpObjectHash:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// AllowOp allows the parties with the given role to the given operation.
|
||||||
|
// Op MUST be one of the Op enumeration. Role MUST be one of:
|
||||||
|
// RoleOwner
|
||||||
|
// RoleContainer
|
||||||
|
// RoleOthers
|
||||||
|
// and if role is RoleContainer, op MUST NOT be:
|
||||||
|
// OpObjectGet
|
||||||
|
// OpObjectHead
|
||||||
|
// OpObjectPut
|
||||||
|
// OpObjectSearch
|
||||||
|
// OpObjectHash
|
||||||
|
//
|
||||||
|
// See also IsOpAllowed.
|
||||||
|
func (x *Basic) AllowOp(op Op, role Role) {
|
||||||
|
var bitPos uint8
|
||||||
|
|
||||||
|
switch role {
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("unable to set rules for unsupported role %v", role))
|
||||||
|
case RoleInnerRing:
|
||||||
|
panic("basic ACL MUST NOT be modified for Inner Ring")
|
||||||
|
case RoleOwner:
|
||||||
|
bitPos = opBitPosOwner
|
||||||
|
case RoleContainer:
|
||||||
|
if isReplicationOp(op) {
|
||||||
|
panic("basic ACL for container replication ops MUST NOT be modified")
|
||||||
|
}
|
||||||
|
|
||||||
|
bitPos = opBitPosContainer
|
||||||
|
case RoleOthers:
|
||||||
|
bitPos = opBitPosOthers
|
||||||
|
}
|
||||||
|
|
||||||
|
setOpBit(&x.bits, op, bitPos)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsOpAllowed checks if parties with the given role are allowed to the given op
|
||||||
|
// according to the Basic rules. Op MUST be one of the Op enumeration.
|
||||||
|
// Role MUST be one of the Role enumeration.
|
||||||
|
//
|
||||||
|
// Members with RoleContainer role have exclusive default access to the
|
||||||
|
// operations of the data replication mechanism:
|
||||||
|
// OpObjectGet
|
||||||
|
// OpObjectHead
|
||||||
|
// OpObjectPut
|
||||||
|
// OpObjectSearch
|
||||||
|
// OpObjectHash
|
||||||
|
//
|
||||||
|
// RoleInnerRing members are allowed to data audit ops only:
|
||||||
|
// OpObjectGet
|
||||||
|
// OpObjectHead
|
||||||
|
// OpObjectHash
|
||||||
|
// OpObjectSearch
|
||||||
|
//
|
||||||
|
// Zero Basic prevents any role from accessing any operation in the absence
|
||||||
|
// of default rights.
|
||||||
|
//
|
||||||
|
// See also AllowOp.
|
||||||
|
func (x Basic) IsOpAllowed(op Op, role Role) bool {
|
||||||
|
var bitPos uint8
|
||||||
|
|
||||||
|
switch role {
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("role is unsupported %v", role))
|
||||||
|
case RoleInnerRing:
|
||||||
|
switch op {
|
||||||
|
case
|
||||||
|
OpObjectGet,
|
||||||
|
OpObjectHead,
|
||||||
|
OpObjectHash,
|
||||||
|
OpObjectSearch:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
case RoleOwner:
|
||||||
|
bitPos = opBitPosOwner
|
||||||
|
case RoleContainer:
|
||||||
|
if isReplicationOp(op) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
bitPos = opBitPosContainer
|
||||||
|
case RoleOthers:
|
||||||
|
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 *Basic) AllowBearerRules(op Op) {
|
||||||
|
setOpBit(&x.bits, op, opBitPosBearer)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AllowedBearerRules checks if bearer rules are allowed using AllowBearerRules.
|
||||||
|
// Op MUST be one of the Op enumeration.
|
||||||
|
//
|
||||||
|
// Zero Basic disallows bearer rules for any op.
|
||||||
|
func (x Basic) AllowedBearerRules(op Op) bool {
|
||||||
|
return isOpBitSet(x.bits, op, opBitPosBearer)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeToString encodes Basic into hexadecimal string.
|
||||||
|
//
|
||||||
|
// See also DecodeString.
|
||||||
|
func (x Basic) EncodeToString() string {
|
||||||
|
return strconv.FormatUint(uint64(x.bits), 16)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Names of the frequently used Basic values.
|
||||||
|
const (
|
||||||
|
NamePrivate = "private"
|
||||||
|
NamePrivateExtended = "eacl-private"
|
||||||
|
NamePublicRO = "public-read"
|
||||||
|
NamePublicROExtended = "eacl-public-read"
|
||||||
|
NamePublicRW = "public-read-write"
|
||||||
|
NamePublicRWExtended = "eacl-public-read-write"
|
||||||
|
NamePublicAppend = "public-append"
|
||||||
|
NamePublicAppendExtended = "eacl-public-append"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Frequently used Basic values (each value MUST NOT be modified, make a
|
||||||
|
// copy instead).
|
||||||
|
var (
|
||||||
|
Private Basic // private
|
||||||
|
PrivateExtended Basic // eacl-private
|
||||||
|
PublicRO Basic // public-read
|
||||||
|
PublicROExtended Basic // eacl-public-read
|
||||||
|
PublicRW Basic // public-read-write
|
||||||
|
PublicRWExtended Basic // eacl-public-read-write
|
||||||
|
PublicAppend Basic // public-append
|
||||||
|
PublicAppendExtended Basic // eacl-public-append
|
||||||
|
)
|
||||||
|
|
||||||
|
// DecodeString decodes string calculated using EncodeToString. Also supports
|
||||||
|
// human-readable names (Name* constants).
|
||||||
|
func (x *Basic) DecodeString(s string) (e error) {
|
||||||
|
switch s {
|
||||||
|
case NamePrivate:
|
||||||
|
*x = Private
|
||||||
|
case NamePrivateExtended:
|
||||||
|
*x = PrivateExtended
|
||||||
|
case NamePublicRO:
|
||||||
|
*x = PublicRO
|
||||||
|
case NamePublicROExtended:
|
||||||
|
*x = PublicROExtended
|
||||||
|
case NamePublicRW:
|
||||||
|
*x = PublicRW
|
||||||
|
case NamePublicRWExtended:
|
||||||
|
*x = PublicRWExtended
|
||||||
|
case NamePublicAppend:
|
||||||
|
*x = PublicAppend
|
||||||
|
case NamePublicAppendExtended:
|
||||||
|
*x = PublicAppendExtended
|
||||||
|
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
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package container
|
package acl
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -6,51 +6,51 @@ import (
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestBasicACL_DisableExtension(t *testing.T) {
|
func TestBasic_DisableExtension(t *testing.T) {
|
||||||
var val, val2 BasicACL
|
var val, val2 Basic
|
||||||
|
|
||||||
require.True(t, val.Extendable())
|
require.True(t, val.Extendable())
|
||||||
val2.fromUint32(val.toUint32())
|
val2.FromBits(val.Bits())
|
||||||
require.True(t, val2.Extendable())
|
require.True(t, val2.Extendable())
|
||||||
|
|
||||||
val.DisableExtension()
|
val.DisableExtension()
|
||||||
|
|
||||||
require.False(t, val.Extendable())
|
require.False(t, val.Extendable())
|
||||||
val2.fromUint32(val.toUint32())
|
val2.FromBits(val.Bits())
|
||||||
require.False(t, val2.Extendable())
|
require.False(t, val2.Extendable())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBasicACL_MakeSticky(t *testing.T) {
|
func TestBasic_MakeSticky(t *testing.T) {
|
||||||
var val, val2 BasicACL
|
var val, val2 Basic
|
||||||
|
|
||||||
require.False(t, val.Sticky())
|
require.False(t, val.Sticky())
|
||||||
val2.fromUint32(val.toUint32())
|
val2.FromBits(val.Bits())
|
||||||
require.False(t, val2.Sticky())
|
require.False(t, val2.Sticky())
|
||||||
|
|
||||||
val.MakeSticky()
|
val.MakeSticky()
|
||||||
|
|
||||||
require.True(t, val.Sticky())
|
require.True(t, val.Sticky())
|
||||||
val2.fromUint32(val.toUint32())
|
val2.FromBits(val.Bits())
|
||||||
require.True(t, val2.Sticky())
|
require.True(t, val2.Sticky())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBasicACL_AllowBearerRules(t *testing.T) {
|
func TestBasic_AllowBearerRules(t *testing.T) {
|
||||||
var val BasicACL
|
var val Basic
|
||||||
|
|
||||||
require.Panics(t, func() { val.AllowBearerRules(aclOpZero) })
|
require.Panics(t, func() { val.AllowBearerRules(opZero) })
|
||||||
require.Panics(t, func() { val.AllowBearerRules(aclOpLast) })
|
require.Panics(t, func() { val.AllowBearerRules(opLast) })
|
||||||
|
|
||||||
require.Panics(t, func() { val.AllowedBearerRules(aclOpZero) })
|
require.Panics(t, func() { val.AllowedBearerRules(opZero) })
|
||||||
require.Panics(t, func() { val.AllowedBearerRules(aclOpLast) })
|
require.Panics(t, func() { val.AllowedBearerRules(opLast) })
|
||||||
|
|
||||||
for op := aclOpZero + 1; op < aclOpLast; op++ {
|
for op := opZero + 1; op < opLast; op++ {
|
||||||
val := val
|
val := val
|
||||||
|
|
||||||
require.False(t, val.AllowedBearerRules(op))
|
require.False(t, val.AllowedBearerRules(op))
|
||||||
|
|
||||||
val.AllowBearerRules(op)
|
val.AllowBearerRules(op)
|
||||||
|
|
||||||
for j := aclOpZero + 1; j < aclOpLast; j++ {
|
for j := opZero + 1; j < opLast; j++ {
|
||||||
if j == op {
|
if j == op {
|
||||||
require.True(t, val.AllowedBearerRules(j), op)
|
require.True(t, val.AllowedBearerRules(j), op)
|
||||||
} else {
|
} else {
|
||||||
|
@ -60,39 +60,39 @@ func TestBasicACL_AllowBearerRules(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBasicACL_AllowOp(t *testing.T) {
|
func TestBasic_AllowOp(t *testing.T) {
|
||||||
var val, val2 BasicACL
|
var val, val2 Basic
|
||||||
|
|
||||||
require.Panics(t, func() { val.IsOpAllowed(aclOpZero, aclRoleZero+1) })
|
require.Panics(t, func() { val.IsOpAllowed(opZero, roleZero+1) })
|
||||||
require.Panics(t, func() { val.IsOpAllowed(aclOpLast, aclRoleZero+1) })
|
require.Panics(t, func() { val.IsOpAllowed(opLast, roleZero+1) })
|
||||||
require.Panics(t, func() { val.IsOpAllowed(aclOpZero+1, aclRoleZero) })
|
require.Panics(t, func() { val.IsOpAllowed(opZero+1, roleZero) })
|
||||||
require.Panics(t, func() { val.IsOpAllowed(aclOpZero+1, aclRoleLast) })
|
require.Panics(t, func() { val.IsOpAllowed(opZero+1, roleLast) })
|
||||||
|
|
||||||
for op := aclOpZero + 1; op < aclOpLast; op++ {
|
for op := opZero + 1; op < opLast; op++ {
|
||||||
require.Panics(t, func() { val.AllowOp(op, ACLRoleInnerRing) })
|
require.Panics(t, func() { val.AllowOp(op, RoleInnerRing) })
|
||||||
|
|
||||||
if isReplicationOp(op) {
|
if isReplicationOp(op) {
|
||||||
require.Panics(t, func() { val.AllowOp(op, ACLRoleContainer) })
|
require.Panics(t, func() { val.AllowOp(op, RoleContainer) })
|
||||||
require.True(t, val.IsOpAllowed(op, ACLRoleContainer))
|
require.True(t, val.IsOpAllowed(op, RoleContainer))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
require.True(t, val.IsOpAllowed(ACLOpObjectGet, ACLRoleInnerRing))
|
require.True(t, val.IsOpAllowed(OpObjectGet, RoleInnerRing))
|
||||||
require.True(t, val.IsOpAllowed(ACLOpObjectHead, ACLRoleInnerRing))
|
require.True(t, val.IsOpAllowed(OpObjectHead, RoleInnerRing))
|
||||||
require.True(t, val.IsOpAllowed(ACLOpObjectSearch, ACLRoleInnerRing))
|
require.True(t, val.IsOpAllowed(OpObjectSearch, RoleInnerRing))
|
||||||
require.True(t, val.IsOpAllowed(ACLOpObjectHash, ACLRoleInnerRing))
|
require.True(t, val.IsOpAllowed(OpObjectHash, RoleInnerRing))
|
||||||
|
|
||||||
const op = aclOpZero + 1
|
const op = opZero + 1
|
||||||
const role = ACLRoleOthers
|
const role = RoleOthers
|
||||||
|
|
||||||
require.False(t, val.IsOpAllowed(op, role))
|
require.False(t, val.IsOpAllowed(op, role))
|
||||||
val2.fromUint32(val.toUint32())
|
val2.FromBits(val.Bits())
|
||||||
require.False(t, val2.IsOpAllowed(op, role))
|
require.False(t, val2.IsOpAllowed(op, role))
|
||||||
|
|
||||||
val.AllowOp(op, role)
|
val.AllowOp(op, role)
|
||||||
|
|
||||||
require.True(t, val.IsOpAllowed(op, role))
|
require.True(t, val.IsOpAllowed(op, role))
|
||||||
val2.fromUint32(val.toUint32())
|
val2.FromBits(val.Bits())
|
||||||
require.True(t, val2.IsOpAllowed(op, role))
|
require.True(t, val2.IsOpAllowed(op, role))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,21 +100,21 @@ type opsExpected struct {
|
||||||
owner, container, innerRing, others, bearer bool
|
owner, container, innerRing, others, bearer bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func testOp(t *testing.T, v BasicACL, op ACLOp, exp opsExpected) {
|
func testOp(t *testing.T, v Basic, op Op, exp opsExpected) {
|
||||||
require.Equal(t, exp.owner, v.IsOpAllowed(op, ACLRoleOwner), op)
|
require.Equal(t, exp.owner, v.IsOpAllowed(op, RoleOwner), op)
|
||||||
require.Equal(t, exp.container, v.IsOpAllowed(op, ACLRoleContainer), op)
|
require.Equal(t, exp.container, v.IsOpAllowed(op, RoleContainer), op)
|
||||||
require.Equal(t, exp.innerRing, v.IsOpAllowed(op, ACLRoleInnerRing), op)
|
require.Equal(t, exp.innerRing, v.IsOpAllowed(op, RoleInnerRing), op)
|
||||||
require.Equal(t, exp.others, v.IsOpAllowed(op, ACLRoleOthers), op)
|
require.Equal(t, exp.others, v.IsOpAllowed(op, RoleOthers), op)
|
||||||
require.Equal(t, exp.bearer, v.AllowedBearerRules(op), op)
|
require.Equal(t, exp.bearer, v.AllowedBearerRules(op), op)
|
||||||
}
|
}
|
||||||
|
|
||||||
type expected struct {
|
type expected struct {
|
||||||
extendable, sticky bool
|
extendable, sticky bool
|
||||||
|
|
||||||
mOps map[ACLOp]opsExpected
|
mOps map[Op]opsExpected
|
||||||
}
|
}
|
||||||
|
|
||||||
func testBasicACLPredefined(t *testing.T, val BasicACL, name string, exp expected) {
|
func testBasicPredefined(t *testing.T, val Basic, name string, exp expected) {
|
||||||
require.Equal(t, exp.sticky, val.Sticky())
|
require.Equal(t, exp.sticky, val.Sticky())
|
||||||
require.Equal(t, exp.extendable, val.Extendable())
|
require.Equal(t, exp.extendable, val.Extendable())
|
||||||
|
|
||||||
|
@ -124,7 +124,7 @@ func testBasicACLPredefined(t *testing.T, val BasicACL, name string, exp expecte
|
||||||
|
|
||||||
s := val.EncodeToString()
|
s := val.EncodeToString()
|
||||||
|
|
||||||
var val2 BasicACL
|
var val2 Basic
|
||||||
|
|
||||||
require.NoError(t, val2.DecodeString(s))
|
require.NoError(t, val2.DecodeString(s))
|
||||||
require.Equal(t, val, val2)
|
require.Equal(t, val, val2)
|
||||||
|
@ -133,55 +133,55 @@ func testBasicACLPredefined(t *testing.T, val BasicACL, name string, exp expecte
|
||||||
require.Equal(t, val, val2)
|
require.Equal(t, val, val2)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBasicACLPredefined(t *testing.T) {
|
func TestBasicPredefined(t *testing.T) {
|
||||||
t.Run("private", func(t *testing.T) {
|
t.Run("private", func(t *testing.T) {
|
||||||
exp := expected{
|
exp := expected{
|
||||||
extendable: false,
|
extendable: false,
|
||||||
sticky: false,
|
sticky: false,
|
||||||
mOps: map[ACLOp]opsExpected{
|
mOps: map[Op]opsExpected{
|
||||||
ACLOpObjectHash: {
|
OpObjectHash: {
|
||||||
owner: true,
|
owner: true,
|
||||||
container: true,
|
container: true,
|
||||||
innerRing: true,
|
innerRing: true,
|
||||||
others: false,
|
others: false,
|
||||||
bearer: false,
|
bearer: false,
|
||||||
},
|
},
|
||||||
ACLOpObjectRange: {
|
OpObjectRange: {
|
||||||
owner: true,
|
owner: true,
|
||||||
container: false,
|
container: false,
|
||||||
innerRing: false,
|
innerRing: false,
|
||||||
others: false,
|
others: false,
|
||||||
bearer: false,
|
bearer: false,
|
||||||
},
|
},
|
||||||
ACLOpObjectSearch: {
|
OpObjectSearch: {
|
||||||
owner: true,
|
owner: true,
|
||||||
container: true,
|
container: true,
|
||||||
innerRing: true,
|
innerRing: true,
|
||||||
others: false,
|
others: false,
|
||||||
bearer: false,
|
bearer: false,
|
||||||
},
|
},
|
||||||
ACLOpObjectDelete: {
|
OpObjectDelete: {
|
||||||
owner: true,
|
owner: true,
|
||||||
container: false,
|
container: false,
|
||||||
innerRing: false,
|
innerRing: false,
|
||||||
others: false,
|
others: false,
|
||||||
bearer: false,
|
bearer: false,
|
||||||
},
|
},
|
||||||
ACLOpObjectPut: {
|
OpObjectPut: {
|
||||||
owner: true,
|
owner: true,
|
||||||
container: true,
|
container: true,
|
||||||
innerRing: false,
|
innerRing: false,
|
||||||
others: false,
|
others: false,
|
||||||
bearer: false,
|
bearer: false,
|
||||||
},
|
},
|
||||||
ACLOpObjectHead: {
|
OpObjectHead: {
|
||||||
owner: true,
|
owner: true,
|
||||||
container: true,
|
container: true,
|
||||||
innerRing: true,
|
innerRing: true,
|
||||||
others: false,
|
others: false,
|
||||||
bearer: false,
|
bearer: false,
|
||||||
},
|
},
|
||||||
ACLOpObjectGet: {
|
OpObjectGet: {
|
||||||
owner: true,
|
owner: true,
|
||||||
container: true,
|
container: true,
|
||||||
innerRing: true,
|
innerRing: true,
|
||||||
|
@ -191,59 +191,59 @@ func TestBasicACLPredefined(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
testBasicACLPredefined(t, BasicACLPrivate, BasicACLNamePrivate, exp)
|
testBasicPredefined(t, Private, NamePrivate, exp)
|
||||||
exp.extendable = true
|
exp.extendable = true
|
||||||
testBasicACLPredefined(t, BasicACLPrivateExtended, BasicACLNamePrivateExtended, exp)
|
testBasicPredefined(t, PrivateExtended, NamePrivateExtended, exp)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("public-read", func(t *testing.T) {
|
t.Run("public-read", func(t *testing.T) {
|
||||||
exp := expected{
|
exp := expected{
|
||||||
extendable: false,
|
extendable: false,
|
||||||
sticky: false,
|
sticky: false,
|
||||||
mOps: map[ACLOp]opsExpected{
|
mOps: map[Op]opsExpected{
|
||||||
ACLOpObjectHash: {
|
OpObjectHash: {
|
||||||
owner: true,
|
owner: true,
|
||||||
container: true,
|
container: true,
|
||||||
innerRing: true,
|
innerRing: true,
|
||||||
others: true,
|
others: true,
|
||||||
bearer: true,
|
bearer: true,
|
||||||
},
|
},
|
||||||
ACLOpObjectRange: {
|
OpObjectRange: {
|
||||||
owner: true,
|
owner: true,
|
||||||
container: false,
|
container: false,
|
||||||
innerRing: false,
|
innerRing: false,
|
||||||
others: true,
|
others: true,
|
||||||
bearer: true,
|
bearer: true,
|
||||||
},
|
},
|
||||||
ACLOpObjectSearch: {
|
OpObjectSearch: {
|
||||||
owner: true,
|
owner: true,
|
||||||
container: true,
|
container: true,
|
||||||
innerRing: true,
|
innerRing: true,
|
||||||
others: true,
|
others: true,
|
||||||
bearer: true,
|
bearer: true,
|
||||||
},
|
},
|
||||||
ACLOpObjectDelete: {
|
OpObjectDelete: {
|
||||||
owner: true,
|
owner: true,
|
||||||
container: false,
|
container: false,
|
||||||
innerRing: false,
|
innerRing: false,
|
||||||
others: false,
|
others: false,
|
||||||
bearer: false,
|
bearer: false,
|
||||||
},
|
},
|
||||||
ACLOpObjectPut: {
|
OpObjectPut: {
|
||||||
owner: true,
|
owner: true,
|
||||||
container: true,
|
container: true,
|
||||||
innerRing: false,
|
innerRing: false,
|
||||||
others: false,
|
others: false,
|
||||||
bearer: false,
|
bearer: false,
|
||||||
},
|
},
|
||||||
ACLOpObjectHead: {
|
OpObjectHead: {
|
||||||
owner: true,
|
owner: true,
|
||||||
container: true,
|
container: true,
|
||||||
innerRing: true,
|
innerRing: true,
|
||||||
others: true,
|
others: true,
|
||||||
bearer: true,
|
bearer: true,
|
||||||
},
|
},
|
||||||
ACLOpObjectGet: {
|
OpObjectGet: {
|
||||||
owner: true,
|
owner: true,
|
||||||
container: true,
|
container: true,
|
||||||
innerRing: true,
|
innerRing: true,
|
||||||
|
@ -253,59 +253,59 @@ func TestBasicACLPredefined(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
testBasicACLPredefined(t, BasicACLPublicRO, BasicACLNamePublicRO, exp)
|
testBasicPredefined(t, PublicRO, NamePublicRO, exp)
|
||||||
exp.extendable = true
|
exp.extendable = true
|
||||||
testBasicACLPredefined(t, BasicACLPublicROExtended, BasicACLNamePublicROExtended, exp)
|
testBasicPredefined(t, PublicROExtended, NamePublicROExtended, exp)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("public-read-write", func(t *testing.T) {
|
t.Run("public-read-write", func(t *testing.T) {
|
||||||
exp := expected{
|
exp := expected{
|
||||||
extendable: false,
|
extendable: false,
|
||||||
sticky: false,
|
sticky: false,
|
||||||
mOps: map[ACLOp]opsExpected{
|
mOps: map[Op]opsExpected{
|
||||||
ACLOpObjectHash: {
|
OpObjectHash: {
|
||||||
owner: true,
|
owner: true,
|
||||||
container: true,
|
container: true,
|
||||||
innerRing: true,
|
innerRing: true,
|
||||||
others: true,
|
others: true,
|
||||||
bearer: true,
|
bearer: true,
|
||||||
},
|
},
|
||||||
ACLOpObjectRange: {
|
OpObjectRange: {
|
||||||
owner: true,
|
owner: true,
|
||||||
container: false,
|
container: false,
|
||||||
innerRing: false,
|
innerRing: false,
|
||||||
others: true,
|
others: true,
|
||||||
bearer: true,
|
bearer: true,
|
||||||
},
|
},
|
||||||
ACLOpObjectSearch: {
|
OpObjectSearch: {
|
||||||
owner: true,
|
owner: true,
|
||||||
container: true,
|
container: true,
|
||||||
innerRing: true,
|
innerRing: true,
|
||||||
others: true,
|
others: true,
|
||||||
bearer: true,
|
bearer: true,
|
||||||
},
|
},
|
||||||
ACLOpObjectDelete: {
|
OpObjectDelete: {
|
||||||
owner: true,
|
owner: true,
|
||||||
container: false,
|
container: false,
|
||||||
innerRing: false,
|
innerRing: false,
|
||||||
others: true,
|
others: true,
|
||||||
bearer: true,
|
bearer: true,
|
||||||
},
|
},
|
||||||
ACLOpObjectPut: {
|
OpObjectPut: {
|
||||||
owner: true,
|
owner: true,
|
||||||
container: true,
|
container: true,
|
||||||
innerRing: false,
|
innerRing: false,
|
||||||
others: true,
|
others: true,
|
||||||
bearer: true,
|
bearer: true,
|
||||||
},
|
},
|
||||||
ACLOpObjectHead: {
|
OpObjectHead: {
|
||||||
owner: true,
|
owner: true,
|
||||||
container: true,
|
container: true,
|
||||||
innerRing: true,
|
innerRing: true,
|
||||||
others: true,
|
others: true,
|
||||||
bearer: true,
|
bearer: true,
|
||||||
},
|
},
|
||||||
ACLOpObjectGet: {
|
OpObjectGet: {
|
||||||
owner: true,
|
owner: true,
|
||||||
container: true,
|
container: true,
|
||||||
innerRing: true,
|
innerRing: true,
|
||||||
|
@ -315,59 +315,59 @@ func TestBasicACLPredefined(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
testBasicACLPredefined(t, BasicACLPublicRW, BasicACLNamePublicRW, exp)
|
testBasicPredefined(t, PublicRW, NamePublicRW, exp)
|
||||||
exp.extendable = true
|
exp.extendable = true
|
||||||
testBasicACLPredefined(t, BasicACLPublicRWExtended, BasicACLNamePublicRWExtended, exp)
|
testBasicPredefined(t, PublicRWExtended, NamePublicRWExtended, exp)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("public-append", func(t *testing.T) {
|
t.Run("public-append", func(t *testing.T) {
|
||||||
exp := expected{
|
exp := expected{
|
||||||
extendable: false,
|
extendable: false,
|
||||||
sticky: false,
|
sticky: false,
|
||||||
mOps: map[ACLOp]opsExpected{
|
mOps: map[Op]opsExpected{
|
||||||
ACLOpObjectHash: {
|
OpObjectHash: {
|
||||||
owner: true,
|
owner: true,
|
||||||
container: true,
|
container: true,
|
||||||
innerRing: true,
|
innerRing: true,
|
||||||
others: true,
|
others: true,
|
||||||
bearer: true,
|
bearer: true,
|
||||||
},
|
},
|
||||||
ACLOpObjectRange: {
|
OpObjectRange: {
|
||||||
owner: true,
|
owner: true,
|
||||||
container: false,
|
container: false,
|
||||||
innerRing: false,
|
innerRing: false,
|
||||||
others: true,
|
others: true,
|
||||||
bearer: true,
|
bearer: true,
|
||||||
},
|
},
|
||||||
ACLOpObjectSearch: {
|
OpObjectSearch: {
|
||||||
owner: true,
|
owner: true,
|
||||||
container: true,
|
container: true,
|
||||||
innerRing: true,
|
innerRing: true,
|
||||||
others: true,
|
others: true,
|
||||||
bearer: true,
|
bearer: true,
|
||||||
},
|
},
|
||||||
ACLOpObjectDelete: {
|
OpObjectDelete: {
|
||||||
owner: true,
|
owner: true,
|
||||||
container: false,
|
container: false,
|
||||||
innerRing: false,
|
innerRing: false,
|
||||||
others: false,
|
others: false,
|
||||||
bearer: true,
|
bearer: true,
|
||||||
},
|
},
|
||||||
ACLOpObjectPut: {
|
OpObjectPut: {
|
||||||
owner: true,
|
owner: true,
|
||||||
container: true,
|
container: true,
|
||||||
innerRing: false,
|
innerRing: false,
|
||||||
others: true,
|
others: true,
|
||||||
bearer: true,
|
bearer: true,
|
||||||
},
|
},
|
||||||
ACLOpObjectHead: {
|
OpObjectHead: {
|
||||||
owner: true,
|
owner: true,
|
||||||
container: true,
|
container: true,
|
||||||
innerRing: true,
|
innerRing: true,
|
||||||
others: true,
|
others: true,
|
||||||
bearer: true,
|
bearer: true,
|
||||||
},
|
},
|
||||||
ACLOpObjectGet: {
|
OpObjectGet: {
|
||||||
owner: true,
|
owner: true,
|
||||||
container: true,
|
container: true,
|
||||||
innerRing: true,
|
innerRing: true,
|
||||||
|
@ -377,8 +377,8 @@ func TestBasicACLPredefined(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
testBasicACLPredefined(t, BasicACLPublicAppend, BasicACLNamePublicAppend, exp)
|
testBasicPredefined(t, PublicAppend, NamePublicAppend, exp)
|
||||||
exp.extendable = true
|
exp.extendable = true
|
||||||
testBasicACLPredefined(t, BasicACLPublicAppendExtended, BasicACLNamePublicAppendExtended, exp)
|
testBasicPredefined(t, PublicAppendExtended, NamePublicAppendExtended, exp)
|
||||||
})
|
})
|
||||||
}
|
}
|
9
container/acl/doc.go
Normal file
9
container/acl/doc.go
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
/*
|
||||||
|
Package acl provides functionality to control access to data and operations on them in NeoFS containers.
|
||||||
|
|
||||||
|
Type Basic represents basic ACL of the NeoFS container which specifies the general order of data access.
|
||||||
|
Basic provides interface of rule composition. Package acl also exports some frequently used settings like
|
||||||
|
private or public.
|
||||||
|
|
||||||
|
*/
|
||||||
|
package acl
|
30
container/acl/init.go
Normal file
30
container/acl/init.go
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
package acl
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// left-to-right order of the object operations
|
||||||
|
orderedOps := [...]Op{
|
||||||
|
OpObjectGet,
|
||||||
|
OpObjectHead,
|
||||||
|
OpObjectPut,
|
||||||
|
OpObjectDelete,
|
||||||
|
OpObjectSearch,
|
||||||
|
OpObjectRange,
|
||||||
|
OpObjectHash,
|
||||||
|
}
|
||||||
|
|
||||||
|
mOrder = make(map[Op]uint8, len(orderedOps))
|
||||||
|
|
||||||
|
for i := range orderedOps {
|
||||||
|
mOrder[orderedOps[i]] = uint8(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// numbers are taken from NeoFS Specification
|
||||||
|
Private.FromBits(0x1C8C8CCC)
|
||||||
|
PrivateExtended.FromBits(0x0C8C8CCC)
|
||||||
|
PublicRO.FromBits(0x1FBF8CFF)
|
||||||
|
PublicROExtended.FromBits(0x0FBF8CFF)
|
||||||
|
PublicRW.FromBits(0x1FBFBFFF)
|
||||||
|
PublicRWExtended.FromBits(0x0FBFBFFF)
|
||||||
|
PublicAppend.FromBits(0x1FBF9FFF)
|
||||||
|
PublicAppendExtended.FromBits(0x0FBF9FFF)
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package container
|
package acl
|
||||||
|
|
||||||
import "fmt"
|
import "fmt"
|
||||||
|
|
||||||
|
@ -21,11 +21,11 @@ func isBitSet(num uint32, n uint8) bool {
|
||||||
return mask != 0 && num&mask == mask
|
return mask != 0 && num&mask == mask
|
||||||
}
|
}
|
||||||
|
|
||||||
// maps ACLOp to op-section index in BasicACL. Filled on init.
|
// maps Op to op-section index in Basic. Filled on init.
|
||||||
var mOrder map[ACLOp]uint8
|
var mOrder map[Op]uint8
|
||||||
|
|
||||||
// sets n-th bit in num for the given op. Panics if op is unsupported.
|
// sets n-th bit in num for the given op. Panics if op is unsupported.
|
||||||
func setOpBit(num *uint32, op ACLOp, opBitPos uint8) {
|
func setOpBit(num *uint32, op Op, opBitPos uint8) {
|
||||||
n, ok := mOrder[op]
|
n, ok := mOrder[op]
|
||||||
if !ok {
|
if !ok {
|
||||||
panic(fmt.Sprintf("op is unsupported %v", op))
|
panic(fmt.Sprintf("op is unsupported %v", op))
|
||||||
|
@ -35,7 +35,7 @@ func setOpBit(num *uint32, op ACLOp, opBitPos uint8) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// checks if n-th bit in num for the given op is set. Panics if op is unsupported.
|
// checks if n-th bit in num for the given op is set. Panics if op is unsupported.
|
||||||
func isOpBitSet(num uint32, op ACLOp, n uint8) bool {
|
func isOpBitSet(num uint32, op Op, n uint8) bool {
|
||||||
off, ok := mOrder[op]
|
off, ok := mOrder[op]
|
||||||
if !ok {
|
if !ok {
|
||||||
panic(fmt.Sprintf("op is unsupported %v", op))
|
panic(fmt.Sprintf("op is unsupported %v", op))
|
|
@ -1,4 +1,4 @@
|
||||||
package container
|
package acl
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -30,23 +30,23 @@ func TestBits(t *testing.T) {
|
||||||
func TestOpBits(t *testing.T) {
|
func TestOpBits(t *testing.T) {
|
||||||
num := uint32(0b_1001_0101_1100_0011_0110_0111_1000_1111)
|
num := uint32(0b_1001_0101_1100_0011_0110_0111_1000_1111)
|
||||||
|
|
||||||
require.Panics(t, func() { isOpBitSet(num, aclOpZero, 0) })
|
require.Panics(t, func() { isOpBitSet(num, opZero, 0) })
|
||||||
require.Panics(t, func() { isOpBitSet(num, aclOpLast, 0) })
|
require.Panics(t, func() { isOpBitSet(num, opLast, 0) })
|
||||||
|
|
||||||
cpNum := num
|
cpNum := num
|
||||||
|
|
||||||
require.Panics(t, func() { setOpBit(&num, aclOpZero, 0) })
|
require.Panics(t, func() { setOpBit(&num, opZero, 0) })
|
||||||
require.EqualValues(t, cpNum, num)
|
require.EqualValues(t, cpNum, num)
|
||||||
require.Panics(t, func() { setOpBit(&num, aclOpLast, 0) })
|
require.Panics(t, func() { setOpBit(&num, opLast, 0) })
|
||||||
require.EqualValues(t, cpNum, num)
|
require.EqualValues(t, cpNum, num)
|
||||||
|
|
||||||
for _, tc := range []struct {
|
for _, tc := range []struct {
|
||||||
op ACLOp
|
op Op
|
||||||
set [4]bool // is bit set (left-to-right)
|
set [4]bool // is bit set (left-to-right)
|
||||||
bits [4]uint32 // result of setting i-th bit (left-to-right) to zero num
|
bits [4]uint32 // result of setting i-th bit (left-to-right) to zero num
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
op: ACLOpObjectHash,
|
op: OpObjectHash,
|
||||||
set: [4]bool{false, true, false, true},
|
set: [4]bool{false, true, false, true},
|
||||||
bits: [4]uint32{
|
bits: [4]uint32{
|
||||||
0b_0000_1000_0000_0000_0000_0000_0000_0000,
|
0b_0000_1000_0000_0000_0000_0000_0000_0000,
|
||||||
|
@ -56,7 +56,7 @@ func TestOpBits(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
op: ACLOpObjectRange,
|
op: OpObjectRange,
|
||||||
set: [4]bool{true, true, false, false},
|
set: [4]bool{true, true, false, false},
|
||||||
bits: [4]uint32{
|
bits: [4]uint32{
|
||||||
0b_0000_0000_1000_0000_0000_0000_0000_0000,
|
0b_0000_0000_1000_0000_0000_0000_0000_0000,
|
||||||
|
@ -66,7 +66,7 @@ func TestOpBits(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
op: ACLOpObjectSearch,
|
op: OpObjectSearch,
|
||||||
set: [4]bool{false, false, true, true},
|
set: [4]bool{false, false, true, true},
|
||||||
bits: [4]uint32{
|
bits: [4]uint32{
|
||||||
0b_0000_0000_0000_1000_0000_0000_0000_0000,
|
0b_0000_0000_0000_1000_0000_0000_0000_0000,
|
||||||
|
@ -76,7 +76,7 @@ func TestOpBits(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
op: ACLOpObjectDelete,
|
op: OpObjectDelete,
|
||||||
set: [4]bool{false, true, true, false},
|
set: [4]bool{false, true, true, false},
|
||||||
bits: [4]uint32{
|
bits: [4]uint32{
|
||||||
0b_0000_0000_0000_0000_1000_0000_0000_0000,
|
0b_0000_0000_0000_0000_1000_0000_0000_0000,
|
||||||
|
@ -86,7 +86,7 @@ func TestOpBits(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
op: ACLOpObjectPut,
|
op: OpObjectPut,
|
||||||
set: [4]bool{false, true, true, true},
|
set: [4]bool{false, true, true, true},
|
||||||
bits: [4]uint32{
|
bits: [4]uint32{
|
||||||
0b_0000_0000_0000_0000_0000_1000_0000_0000,
|
0b_0000_0000_0000_0000_0000_1000_0000_0000,
|
||||||
|
@ -96,7 +96,7 @@ func TestOpBits(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
op: ACLOpObjectHead,
|
op: OpObjectHead,
|
||||||
set: [4]bool{true, false, false, false},
|
set: [4]bool{true, false, false, false},
|
||||||
bits: [4]uint32{
|
bits: [4]uint32{
|
||||||
0b_0000_0000_0000_0000_0000_0000_1000_0000,
|
0b_0000_0000_0000_0000_0000_0000_1000_0000,
|
||||||
|
@ -106,7 +106,7 @@ func TestOpBits(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
op: ACLOpObjectGet,
|
op: OpObjectGet,
|
||||||
set: [4]bool{true, true, true, true},
|
set: [4]bool{true, true, true, true},
|
||||||
bits: [4]uint32{
|
bits: [4]uint32{
|
||||||
0b_0000_0000_0000_0000_0000_0000_0000_1000,
|
0b_0000_0000_0000_0000_0000_0000_0000_1000,
|
|
@ -1,272 +0,0 @@
|
||||||
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
|
|
||||||
}
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"github.com/nspcc-dev/neofs-api-go/v2/container"
|
"github.com/nspcc-dev/neofs-api-go/v2/container"
|
||||||
v2netmap "github.com/nspcc-dev/neofs-api-go/v2/netmap"
|
v2netmap "github.com/nspcc-dev/neofs-api-go/v2/netmap"
|
||||||
"github.com/nspcc-dev/neofs-api-go/v2/refs"
|
"github.com/nspcc-dev/neofs-api-go/v2/refs"
|
||||||
|
"github.com/nspcc-dev/neofs-sdk-go/container/acl"
|
||||||
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
|
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/netmap"
|
"github.com/nspcc-dev/neofs-sdk-go/netmap"
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/user"
|
"github.com/nspcc-dev/neofs-sdk-go/user"
|
||||||
|
@ -22,7 +23,7 @@ type Container struct {
|
||||||
// Defaults:
|
// Defaults:
|
||||||
// - token: nil;
|
// - token: nil;
|
||||||
// - sig: nil;
|
// - sig: nil;
|
||||||
// - basicACL: BasicACLPrivate;
|
// - basicACL: Private;
|
||||||
// - version: version.Current;
|
// - version: version.Current;
|
||||||
// - nonce: random UUID;
|
// - nonce: random UUID;
|
||||||
// - attr: nil;
|
// - attr: nil;
|
||||||
|
@ -135,13 +136,13 @@ func (c *Container) SetNonceUUID(v uuid.UUID) {
|
||||||
c.v2.SetNonce(data)
|
c.v2.SetNonce(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Container) BasicACL() (res BasicACL) {
|
func (c *Container) BasicACL() (res acl.Basic) {
|
||||||
res.fromUint32(c.v2.GetBasicACL())
|
res.FromBits(c.v2.GetBasicACL())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Container) SetBasicACL(v BasicACL) {
|
func (c *Container) SetBasicACL(v acl.Basic) {
|
||||||
c.v2.SetBasicACL(v.toUint32())
|
c.v2.SetBasicACL(v.Bits())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Container) Attributes() Attributes {
|
func (c *Container) Attributes() Attributes {
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/nspcc-dev/neofs-api-go/v2/refs"
|
"github.com/nspcc-dev/neofs-api-go/v2/refs"
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/container"
|
"github.com/nspcc-dev/neofs-sdk-go/container"
|
||||||
|
"github.com/nspcc-dev/neofs-sdk-go/container/acl"
|
||||||
containertest "github.com/nspcc-dev/neofs-sdk-go/container/test"
|
containertest "github.com/nspcc-dev/neofs-sdk-go/container/test"
|
||||||
netmaptest "github.com/nspcc-dev/neofs-sdk-go/netmap/test"
|
netmaptest "github.com/nspcc-dev/neofs-sdk-go/netmap/test"
|
||||||
usertest "github.com/nspcc-dev/neofs-sdk-go/user/test"
|
usertest "github.com/nspcc-dev/neofs-sdk-go/user/test"
|
||||||
|
@ -22,7 +23,7 @@ func TestNewContainer(t *testing.T) {
|
||||||
ownerID := usertest.ID()
|
ownerID := usertest.ID()
|
||||||
policy := netmaptest.PlacementPolicy()
|
policy := netmaptest.PlacementPolicy()
|
||||||
|
|
||||||
c.SetBasicACL(container.BasicACLPublicRW)
|
c.SetBasicACL(acl.PublicRW)
|
||||||
|
|
||||||
attrs := containertest.Attributes()
|
attrs := containertest.Attributes()
|
||||||
c.SetAttributes(attrs)
|
c.SetAttributes(attrs)
|
||||||
|
@ -39,7 +40,7 @@ func TestNewContainer(t *testing.T) {
|
||||||
|
|
||||||
require.EqualValues(t, newContainer.PlacementPolicy(), &policy)
|
require.EqualValues(t, newContainer.PlacementPolicy(), &policy)
|
||||||
require.EqualValues(t, newContainer.Attributes(), attrs)
|
require.EqualValues(t, newContainer.Attributes(), attrs)
|
||||||
require.EqualValues(t, newContainer.BasicACL(), container.BasicACLPublicRW)
|
require.EqualValues(t, newContainer.BasicACL(), acl.PublicRW)
|
||||||
|
|
||||||
newNonce, err := newContainer.NonceUUID()
|
newNonce, err := newContainer.NonceUUID()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -88,7 +89,7 @@ func TestContainer_ToV2(t *testing.T) {
|
||||||
require.Nil(t, cnt.PlacementPolicy())
|
require.Nil(t, cnt.PlacementPolicy())
|
||||||
require.Nil(t, cnt.OwnerID())
|
require.Nil(t, cnt.OwnerID())
|
||||||
|
|
||||||
require.EqualValues(t, container.BasicACLPrivate, cnt.BasicACL())
|
require.EqualValues(t, acl.Private, cnt.BasicACL())
|
||||||
require.Equal(t, version.Current(), *cnt.Version())
|
require.Equal(t, version.Current(), *cnt.Version())
|
||||||
|
|
||||||
nonce, err := cnt.NonceUUID()
|
nonce, err := cnt.NonceUUID()
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
package container
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
// left-to-right order of the object operations
|
|
||||||
orderedOps := [...]ACLOp{
|
|
||||||
ACLOpObjectGet,
|
|
||||||
ACLOpObjectHead,
|
|
||||||
ACLOpObjectPut,
|
|
||||||
ACLOpObjectDelete,
|
|
||||||
ACLOpObjectSearch,
|
|
||||||
ACLOpObjectRange,
|
|
||||||
ACLOpObjectHash,
|
|
||||||
}
|
|
||||||
|
|
||||||
mOrder = make(map[ACLOp]uint8, len(orderedOps))
|
|
||||||
|
|
||||||
for i := range orderedOps {
|
|
||||||
mOrder[orderedOps[i]] = uint8(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
// numbers are taken from NeoFS Specification
|
|
||||||
BasicACLPrivate.fromUint32(0x1C8C8CCC)
|
|
||||||
BasicACLPrivateExtended.fromUint32(0x0C8C8CCC)
|
|
||||||
BasicACLPublicRO.fromUint32(0x1FBF8CFF)
|
|
||||||
BasicACLPublicROExtended.fromUint32(0x0FBF8CFF)
|
|
||||||
BasicACLPublicRW.fromUint32(0x1FBFBFFF)
|
|
||||||
BasicACLPublicRWExtended.fromUint32(0x0FBFBFFF)
|
|
||||||
BasicACLPublicAppend.fromUint32(0x1FBF9FFF)
|
|
||||||
BasicACLPublicAppendExtended.fromUint32(0x0FBF9FFF)
|
|
||||||
}
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
"github.com/nspcc-dev/neofs-sdk-go/container/acl"
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/netmap"
|
"github.com/nspcc-dev/neofs-sdk-go/netmap"
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/user"
|
"github.com/nspcc-dev/neofs-sdk-go/user"
|
||||||
)
|
)
|
||||||
|
@ -12,7 +13,7 @@ type (
|
||||||
Option func(*containerOptions)
|
Option func(*containerOptions)
|
||||||
|
|
||||||
containerOptions struct {
|
containerOptions struct {
|
||||||
acl BasicACL
|
acl acl.Basic
|
||||||
policy *netmap.PlacementPolicy
|
policy *netmap.PlacementPolicy
|
||||||
attributes Attributes
|
attributes Attributes
|
||||||
owner *user.ID
|
owner *user.ID
|
||||||
|
@ -27,24 +28,24 @@ func defaultContainerOptions() containerOptions {
|
||||||
}
|
}
|
||||||
|
|
||||||
return containerOptions{
|
return containerOptions{
|
||||||
acl: BasicACLPrivate,
|
acl: acl.Private,
|
||||||
nonce: rand,
|
nonce: rand,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithPublicBasicACL() Option {
|
func WithPublicBasicACL() Option {
|
||||||
return func(option *containerOptions) {
|
return func(option *containerOptions) {
|
||||||
option.acl = BasicACLPublicRW
|
option.acl = acl.PublicRW
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithReadOnlyBasicACL() Option {
|
func WithReadOnlyBasicACL() Option {
|
||||||
return func(option *containerOptions) {
|
return func(option *containerOptions) {
|
||||||
option.acl = BasicACLPublicRO
|
option.acl = acl.PublicRO
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithCustomBasicACL(acl BasicACL) Option {
|
func WithCustomBasicACL(acl acl.Basic) Option {
|
||||||
return func(option *containerOptions) {
|
return func(option *containerOptions) {
|
||||||
option.acl = acl
|
option.acl = acl
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package containertest
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/container"
|
"github.com/nspcc-dev/neofs-sdk-go/container"
|
||||||
|
"github.com/nspcc-dev/neofs-sdk-go/container/acl"
|
||||||
cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test"
|
cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test"
|
||||||
netmaptest "github.com/nspcc-dev/neofs-sdk-go/netmap/test"
|
netmaptest "github.com/nspcc-dev/neofs-sdk-go/netmap/test"
|
||||||
usertest "github.com/nspcc-dev/neofs-sdk-go/user/test"
|
usertest "github.com/nspcc-dev/neofs-sdk-go/user/test"
|
||||||
|
@ -31,7 +32,7 @@ func Container() *container.Container {
|
||||||
x.SetVersion(&ver)
|
x.SetVersion(&ver)
|
||||||
x.SetAttributes(Attributes())
|
x.SetAttributes(Attributes())
|
||||||
x.SetOwnerID(usertest.ID())
|
x.SetOwnerID(usertest.ID())
|
||||||
x.SetBasicACL(container.BasicACLPublicRW)
|
x.SetBasicACL(BasicACL())
|
||||||
p := netmaptest.PlacementPolicy()
|
p := netmaptest.PlacementPolicy()
|
||||||
x.SetPlacementPolicy(&p)
|
x.SetPlacementPolicy(&p)
|
||||||
|
|
||||||
|
@ -48,3 +49,9 @@ func UsedSpaceAnnouncement() *container.UsedSpaceAnnouncement {
|
||||||
|
|
||||||
return x
|
return x
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BasicACL returns random acl.Basic.
|
||||||
|
func BasicACL() (x acl.Basic) {
|
||||||
|
x.FromBits(0xffffffff)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue