[#1533] acl: Upgrade NeoFS SDK Go with refactored basic ACL

Signed-off-by: Leonard Lyubich <leonard@nspcc.ru>
This commit is contained in:
Leonard Lyubich 2022-06-17 16:40:51 +03:00 committed by fyrchik
parent b13dca8052
commit 305dd7598f
15 changed files with 113 additions and 427 deletions

View file

@ -13,8 +13,8 @@ import (
"github.com/nspcc-dev/neofs-node/cmd/neofs-cli/internal/common" "github.com/nspcc-dev/neofs-node/cmd/neofs-cli/internal/common"
"github.com/nspcc-dev/neofs-node/cmd/neofs-cli/internal/commonflags" "github.com/nspcc-dev/neofs-node/cmd/neofs-cli/internal/commonflags"
"github.com/nspcc-dev/neofs-node/cmd/neofs-cli/internal/key" "github.com/nspcc-dev/neofs-node/cmd/neofs-cli/internal/key"
"github.com/nspcc-dev/neofs-sdk-go/acl"
"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"
"github.com/nspcc-dev/neofs-sdk-go/netmap" "github.com/nspcc-dev/neofs-sdk-go/netmap"
"github.com/nspcc-dev/neofs-sdk-go/session" "github.com/nspcc-dev/neofs-sdk-go/session"
subnetid "github.com/nspcc-dev/neofs-sdk-go/subnet/id" subnetid "github.com/nspcc-dev/neofs-sdk-go/subnet/id"
@ -23,31 +23,6 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
// keywords of predefined basic ACL values
const (
basicACLPrivate = "private"
basicACLReadOnly = "public-read"
basicACLPublic = "public-read-write"
basicACLAppend = "public-append"
basicACLNoFinalPrivate = "eacl-private"
basicACLNoFinalReadOnly = "eacl-public-read"
basicACLNoFinalPublic = "eacl-public-read-write"
basicACLNoFinalAppend = "eacl-public-append"
)
var wellKnownBasicACL = map[string]acl.BasicACL{
basicACLPublic: acl.PublicBasicRule,
basicACLPrivate: acl.PrivateBasicRule,
basicACLReadOnly: acl.ReadOnlyBasicRule,
basicACLAppend: acl.PublicAppendRule,
basicACLNoFinalPublic: acl.EACLPublicBasicRule,
basicACLNoFinalPrivate: acl.EACLPrivateBasicRule,
basicACLNoFinalReadOnly: acl.EACLReadOnlyBasicRule,
basicACLNoFinalAppend: acl.EACLPublicAppendRule,
}
var ( var (
containerACL string containerACL string
containerNonce string containerNonce string
@ -80,8 +55,8 @@ It will be stored in sidechain when inner ring will accepts it.`,
attributes, err := parseAttributes(containerAttributes) attributes, err := parseAttributes(containerAttributes)
common.ExitOnErr(cmd, "", err) common.ExitOnErr(cmd, "", err)
basicACL, err := parseBasicACL(containerACL) var basicACL acl.Basic
common.ExitOnErr(cmd, "", err) common.ExitOnErr(cmd, "decode basic ACL string: %w", basicACL.DecodeString(containerACL))
nonce, err := parseNonce(containerNonce) nonce, err := parseNonce(containerNonce)
common.ExitOnErr(cmd, "", err) common.ExitOnErr(cmd, "", err)
@ -157,7 +132,9 @@ func initContainerCreateCmd() {
flags := createContainerCmd.Flags() flags := createContainerCmd.Flags()
flags.StringVar(&containerACL, "basic-acl", basicACLPrivate, fmt.Sprintf("hex encoded basic ACL value or keywords like '%s', '%s', '%s'", basicACLPublic, basicACLPrivate, basicACLNoFinalReadOnly)) flags.StringVar(&containerACL, "basic-acl", acl.NamePrivate, fmt.Sprintf("hex encoded basic ACL value or keywords like '%s', '%s', '%s'",
acl.NamePublicRW, acl.NamePrivate, acl.NamePublicROExtended,
))
flags.StringVarP(&containerPolicy, "policy", "p", "", "QL-encoded or JSON-encoded placement policy or path to file with it") flags.StringVarP(&containerPolicy, "policy", "p", "", "QL-encoded or JSON-encoded placement policy or path to file with it")
flags.StringSliceVarP(&containerAttributes, "attributes", "a", nil, "comma separated pairs of container attributes in form of Key1=Value1,Key2=Value2") flags.StringSliceVarP(&containerAttributes, "attributes", "a", nil, "comma separated pairs of container attributes in form of Key1=Value1,Key2=Value2")
flags.StringVarP(&containerNonce, "nonce", "n", "", "UUIDv4 nonce value for container") flags.StringVarP(&containerNonce, "nonce", "n", "", "UUIDv4 nonce value for container")
@ -226,21 +203,6 @@ func parseAttributes(attributes []string) ([]container.Attribute, error) {
return result, nil return result, nil
} }
func parseBasicACL(basicACL string) (acl.BasicACL, error) {
if value, ok := wellKnownBasicACL[basicACL]; ok {
return value, nil
}
basicACL = strings.Trim(strings.ToLower(basicACL), "0x")
value, err := strconv.ParseUint(basicACL, 16, 32)
if err != nil {
return 0, fmt.Errorf("can't parse basic ACL: %s", basicACL)
}
return acl.BasicACL(value), nil
}
func parseNonce(nonce string) (uuid.UUID, error) { func parseNonce(nonce string) (uuid.UUID, error) {
if nonce == "" { if nonce == "" {
result := uuid.New() result := uuid.New()

View file

@ -9,8 +9,8 @@ import (
"github.com/nspcc-dev/neofs-node/cmd/neofs-cli/internal/common" "github.com/nspcc-dev/neofs-node/cmd/neofs-cli/internal/common"
"github.com/nspcc-dev/neofs-node/cmd/neofs-cli/internal/commonflags" "github.com/nspcc-dev/neofs-node/cmd/neofs-cli/internal/commonflags"
"github.com/nspcc-dev/neofs-node/cmd/neofs-cli/internal/key" "github.com/nspcc-dev/neofs-node/cmd/neofs-cli/internal/key"
"github.com/nspcc-dev/neofs-sdk-go/acl"
"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"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -120,7 +120,7 @@ func prettyPrintContainer(cmd *cobra.Command, cnr *container.Container, jsonEnco
cmd.Println("owner ID:", cnr.OwnerID()) cmd.Println("owner ID:", cnr.OwnerID())
basicACL := cnr.BasicACL() basicACL := cnr.BasicACL()
prettyPrintBasicACL(cmd, acl.BasicACL(basicACL)) prettyPrintBasicACL(cmd, basicACL)
for _, attribute := range cnr.Attributes() { for _, attribute := range cnr.Attributes() {
if attribute.Key() == container.AttributeTimestamp { if attribute.Key() == container.AttributeTimestamp {
@ -152,13 +152,33 @@ func prettyPrintContainer(cmd *cobra.Command, cnr *container.Container, jsonEnco
} }
} }
func prettyPrintBasicACL(cmd *cobra.Command, basicACL acl.BasicACL) { func prettyPrintBasicACL(cmd *cobra.Command, basicACL acl.Basic) {
cmd.Printf("basic ACL: %s", basicACL) cmd.Printf("basic ACL: %s", basicACL.EncodeToString())
for k, v := range wellKnownBasicACL {
if v == basicACL { var prettyName string
cmd.Printf(" (%s)\n", k)
return switch basicACL {
} case acl.Private:
prettyName = acl.NamePrivate
case acl.PrivateExtended:
prettyName = acl.NamePrivateExtended
case acl.PublicRO:
prettyName = acl.NamePublicRO
case acl.PublicROExtended:
prettyName = acl.NamePublicROExtended
case acl.PublicRW:
prettyName = acl.NamePublicRW
case acl.PublicRWExtended:
prettyName = acl.NamePublicRWExtended
case acl.PublicAppend:
prettyName = acl.NamePublicAppend
case acl.PublicAppendExtended:
prettyName = acl.NamePublicAppendExtended
} }
if prettyName != "" {
cmd.Printf(" (%s)", prettyName)
}
cmd.Println() cmd.Println()
} }

2
go.mod
View file

@ -19,7 +19,7 @@ require (
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20220601120906-3bec6657f5c5 // indirect github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20220601120906-3bec6657f5c5 // indirect
github.com/nspcc-dev/neofs-api-go/v2 v2.12.3-0.20220621170933-dd233c3fbc84 github.com/nspcc-dev/neofs-api-go/v2 v2.12.3-0.20220621170933-dd233c3fbc84
github.com/nspcc-dev/neofs-contract v0.15.1 github.com/nspcc-dev/neofs-contract v0.15.1
github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.4.0.20220622125136-af7e20073bc6 github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.4.0.20220623080532-aa5ee1dcde1c
github.com/nspcc-dev/tzhash v1.5.2 github.com/nspcc-dev/tzhash v1.5.2
github.com/panjf2000/ants/v2 v2.4.0 github.com/panjf2000/ants/v2 v2.4.0
github.com/paulmach/orb v0.2.2 github.com/paulmach/orb v0.2.2

BIN
go.sum

Binary file not shown.

View file

@ -52,12 +52,7 @@ func (cp *Processor) checkSetEACL(e container.SetEACL) error {
} }
// ACL extensions can be disabled by basic ACL, check it // ACL extensions can be disabled by basic ACL, check it
basicACL := cnr.Value.BasicACL() if !cnr.Value.BasicACL().Extendable() {
const finalBitMask = 1 << 28
// Temp solution: NeoFS SDK is going to provide convenient interface to do this soon.
// This place won't be missed since BasicACL() signature will be changed.
if basicACL&finalBitMask == finalBitMask {
return errors.New("ACL extension disabled by container basic ACL") return errors.New("ACL extension disabled by container basic ACL")
} }

View file

@ -14,6 +14,7 @@ import (
eaclV2 "github.com/nspcc-dev/neofs-node/pkg/services/object/acl/eacl/v2" eaclV2 "github.com/nspcc-dev/neofs-node/pkg/services/object/acl/eacl/v2"
v2 "github.com/nspcc-dev/neofs-node/pkg/services/object/acl/v2" v2 "github.com/nspcc-dev/neofs-node/pkg/services/object/acl/v2"
bearerSDK "github.com/nspcc-dev/neofs-sdk-go/bearer" bearerSDK "github.com/nspcc-dev/neofs-sdk-go/bearer"
"github.com/nspcc-dev/neofs-sdk-go/container/acl"
neofsecdsa "github.com/nspcc-dev/neofs-sdk-go/crypto/ecdsa" neofsecdsa "github.com/nspcc-dev/neofs-sdk-go/crypto/ecdsa"
eaclSDK "github.com/nspcc-dev/neofs-sdk-go/eacl" eaclSDK "github.com/nspcc-dev/neofs-sdk-go/eacl"
"github.com/nspcc-dev/neofs-sdk-go/user" "github.com/nspcc-dev/neofs-sdk-go/user"
@ -91,35 +92,18 @@ func NewChecker(prm *CheckerPrm) *Checker {
// CheckBasicACL is a main check function for basic ACL. // CheckBasicACL is a main check function for basic ACL.
func (c *Checker) CheckBasicACL(info v2.RequestInfo) bool { func (c *Checker) CheckBasicACL(info v2.RequestInfo) bool {
// check basic ACL permissions // check basic ACL permissions
var checkFn func(eaclSDK.Operation) bool return info.BasicACL().IsOpAllowed(info.Operation(), info.RequestRole())
switch info.RequestRole() {
case eaclSDK.RoleUser:
checkFn = basicACLHelper(info.BasicACL()).UserAllowed
case eaclSDK.RoleSystem:
checkFn = basicACLHelper(info.BasicACL()).SystemAllowed
if info.IsInnerRing() {
checkFn = basicACLHelper(info.BasicACL()).InnerRingAllowed
}
case eaclSDK.RoleOthers:
checkFn = basicACLHelper(info.BasicACL()).OthersAllowed
default:
// log there
return false
}
return checkFn(info.Operation())
} }
// StickyBitCheck validates owner field in the request if sticky bit is enabled. // StickyBitCheck validates owner field in the request if sticky bit is enabled.
func (c *Checker) StickyBitCheck(info v2.RequestInfo, owner user.ID) bool { func (c *Checker) StickyBitCheck(info v2.RequestInfo, owner user.ID) bool {
// According to NeoFS specification sticky bit has no effect on system nodes // According to NeoFS specification sticky bit has no effect on system nodes
// for correct intra-container work with objects (in particular, replication). // for correct intra-container work with objects (in particular, replication).
if info.RequestRole() == eaclSDK.RoleSystem { if info.RequestRole() == acl.RoleContainer {
return true return true
} }
if !basicACLHelper(info.BasicACL()).Sticky() { if !info.BasicACL().Sticky() {
return true return true
} }
@ -134,12 +118,13 @@ func (c *Checker) StickyBitCheck(info v2.RequestInfo, owner user.ID) bool {
// CheckEACL is a main check function for extended ACL. // CheckEACL is a main check function for extended ACL.
func (c *Checker) CheckEACL(msg interface{}, reqInfo v2.RequestInfo) error { func (c *Checker) CheckEACL(msg interface{}, reqInfo v2.RequestInfo) error {
if basicACLHelper(reqInfo.BasicACL()).Final() { basicACL := reqInfo.BasicACL()
if !basicACL.Extendable() {
return nil return nil
} }
// if bearer token is not allowed, then ignore it // if bearer token is not allowed, then ignore it
if !basicACLHelper(reqInfo.BasicACL()).BearerAllowed(reqInfo.Operation()) { if !basicACL.AllowedBearerRules(reqInfo.Operation()) {
reqInfo.CleanBearer() reqInfo.CleanBearer()
} }
@ -190,9 +175,21 @@ func (c *Checker) CheckEACL(msg interface{}, reqInfo v2.RequestInfo) error {
return fmt.Errorf("can't parse headers: %w", err) return fmt.Errorf("can't parse headers: %w", err)
} }
var eaclRole eaclSDK.Role
switch op := reqInfo.RequestRole(); op {
default:
eaclRole = eaclSDK.Role(op)
case acl.RoleOwner:
eaclRole = eaclSDK.RoleUser
case acl.RoleInnerRing, acl.RoleContainer:
eaclRole = eaclSDK.RoleSystem
case acl.RoleOthers:
eaclRole = eaclSDK.RoleOthers
}
action, _ := c.validator.CalculateAction(new(eaclSDK.ValidationUnit). action, _ := c.validator.CalculateAction(new(eaclSDK.ValidationUnit).
WithRole(reqInfo.RequestRole()). WithRole(eaclRole).
WithOperation(reqInfo.Operation()). WithOperation(eaclSDK.Operation(reqInfo.Operation())).
WithContainerID(&cnr). WithContainerID(&cnr).
WithSenderKey(reqInfo.SenderKey()). WithSenderKey(reqInfo.SenderKey()).
WithHeaderSource(hdrSrc). WithHeaderSource(hdrSrc).

View file

@ -6,6 +6,7 @@ import (
"github.com/nspcc-dev/neofs-node/pkg/core/container" "github.com/nspcc-dev/neofs-node/pkg/core/container"
"github.com/nspcc-dev/neofs-node/pkg/local_object_storage/engine" "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/engine"
v2 "github.com/nspcc-dev/neofs-node/pkg/services/object/acl/v2" v2 "github.com/nspcc-dev/neofs-node/pkg/services/object/acl/v2"
"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"
eaclSDK "github.com/nspcc-dev/neofs-sdk-go/eacl" eaclSDK "github.com/nspcc-dev/neofs-sdk-go/eacl"
"github.com/nspcc-dev/neofs-sdk-go/user" "github.com/nspcc-dev/neofs-sdk-go/user"
@ -37,13 +38,14 @@ func TestStickyCheck(t *testing.T) {
var info v2.RequestInfo var info v2.RequestInfo
info.SetSenderKey(make([]byte, 33)) // any non-empty key info.SetSenderKey(make([]byte, 33)) // any non-empty key
info.SetRequestRole(eaclSDK.RoleSystem) info.SetRequestRole(acl.RoleContainer)
setSticky(&info, true)
require.True(t, checker.StickyBitCheck(info, *usertest.ID())) require.True(t, checker.StickyBitCheck(info, *usertest.ID()))
setSticky(&info, false) var basicACL acl.Basic
basicACL.MakeSticky()
info.SetBasicACL(basicACL)
require.True(t, checker.StickyBitCheck(info, *usertest.ID())) require.True(t, checker.StickyBitCheck(info, *usertest.ID()))
}) })
@ -51,13 +53,15 @@ func TestStickyCheck(t *testing.T) {
t.Run("owner ID and/or public key emptiness", func(t *testing.T) { t.Run("owner ID and/or public key emptiness", func(t *testing.T) {
var info v2.RequestInfo var info v2.RequestInfo
info.SetRequestRole(eaclSDK.RoleOthers) // should be non-system role info.SetRequestRole(acl.RoleOthers) // should be non-system role
assertFn := func(isSticky, withKey, withOwner, expected bool) { assertFn := func(isSticky, withKey, withOwner, expected bool) {
info := info
if isSticky { if isSticky {
setSticky(&info, true) var basicACL acl.Basic
} else { basicACL.MakeSticky()
setSticky(&info, false)
info.SetBasicACL(basicACL)
} }
if withKey { if withKey {
@ -84,15 +88,3 @@ func TestStickyCheck(t *testing.T) {
assertFn(false, true, true, true) assertFn(false, true, true, true)
}) })
} }
func setSticky(req *v2.RequestInfo, enabled bool) {
bh := basicACLHelper(req.BasicACL())
if enabled {
bh.SetSticky()
} else {
bh.ResetSticky()
}
req.SetBasicACL(uint32(bh))
}

View file

@ -1,193 +0,0 @@
package acl
import (
"github.com/nspcc-dev/neofs-sdk-go/eacl"
)
// wrapper around basic ACL to provide easy access to basic ACL fields
type basicACLHelper uint32
const (
reservedBitNumber = 2 // first left bits are reserved
stickyBitPos = reservedBitNumber // X-bit after reserved bits
finalBitPos = stickyBitPos + 1 // F-bit after X-bit
)
const (
opOffset = finalBitPos + 1 // offset of operation bits
bitsPerOp = 4 // number of bits per operation
opNumber = 7 // number of operation bit sections
)
const (
bitUser uint8 = iota
bitSystem
bitOthers
bitBearer
)
const leftACLBitPos = opOffset + bitsPerOp*opNumber - 1
var order = map[eacl.Operation]uint8{
eacl.OperationRangeHash: 0,
eacl.OperationRange: 1,
eacl.OperationSearch: 2,
eacl.OperationDelete: 3,
eacl.OperationPut: 4,
eacl.OperationHead: 5,
eacl.OperationGet: 6,
}
// returns true if n-th left bit is set (starting at 0).
func isLeftBitSet(value basicACLHelper, n uint8) bool {
bitMask := basicACLHelper(1 << (leftACLBitPos - n))
return bitMask != 0 && value&bitMask == bitMask
}
// sets n-th left bit (starting at 0).
func setLeftBit(value *basicACLHelper, n uint8) {
*value |= basicACLHelper(1 << (leftACLBitPos - n))
}
// resets n-th left bit (starting at 0).
func resetLeftBit(value *basicACLHelper, n uint8) {
*value &= ^basicACLHelper(1 << (leftACLBitPos - n))
}
// Final returns true if final option is enabled in ACL.
func (a basicACLHelper) Final() bool {
return isLeftBitSet(a, finalBitPos)
}
// SetFinal enables final option in ACL.
func (a *basicACLHelper) SetFinal() {
setLeftBit(a, finalBitPos)
}
// ResetFinal disables final option in ACL.
func (a *basicACLHelper) ResetFinal() {
resetLeftBit(a, finalBitPos)
}
// Sticky returns true if sticky option is enabled in ACL.
func (a basicACLHelper) Sticky() bool {
return isLeftBitSet(a, stickyBitPos)
}
// SetSticky enables the sticky option in ACL.
func (a *basicACLHelper) SetSticky() {
setLeftBit(a, stickyBitPos)
}
// ResetSticky disables the sticky option in ACL.
func (a *basicACLHelper) ResetSticky() {
resetLeftBit(a, stickyBitPos)
}
// UserAllowed returns true if user allowed the n-th operation in ACL.
func (a basicACLHelper) UserAllowed(op eacl.Operation) bool {
if n, ok := order[op]; ok {
return isLeftBitSet(a, opOffset+n*bitsPerOp+bitUser)
}
return false
}
// AllowUser allows user the n-th operation in ACL.
func (a *basicACLHelper) AllowUser(op eacl.Operation) {
if n, ok := order[op]; ok {
setLeftBit(a, opOffset+n*bitsPerOp+bitUser)
}
}
// ForbidUser forbids user the n-th operation in ACL.
func (a *basicACLHelper) ForbidUser(op eacl.Operation) {
if n, ok := order[op]; ok {
resetLeftBit(a, opOffset+n*bitsPerOp+bitUser)
}
}
// SystemAllowed returns true if System group allowed the n-th operation is set in ACL.
func (a basicACLHelper) SystemAllowed(op eacl.Operation) bool {
if op != eacl.OperationDelete && op != eacl.OperationRange {
return true
}
if n, ok := order[op]; ok {
return isLeftBitSet(a, opOffset+n*bitsPerOp+bitSystem)
}
return false
}
// InnerRingAllowed returns true if the operation is allowed by ACL for
// InnerRing nodes, as part of System group.
func (a basicACLHelper) InnerRingAllowed(op eacl.Operation) bool {
switch op {
case eacl.OperationSearch, eacl.OperationRangeHash, eacl.OperationHead, eacl.OperationGet:
return true
default:
if n, ok := order[op]; ok {
return isLeftBitSet(a, opOffset+n*bitsPerOp+bitSystem)
}
return false
}
}
// AllowSystem allows System group the n-th operation in ACL.
func (a *basicACLHelper) AllowSystem(op eacl.Operation) {
if n, ok := order[op]; ok {
setLeftBit(a, opOffset+n*bitsPerOp+bitSystem)
}
}
// ForbidSystem forbids System group the n-th operation in ACL.
func (a *basicACLHelper) ForbidSystem(op eacl.Operation) {
if n, ok := order[op]; ok {
resetLeftBit(a, opOffset+n*bitsPerOp+bitSystem)
}
}
// OthersAllowed returns true if Others group allowed the n-th operation is set in ACL.
func (a basicACLHelper) OthersAllowed(op eacl.Operation) bool {
if n, ok := order[op]; ok {
return isLeftBitSet(a, opOffset+n*bitsPerOp+bitOthers)
}
return false
}
// AllowOthers allows Others group the n-th operation in ACL.
func (a *basicACLHelper) AllowOthers(op eacl.Operation) {
if n, ok := order[op]; ok {
setLeftBit(a, opOffset+n*bitsPerOp+bitOthers)
}
}
// ForbidOthers forbids Others group the n-th operation in ACL.
func (a *basicACLHelper) ForbidOthers(op eacl.Operation) {
if n, ok := order[op]; ok {
resetLeftBit(a, opOffset+n*bitsPerOp+bitOthers)
}
}
// BearerAllowed returns true if Bearer token usage is allowed for n-th operation in ACL.
func (a basicACLHelper) BearerAllowed(op eacl.Operation) bool {
if n, ok := order[op]; ok {
return isLeftBitSet(a, opOffset+n*bitsPerOp+bitBearer)
}
return false
}
// AllowBearer allows Bearer token usage for n-th operation in ACL.
func (a *basicACLHelper) AllowBearer(op eacl.Operation) {
if n, ok := order[op]; ok {
setLeftBit(a, opOffset+n*bitsPerOp+bitBearer)
}
}
// ForbidBearer forbids Bearer token usage for n-th operation in ACL.
func (a *basicACLHelper) ForbidBearer(op eacl.Operation) {
if n, ok := order[op]; ok {
resetLeftBit(a, opOffset+n*bitsPerOp+bitBearer)
}
}

View file

@ -1,70 +0,0 @@
package acl
import (
"testing"
"github.com/nspcc-dev/neofs-sdk-go/eacl"
"github.com/stretchr/testify/require"
)
// from neofs-api basic ACL specification
const (
privateContainer uint32 = 0x1C8C8CCC
publicContainerWithSticky uint32 = 0x3FFFFFFF
readonlyContainer uint32 = 0x1FFFCCFF
)
var (
allOperations = []eacl.Operation{
eacl.OperationGet, eacl.OperationPut, eacl.OperationDelete,
eacl.OperationHead, eacl.OperationSearch, eacl.OperationRange,
eacl.OperationRangeHash,
}
)
func TestDefaultBasicACLs(t *testing.T) {
t.Run("private", func(t *testing.T) {
r := basicACLHelper(privateContainer)
require.False(t, r.Sticky())
for _, op := range allOperations {
require.True(t, r.UserAllowed(op))
require.False(t, r.OthersAllowed(op))
if op == eacl.OperationDelete || op == eacl.OperationRange {
require.False(t, r.SystemAllowed(op))
} else {
require.True(t, r.SystemAllowed(op))
}
}
})
t.Run("public with sticky", func(t *testing.T) {
r := basicACLHelper(publicContainerWithSticky)
require.True(t, r.Sticky())
for _, op := range allOperations {
require.True(t, r.UserAllowed(op))
require.True(t, r.OthersAllowed(op))
require.True(t, r.SystemAllowed(op))
}
})
t.Run("read only", func(t *testing.T) {
r := basicACLHelper(readonlyContainer)
require.False(t, r.Sticky())
for _, op := range allOperations {
require.True(t, r.UserAllowed(op))
require.True(t, r.SystemAllowed(op))
if op == eacl.OperationDelete || op == eacl.OperationPut {
require.False(t, r.OthersAllowed(op))
} else {
require.True(t, r.OthersAllowed(op))
}
}
})
}

View file

@ -7,8 +7,8 @@ import (
core "github.com/nspcc-dev/neofs-node/pkg/core/netmap" core "github.com/nspcc-dev/neofs-node/pkg/core/netmap"
"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"
cid "github.com/nspcc-dev/neofs-sdk-go/container/id" cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
eaclSDK "github.com/nspcc-dev/neofs-sdk-go/eacl"
"github.com/nspcc-dev/neofs-sdk-go/netmap" "github.com/nspcc-dev/neofs-sdk-go/netmap"
"go.uber.org/zap" "go.uber.org/zap"
) )
@ -20,8 +20,7 @@ type senderClassifier struct {
} }
type classifyResult struct { type classifyResult struct {
role eaclSDK.Role role acl.Role
isIR bool
key []byte key []byte
} }
@ -46,8 +45,7 @@ func (c senderClassifier) classify(
// if request owner is the same as container owner, return RoleUser // if request owner is the same as container owner, return RoleUser
if ownerID.Equals(*ownerCnr) { if ownerID.Equals(*ownerCnr) {
return &classifyResult{ return &classifyResult{
role: eaclSDK.RoleUser, role: acl.RoleOwner,
isIR: false,
key: ownerKeyInBytes, key: ownerKeyInBytes,
}, nil }, nil
} }
@ -59,8 +57,7 @@ func (c senderClassifier) classify(
zap.String("error", err.Error())) zap.String("error", err.Error()))
} else if isInnerRingNode { } else if isInnerRingNode {
return &classifyResult{ return &classifyResult{
role: eaclSDK.RoleSystem, role: acl.RoleInnerRing,
isIR: true,
key: ownerKeyInBytes, key: ownerKeyInBytes,
}, nil }, nil
} }
@ -77,15 +74,14 @@ func (c senderClassifier) classify(
zap.String("error", err.Error())) zap.String("error", err.Error()))
} else if isContainerNode { } else if isContainerNode {
return &classifyResult{ return &classifyResult{
role: eaclSDK.RoleSystem, role: acl.RoleContainer,
isIR: false,
key: ownerKeyInBytes, key: ownerKeyInBytes,
}, nil }, nil
} }
// if none of above, return RoleOthers // if none of above, return RoleOthers
return &classifyResult{ return &classifyResult{
role: eaclSDK.RoleOthers, role: acl.RoleOthers,
key: ownerKeyInBytes, key: ownerKeyInBytes,
}, nil }, nil
} }

View file

@ -11,8 +11,6 @@ var (
// ErrMalformedRequest is returned when request contains // ErrMalformedRequest is returned when request contains
// invalid data. // invalid data.
ErrMalformedRequest = errors.New("malformed request") ErrMalformedRequest = errors.New("malformed request")
// ErrUnknownRole is returned when role of the sender is unknown.
ErrUnknownRole = errors.New("can't classify request sender")
// ErrInvalidVerb is returned when session token verb doesn't include necessary operation. // ErrInvalidVerb is returned when session token verb doesn't include necessary operation.
ErrInvalidVerb = errors.New("session token verb is invalid") ErrInvalidVerb = errors.New("session token verb is invalid")
) )

View file

@ -7,8 +7,8 @@ import (
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
sessionV2 "github.com/nspcc-dev/neofs-api-go/v2/session" sessionV2 "github.com/nspcc-dev/neofs-api-go/v2/session"
"github.com/nspcc-dev/neofs-sdk-go/bearer" "github.com/nspcc-dev/neofs-sdk-go/bearer"
"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"
eaclSDK "github.com/nspcc-dev/neofs-sdk-go/eacl"
oid "github.com/nspcc-dev/neofs-sdk-go/object/id" oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
sessionSDK "github.com/nspcc-dev/neofs-sdk-go/session" sessionSDK "github.com/nspcc-dev/neofs-sdk-go/session"
"github.com/nspcc-dev/neofs-sdk-go/user" "github.com/nspcc-dev/neofs-sdk-go/user"
@ -17,11 +17,10 @@ import (
// RequestInfo groups parsed version-independent (from SDK library) // RequestInfo groups parsed version-independent (from SDK library)
// request information and raw API request. // request information and raw API request.
type RequestInfo struct { type RequestInfo struct {
basicACL uint32 basicACL acl.Basic
requestRole eaclSDK.Role requestRole acl.Role
isInnerRing bool operation acl.Op // put, get, head, etc.
operation eaclSDK.Operation // put, get, head, etc. cnrOwner user.ID // container owner
cnrOwner user.ID // container owner
idCnr cid.ID idCnr cid.ID
@ -36,11 +35,11 @@ type RequestInfo struct {
srcRequest interface{} srcRequest interface{}
} }
func (r *RequestInfo) SetBasicACL(basicACL uint32) { func (r *RequestInfo) SetBasicACL(basicACL acl.Basic) {
r.basicACL = basicACL r.basicACL = basicACL
} }
func (r *RequestInfo) SetRequestRole(requestRole eaclSDK.Role) { func (r *RequestInfo) SetRequestRole(requestRole acl.Role) {
r.requestRole = requestRole r.requestRole = requestRole
} }
@ -78,13 +77,8 @@ func (r RequestInfo) Bearer() *bearer.Token {
return r.bearer return r.bearer
} }
// IsInnerRing specifies if request was made by inner ring.
func (r RequestInfo) IsInnerRing() bool {
return r.isInnerRing
}
// BasicACL returns basic ACL of the container. // BasicACL returns basic ACL of the container.
func (r RequestInfo) BasicACL() uint32 { func (r RequestInfo) BasicACL() acl.Basic {
return r.basicACL return r.basicACL
} }
@ -94,12 +88,12 @@ func (r RequestInfo) SenderKey() []byte {
} }
// Operation returns request's operation. // Operation returns request's operation.
func (r RequestInfo) Operation() eaclSDK.Operation { func (r RequestInfo) Operation() acl.Op {
return r.operation return r.operation
} }
// RequestRole returns request sender's role. // RequestRole returns request sender's role.
func (r RequestInfo) RequestRole() eaclSDK.Role { func (r RequestInfo) RequestRole() acl.Role {
return r.requestRole return r.requestRole
} }

View file

@ -9,8 +9,8 @@ import (
"github.com/nspcc-dev/neofs-node/pkg/core/container" "github.com/nspcc-dev/neofs-node/pkg/core/container"
"github.com/nspcc-dev/neofs-node/pkg/core/netmap" "github.com/nspcc-dev/neofs-node/pkg/core/netmap"
"github.com/nspcc-dev/neofs-node/pkg/services/object" "github.com/nspcc-dev/neofs-node/pkg/services/object"
"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"
eaclSDK "github.com/nspcc-dev/neofs-sdk-go/eacl"
sessionSDK "github.com/nspcc-dev/neofs-sdk-go/session" sessionSDK "github.com/nspcc-dev/neofs-sdk-go/session"
"github.com/nspcc-dev/neofs-sdk-go/user" "github.com/nspcc-dev/neofs-sdk-go/user"
"go.uber.org/zap" "go.uber.org/zap"
@ -130,7 +130,7 @@ func (b Service) Get(request *objectV2.GetRequest, stream object.GetObjectStream
src: request, src: request,
} }
reqInfo, err := b.findRequestInfo(req, cnr, eaclSDK.OperationGet) reqInfo, err := b.findRequestInfo(req, cnr, acl.OpObjectGet)
if err != nil { if err != nil {
return err return err
} }
@ -189,7 +189,7 @@ func (b Service) Head(
src: request, src: request,
} }
reqInfo, err := b.findRequestInfo(req, cnr, eaclSDK.OperationHead) reqInfo, err := b.findRequestInfo(req, cnr, acl.OpObjectHead)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -240,7 +240,7 @@ func (b Service) Search(request *objectV2.SearchRequest, stream object.SearchStr
src: request, src: request,
} }
reqInfo, err := b.findRequestInfo(req, id, eaclSDK.OperationSearch) reqInfo, err := b.findRequestInfo(req, id, acl.OpObjectSearch)
if err != nil { if err != nil {
return err return err
} }
@ -288,7 +288,7 @@ func (b Service) Delete(
src: request, src: request,
} }
reqInfo, err := b.findRequestInfo(req, cnr, eaclSDK.OperationDelete) reqInfo, err := b.findRequestInfo(req, cnr, acl.OpObjectDelete)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -332,7 +332,7 @@ func (b Service) GetRange(request *objectV2.GetRangeRequest, stream object.GetOb
src: request, src: request,
} }
reqInfo, err := b.findRequestInfo(req, cnr, eaclSDK.OperationRange) reqInfo, err := b.findRequestInfo(req, cnr, acl.OpObjectRange)
if err != nil { if err != nil {
return err return err
} }
@ -381,7 +381,7 @@ func (b Service) GetRangeHash(
src: request, src: request,
} }
reqInfo, err := b.findRequestInfo(req, cnr, eaclSDK.OperationRangeHash) reqInfo, err := b.findRequestInfo(req, cnr, acl.OpObjectHash)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -450,7 +450,7 @@ func (p putStreamBasicChecker) Send(request *objectV2.PutRequest) error {
src: request, src: request,
} }
reqInfo, err := p.source.findRequestInfo(req, cnr, eaclSDK.OperationPut) reqInfo, err := p.source.findRequestInfo(req, cnr, acl.OpObjectPut)
if err != nil { if err != nil {
return err return err
} }
@ -502,7 +502,7 @@ func (g *searchStreamBasicChecker) Send(resp *objectV2.SearchResponse) error {
return g.SearchStream.Send(resp) return g.SearchStream.Send(resp)
} }
func (b Service) findRequestInfo(req MetaWithToken, idCnr cid.ID, op eaclSDK.Operation) (info RequestInfo, err error) { func (b Service) findRequestInfo(req MetaWithToken, idCnr cid.ID, op acl.Op) (info RequestInfo, err error) {
cnr, err := b.containers.Get(idCnr) // fetch actual container cnr, err := b.containers.Get(idCnr) // fetch actual container
if err != nil { if err != nil {
return info, err return info, err
@ -531,13 +531,8 @@ func (b Service) findRequestInfo(req MetaWithToken, idCnr cid.ID, op eaclSDK.Ope
return info, err return info, err
} }
if res.role == eaclSDK.RoleUnknown {
return info, ErrUnknownRole
}
info.basicACL = cnr.Value.BasicACL() info.basicACL = cnr.Value.BasicACL()
info.requestRole = res.role info.requestRole = res.role
info.isInnerRing = res.isIR
info.operation = op info.operation = op
info.cnrOwner = *cnr.Value.OwnerID() info.cnrOwner = *cnr.Value.OwnerID()
info.idCnr = idCnr info.idCnr = idCnr

View file

@ -11,8 +11,8 @@ import (
refsV2 "github.com/nspcc-dev/neofs-api-go/v2/refs" refsV2 "github.com/nspcc-dev/neofs-api-go/v2/refs"
sessionV2 "github.com/nspcc-dev/neofs-api-go/v2/session" sessionV2 "github.com/nspcc-dev/neofs-api-go/v2/session"
"github.com/nspcc-dev/neofs-sdk-go/bearer" "github.com/nspcc-dev/neofs-sdk-go/bearer"
"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"
eaclSDK "github.com/nspcc-dev/neofs-sdk-go/eacl"
oid "github.com/nspcc-dev/neofs-sdk-go/object/id" oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
sessionSDK "github.com/nspcc-dev/neofs-sdk-go/session" sessionSDK "github.com/nspcc-dev/neofs-sdk-go/session"
"github.com/nspcc-dev/neofs-sdk-go/user" "github.com/nspcc-dev/neofs-sdk-go/user"
@ -205,27 +205,27 @@ func isOwnerFromKey(id user.ID, key *keys.PublicKey) bool {
} }
// assertVerb checks that token verb corresponds to op. // assertVerb checks that token verb corresponds to op.
func assertVerb(tok sessionSDK.Object, op eaclSDK.Operation) bool { func assertVerb(tok sessionSDK.Object, op acl.Op) bool {
//nolint:exhaustive //nolint:exhaustive
switch op { switch op {
case eaclSDK.OperationPut: case acl.OpObjectPut:
return tok.AssertVerb(sessionSDK.VerbObjectPut, sessionSDK.VerbObjectDelete) return tok.AssertVerb(sessionSDK.VerbObjectPut, sessionSDK.VerbObjectDelete)
case eaclSDK.OperationDelete: case acl.OpObjectDelete:
return tok.AssertVerb(sessionSDK.VerbObjectDelete) return tok.AssertVerb(sessionSDK.VerbObjectDelete)
case eaclSDK.OperationGet: case acl.OpObjectGet:
return tok.AssertVerb(sessionSDK.VerbObjectGet) return tok.AssertVerb(sessionSDK.VerbObjectGet)
case eaclSDK.OperationHead: case acl.OpObjectHead:
return tok.AssertVerb( return tok.AssertVerb(
sessionSDK.VerbObjectHead, sessionSDK.VerbObjectHead,
sessionSDK.VerbObjectGet, sessionSDK.VerbObjectGet,
sessionSDK.VerbObjectDelete, sessionSDK.VerbObjectDelete,
sessionSDK.VerbObjectRange, sessionSDK.VerbObjectRange,
sessionSDK.VerbObjectRangeHash) sessionSDK.VerbObjectRangeHash)
case eaclSDK.OperationSearch: case acl.OpObjectSearch:
return tok.AssertVerb(sessionSDK.VerbObjectSearch, sessionSDK.VerbObjectDelete) return tok.AssertVerb(sessionSDK.VerbObjectSearch, sessionSDK.VerbObjectDelete)
case eaclSDK.OperationRange: case acl.OpObjectRange:
return tok.AssertVerb(sessionSDK.VerbObjectRange, sessionSDK.VerbObjectRangeHash) return tok.AssertVerb(sessionSDK.VerbObjectRange, sessionSDK.VerbObjectRangeHash)
case eaclSDK.OperationRangeHash: case acl.OpObjectHash:
return tok.AssertVerb(sessionSDK.VerbObjectRangeHash) return tok.AssertVerb(sessionSDK.VerbObjectRangeHash)
} }

View file

@ -9,7 +9,7 @@ import (
"github.com/nspcc-dev/neofs-api-go/v2/acl" "github.com/nspcc-dev/neofs-api-go/v2/acl"
"github.com/nspcc-dev/neofs-api-go/v2/session" "github.com/nspcc-dev/neofs-api-go/v2/session"
bearertest "github.com/nspcc-dev/neofs-sdk-go/bearer/test" bearertest "github.com/nspcc-dev/neofs-sdk-go/bearer/test"
"github.com/nspcc-dev/neofs-sdk-go/eacl" aclsdk "github.com/nspcc-dev/neofs-sdk-go/container/acl"
sessionSDK "github.com/nspcc-dev/neofs-sdk-go/session" sessionSDK "github.com/nspcc-dev/neofs-sdk-go/session"
sessiontest "github.com/nspcc-dev/neofs-sdk-go/session/test" sessiontest "github.com/nspcc-dev/neofs-sdk-go/session/test"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@ -59,20 +59,20 @@ func testGenerateMetaHeader(depth uint32, b *acl.BearerToken, s *session.Token)
func TestIsVerbCompatible(t *testing.T) { func TestIsVerbCompatible(t *testing.T) {
// Source: https://nspcc.ru/upload/neofs-spec-latest.pdf#page=28 // Source: https://nspcc.ru/upload/neofs-spec-latest.pdf#page=28
table := map[eacl.Operation][]sessionSDK.ObjectVerb{ table := map[aclsdk.Op][]sessionSDK.ObjectVerb{
eacl.OperationPut: {sessionSDK.VerbObjectPut, sessionSDK.VerbObjectDelete}, aclsdk.OpObjectPut: {sessionSDK.VerbObjectPut, sessionSDK.VerbObjectDelete},
eacl.OperationDelete: {sessionSDK.VerbObjectDelete}, aclsdk.OpObjectDelete: {sessionSDK.VerbObjectDelete},
eacl.OperationGet: {sessionSDK.VerbObjectGet}, aclsdk.OpObjectGet: {sessionSDK.VerbObjectGet},
eacl.OperationHead: { aclsdk.OpObjectHead: {
sessionSDK.VerbObjectHead, sessionSDK.VerbObjectHead,
sessionSDK.VerbObjectGet, sessionSDK.VerbObjectGet,
sessionSDK.VerbObjectDelete, sessionSDK.VerbObjectDelete,
sessionSDK.VerbObjectRange, sessionSDK.VerbObjectRange,
sessionSDK.VerbObjectRangeHash, sessionSDK.VerbObjectRangeHash,
}, },
eacl.OperationRange: {sessionSDK.VerbObjectRange, sessionSDK.VerbObjectRangeHash}, aclsdk.OpObjectRange: {sessionSDK.VerbObjectRange, sessionSDK.VerbObjectRangeHash},
eacl.OperationRangeHash: {sessionSDK.VerbObjectRangeHash}, aclsdk.OpObjectHash: {sessionSDK.VerbObjectRangeHash},
eacl.OperationSearch: {sessionSDK.VerbObjectSearch, sessionSDK.VerbObjectDelete}, aclsdk.OpObjectSearch: {sessionSDK.VerbObjectSearch, sessionSDK.VerbObjectDelete},
} }
verbs := []sessionSDK.ObjectVerb{ verbs := []sessionSDK.ObjectVerb{