forked from TrueCloudLab/frostfs-node
[#1533] acl: Upgrade NeoFS SDK Go with refactored basic ACL
Signed-off-by: Leonard Lyubich <leonard@nspcc.ru>
This commit is contained in:
parent
b13dca8052
commit
305dd7598f
15 changed files with 115 additions and 429 deletions
|
@ -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/commonflags"
|
||||
"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/acl"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/netmap"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/session"
|
||||
subnetid "github.com/nspcc-dev/neofs-sdk-go/subnet/id"
|
||||
|
@ -23,31 +23,6 @@ import (
|
|||
"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 (
|
||||
containerACL string
|
||||
containerNonce string
|
||||
|
@ -80,8 +55,8 @@ It will be stored in sidechain when inner ring will accepts it.`,
|
|||
attributes, err := parseAttributes(containerAttributes)
|
||||
common.ExitOnErr(cmd, "", err)
|
||||
|
||||
basicACL, err := parseBasicACL(containerACL)
|
||||
common.ExitOnErr(cmd, "", err)
|
||||
var basicACL acl.Basic
|
||||
common.ExitOnErr(cmd, "decode basic ACL string: %w", basicACL.DecodeString(containerACL))
|
||||
|
||||
nonce, err := parseNonce(containerNonce)
|
||||
common.ExitOnErr(cmd, "", err)
|
||||
|
@ -157,7 +132,9 @@ func initContainerCreateCmd() {
|
|||
|
||||
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.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")
|
||||
|
@ -226,21 +203,6 @@ func parseAttributes(attributes []string) ([]container.Attribute, error) {
|
|||
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) {
|
||||
if nonce == "" {
|
||||
result := uuid.New()
|
||||
|
|
|
@ -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/commonflags"
|
||||
"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/acl"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
|
@ -120,7 +120,7 @@ func prettyPrintContainer(cmd *cobra.Command, cnr *container.Container, jsonEnco
|
|||
cmd.Println("owner ID:", cnr.OwnerID())
|
||||
|
||||
basicACL := cnr.BasicACL()
|
||||
prettyPrintBasicACL(cmd, acl.BasicACL(basicACL))
|
||||
prettyPrintBasicACL(cmd, basicACL)
|
||||
|
||||
for _, attribute := range cnr.Attributes() {
|
||||
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) {
|
||||
cmd.Printf("basic ACL: %s", basicACL)
|
||||
for k, v := range wellKnownBasicACL {
|
||||
if v == basicACL {
|
||||
cmd.Printf(" (%s)\n", k)
|
||||
return
|
||||
}
|
||||
func prettyPrintBasicACL(cmd *cobra.Command, basicACL acl.Basic) {
|
||||
cmd.Printf("basic ACL: %s", basicACL.EncodeToString())
|
||||
|
||||
var prettyName string
|
||||
|
||||
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()
|
||||
}
|
||||
|
|
2
go.mod
2
go.mod
|
@ -19,7 +19,7 @@ require (
|
|||
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-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/panjf2000/ants/v2 v2.4.0
|
||||
github.com/paulmach/orb v0.2.2
|
||||
|
|
4
go.sum
4
go.sum
|
@ -409,8 +409,8 @@ github.com/nspcc-dev/neofs-crypto v0.3.0 h1:zlr3pgoxuzrmGCxc5W8dGVfA9Rro8diFvVnB
|
|||
github.com/nspcc-dev/neofs-crypto v0.3.0/go.mod h1:8w16GEJbH6791ktVqHN9YRNH3s9BEEKYxGhlFnp0cDw=
|
||||
github.com/nspcc-dev/neofs-sdk-go v0.0.0-20211201182451-a5b61c4f6477/go.mod h1:dfMtQWmBHYpl9Dez23TGtIUKiFvCIxUZq/CkSIhEpz4=
|
||||
github.com/nspcc-dev/neofs-sdk-go v0.0.0-20220113123743-7f3162110659/go.mod h1:/jay1lr3w7NQd/VDBkEhkJmDmyPNsu4W+QV2obsUV40=
|
||||
github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.4.0.20220622125136-af7e20073bc6 h1:SrD9Qo8Knh5B08WC3XD5GKqt2tiG4ToCm+gc5BPQKxY=
|
||||
github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.4.0.20220622125136-af7e20073bc6/go.mod h1:EpzpilARa1/7Pgtn8qB/iXXyvC1AIzhlm8mbU+S52MU=
|
||||
github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.4.0.20220623080532-aa5ee1dcde1c h1:1kYefIk0WdUZJPsZ5HXN7hdNgsb3gEP+/AVMS6lxUzI=
|
||||
github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.4.0.20220623080532-aa5ee1dcde1c/go.mod h1:EpzpilARa1/7Pgtn8qB/iXXyvC1AIzhlm8mbU+S52MU=
|
||||
github.com/nspcc-dev/rfc6979 v0.1.0/go.mod h1:exhIh1PdpDC5vQmyEsGvc4YDM/lyQp/452QxGq/UEso=
|
||||
github.com/nspcc-dev/rfc6979 v0.2.0 h1:3e1WNxrN60/6N0DW7+UYisLeZJyfqZTNOjeV/toYvOE=
|
||||
github.com/nspcc-dev/rfc6979 v0.2.0/go.mod h1:exhIh1PdpDC5vQmyEsGvc4YDM/lyQp/452QxGq/UEso=
|
||||
|
|
|
@ -52,12 +52,7 @@ func (cp *Processor) checkSetEACL(e container.SetEACL) error {
|
|||
}
|
||||
|
||||
// ACL extensions can be disabled by basic ACL, check it
|
||||
basicACL := cnr.Value.BasicACL()
|
||||
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 {
|
||||
if !cnr.Value.BasicACL().Extendable() {
|
||||
return errors.New("ACL extension disabled by container basic ACL")
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
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"
|
||||
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"
|
||||
eaclSDK "github.com/nspcc-dev/neofs-sdk-go/eacl"
|
||||
"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.
|
||||
func (c *Checker) CheckBasicACL(info v2.RequestInfo) bool {
|
||||
// check basic ACL permissions
|
||||
var checkFn func(eaclSDK.Operation) bool
|
||||
|
||||
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())
|
||||
return info.BasicACL().IsOpAllowed(info.Operation(), info.RequestRole())
|
||||
}
|
||||
|
||||
// StickyBitCheck validates owner field in the request if sticky bit is enabled.
|
||||
func (c *Checker) StickyBitCheck(info v2.RequestInfo, owner user.ID) bool {
|
||||
// According to NeoFS specification sticky bit has no effect on system nodes
|
||||
// for correct intra-container work with objects (in particular, replication).
|
||||
if info.RequestRole() == eaclSDK.RoleSystem {
|
||||
if info.RequestRole() == acl.RoleContainer {
|
||||
return true
|
||||
}
|
||||
|
||||
if !basicACLHelper(info.BasicACL()).Sticky() {
|
||||
if !info.BasicACL().Sticky() {
|
||||
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.
|
||||
func (c *Checker) CheckEACL(msg interface{}, reqInfo v2.RequestInfo) error {
|
||||
if basicACLHelper(reqInfo.BasicACL()).Final() {
|
||||
basicACL := reqInfo.BasicACL()
|
||||
if !basicACL.Extendable() {
|
||||
return nil
|
||||
}
|
||||
|
||||
// if bearer token is not allowed, then ignore it
|
||||
if !basicACLHelper(reqInfo.BasicACL()).BearerAllowed(reqInfo.Operation()) {
|
||||
if !basicACL.AllowedBearerRules(reqInfo.Operation()) {
|
||||
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)
|
||||
}
|
||||
|
||||
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).
|
||||
WithRole(reqInfo.RequestRole()).
|
||||
WithOperation(reqInfo.Operation()).
|
||||
WithRole(eaclRole).
|
||||
WithOperation(eaclSDK.Operation(reqInfo.Operation())).
|
||||
WithContainerID(&cnr).
|
||||
WithSenderKey(reqInfo.SenderKey()).
|
||||
WithHeaderSource(hdrSrc).
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"github.com/nspcc-dev/neofs-node/pkg/core/container"
|
||||
"github.com/nspcc-dev/neofs-node/pkg/local_object_storage/engine"
|
||||
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"
|
||||
eaclSDK "github.com/nspcc-dev/neofs-sdk-go/eacl"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/user"
|
||||
|
@ -37,13 +38,14 @@ func TestStickyCheck(t *testing.T) {
|
|||
var info v2.RequestInfo
|
||||
|
||||
info.SetSenderKey(make([]byte, 33)) // any non-empty key
|
||||
info.SetRequestRole(eaclSDK.RoleSystem)
|
||||
|
||||
setSticky(&info, true)
|
||||
info.SetRequestRole(acl.RoleContainer)
|
||||
|
||||
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()))
|
||||
})
|
||||
|
@ -51,13 +53,15 @@ func TestStickyCheck(t *testing.T) {
|
|||
t.Run("owner ID and/or public key emptiness", func(t *testing.T) {
|
||||
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) {
|
||||
info := info
|
||||
if isSticky {
|
||||
setSticky(&info, true)
|
||||
} else {
|
||||
setSticky(&info, false)
|
||||
var basicACL acl.Basic
|
||||
basicACL.MakeSticky()
|
||||
|
||||
info.SetBasicACL(basicACL)
|
||||
}
|
||||
|
||||
if withKey {
|
||||
|
@ -84,15 +88,3 @@ func TestStickyCheck(t *testing.T) {
|
|||
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))
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
|
@ -7,8 +7,8 @@ import (
|
|||
|
||||
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/acl"
|
||||
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"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
@ -20,8 +20,7 @@ type senderClassifier struct {
|
|||
}
|
||||
|
||||
type classifyResult struct {
|
||||
role eaclSDK.Role
|
||||
isIR bool
|
||||
role acl.Role
|
||||
key []byte
|
||||
}
|
||||
|
||||
|
@ -46,8 +45,7 @@ func (c senderClassifier) classify(
|
|||
// if request owner is the same as container owner, return RoleUser
|
||||
if ownerID.Equals(*ownerCnr) {
|
||||
return &classifyResult{
|
||||
role: eaclSDK.RoleUser,
|
||||
isIR: false,
|
||||
role: acl.RoleOwner,
|
||||
key: ownerKeyInBytes,
|
||||
}, nil
|
||||
}
|
||||
|
@ -59,8 +57,7 @@ func (c senderClassifier) classify(
|
|||
zap.String("error", err.Error()))
|
||||
} else if isInnerRingNode {
|
||||
return &classifyResult{
|
||||
role: eaclSDK.RoleSystem,
|
||||
isIR: true,
|
||||
role: acl.RoleInnerRing,
|
||||
key: ownerKeyInBytes,
|
||||
}, nil
|
||||
}
|
||||
|
@ -77,15 +74,14 @@ func (c senderClassifier) classify(
|
|||
zap.String("error", err.Error()))
|
||||
} else if isContainerNode {
|
||||
return &classifyResult{
|
||||
role: eaclSDK.RoleSystem,
|
||||
isIR: false,
|
||||
role: acl.RoleContainer,
|
||||
key: ownerKeyInBytes,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// if none of above, return RoleOthers
|
||||
return &classifyResult{
|
||||
role: eaclSDK.RoleOthers,
|
||||
role: acl.RoleOthers,
|
||||
key: ownerKeyInBytes,
|
||||
}, nil
|
||||
}
|
||||
|
|
|
@ -11,8 +11,6 @@ var (
|
|||
// ErrMalformedRequest is returned when request contains
|
||||
// invalid data.
|
||||
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 = errors.New("session token verb is invalid")
|
||||
)
|
||||
|
|
|
@ -7,8 +7,8 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
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/container/acl"
|
||||
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"
|
||||
sessionSDK "github.com/nspcc-dev/neofs-sdk-go/session"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/user"
|
||||
|
@ -17,11 +17,10 @@ import (
|
|||
// RequestInfo groups parsed version-independent (from SDK library)
|
||||
// request information and raw API request.
|
||||
type RequestInfo struct {
|
||||
basicACL uint32
|
||||
requestRole eaclSDK.Role
|
||||
isInnerRing bool
|
||||
operation eaclSDK.Operation // put, get, head, etc.
|
||||
cnrOwner user.ID // container owner
|
||||
basicACL acl.Basic
|
||||
requestRole acl.Role
|
||||
operation acl.Op // put, get, head, etc.
|
||||
cnrOwner user.ID // container owner
|
||||
|
||||
idCnr cid.ID
|
||||
|
||||
|
@ -36,11 +35,11 @@ type RequestInfo struct {
|
|||
srcRequest interface{}
|
||||
}
|
||||
|
||||
func (r *RequestInfo) SetBasicACL(basicACL uint32) {
|
||||
func (r *RequestInfo) SetBasicACL(basicACL acl.Basic) {
|
||||
r.basicACL = basicACL
|
||||
}
|
||||
|
||||
func (r *RequestInfo) SetRequestRole(requestRole eaclSDK.Role) {
|
||||
func (r *RequestInfo) SetRequestRole(requestRole acl.Role) {
|
||||
r.requestRole = requestRole
|
||||
}
|
||||
|
||||
|
@ -78,13 +77,8 @@ func (r RequestInfo) Bearer() *bearer.Token {
|
|||
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.
|
||||
func (r RequestInfo) BasicACL() uint32 {
|
||||
func (r RequestInfo) BasicACL() acl.Basic {
|
||||
return r.basicACL
|
||||
}
|
||||
|
||||
|
@ -94,12 +88,12 @@ func (r RequestInfo) SenderKey() []byte {
|
|||
}
|
||||
|
||||
// Operation returns request's operation.
|
||||
func (r RequestInfo) Operation() eaclSDK.Operation {
|
||||
func (r RequestInfo) Operation() acl.Op {
|
||||
return r.operation
|
||||
}
|
||||
|
||||
// RequestRole returns request sender's role.
|
||||
func (r RequestInfo) RequestRole() eaclSDK.Role {
|
||||
func (r RequestInfo) RequestRole() acl.Role {
|
||||
return r.requestRole
|
||||
}
|
||||
|
||||
|
|
|
@ -9,8 +9,8 @@ import (
|
|||
"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/services/object"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/container/acl"
|
||||
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"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/user"
|
||||
"go.uber.org/zap"
|
||||
|
@ -130,7 +130,7 @@ func (b Service) Get(request *objectV2.GetRequest, stream object.GetObjectStream
|
|||
src: request,
|
||||
}
|
||||
|
||||
reqInfo, err := b.findRequestInfo(req, cnr, eaclSDK.OperationGet)
|
||||
reqInfo, err := b.findRequestInfo(req, cnr, acl.OpObjectGet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -189,7 +189,7 @@ func (b Service) Head(
|
|||
src: request,
|
||||
}
|
||||
|
||||
reqInfo, err := b.findRequestInfo(req, cnr, eaclSDK.OperationHead)
|
||||
reqInfo, err := b.findRequestInfo(req, cnr, acl.OpObjectHead)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -240,7 +240,7 @@ func (b Service) Search(request *objectV2.SearchRequest, stream object.SearchStr
|
|||
src: request,
|
||||
}
|
||||
|
||||
reqInfo, err := b.findRequestInfo(req, id, eaclSDK.OperationSearch)
|
||||
reqInfo, err := b.findRequestInfo(req, id, acl.OpObjectSearch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -288,7 +288,7 @@ func (b Service) Delete(
|
|||
src: request,
|
||||
}
|
||||
|
||||
reqInfo, err := b.findRequestInfo(req, cnr, eaclSDK.OperationDelete)
|
||||
reqInfo, err := b.findRequestInfo(req, cnr, acl.OpObjectDelete)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -332,7 +332,7 @@ func (b Service) GetRange(request *objectV2.GetRangeRequest, stream object.GetOb
|
|||
src: request,
|
||||
}
|
||||
|
||||
reqInfo, err := b.findRequestInfo(req, cnr, eaclSDK.OperationRange)
|
||||
reqInfo, err := b.findRequestInfo(req, cnr, acl.OpObjectRange)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -381,7 +381,7 @@ func (b Service) GetRangeHash(
|
|||
src: request,
|
||||
}
|
||||
|
||||
reqInfo, err := b.findRequestInfo(req, cnr, eaclSDK.OperationRangeHash)
|
||||
reqInfo, err := b.findRequestInfo(req, cnr, acl.OpObjectHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -450,7 +450,7 @@ func (p putStreamBasicChecker) Send(request *objectV2.PutRequest) error {
|
|||
src: request,
|
||||
}
|
||||
|
||||
reqInfo, err := p.source.findRequestInfo(req, cnr, eaclSDK.OperationPut)
|
||||
reqInfo, err := p.source.findRequestInfo(req, cnr, acl.OpObjectPut)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -502,7 +502,7 @@ func (g *searchStreamBasicChecker) Send(resp *objectV2.SearchResponse) error {
|
|||
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
|
||||
if err != nil {
|
||||
return info, err
|
||||
|
@ -531,13 +531,8 @@ func (b Service) findRequestInfo(req MetaWithToken, idCnr cid.ID, op eaclSDK.Ope
|
|||
return info, err
|
||||
}
|
||||
|
||||
if res.role == eaclSDK.RoleUnknown {
|
||||
return info, ErrUnknownRole
|
||||
}
|
||||
|
||||
info.basicACL = cnr.Value.BasicACL()
|
||||
info.requestRole = res.role
|
||||
info.isInnerRing = res.isIR
|
||||
info.operation = op
|
||||
info.cnrOwner = *cnr.Value.OwnerID()
|
||||
info.idCnr = idCnr
|
||||
|
|
|
@ -11,8 +11,8 @@ import (
|
|||
refsV2 "github.com/nspcc-dev/neofs-api-go/v2/refs"
|
||||
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/container/acl"
|
||||
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"
|
||||
sessionSDK "github.com/nspcc-dev/neofs-sdk-go/session"
|
||||
"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.
|
||||
func assertVerb(tok sessionSDK.Object, op eaclSDK.Operation) bool {
|
||||
func assertVerb(tok sessionSDK.Object, op acl.Op) bool {
|
||||
//nolint:exhaustive
|
||||
switch op {
|
||||
case eaclSDK.OperationPut:
|
||||
case acl.OpObjectPut:
|
||||
return tok.AssertVerb(sessionSDK.VerbObjectPut, sessionSDK.VerbObjectDelete)
|
||||
case eaclSDK.OperationDelete:
|
||||
case acl.OpObjectDelete:
|
||||
return tok.AssertVerb(sessionSDK.VerbObjectDelete)
|
||||
case eaclSDK.OperationGet:
|
||||
case acl.OpObjectGet:
|
||||
return tok.AssertVerb(sessionSDK.VerbObjectGet)
|
||||
case eaclSDK.OperationHead:
|
||||
case acl.OpObjectHead:
|
||||
return tok.AssertVerb(
|
||||
sessionSDK.VerbObjectHead,
|
||||
sessionSDK.VerbObjectGet,
|
||||
sessionSDK.VerbObjectDelete,
|
||||
sessionSDK.VerbObjectRange,
|
||||
sessionSDK.VerbObjectRangeHash)
|
||||
case eaclSDK.OperationSearch:
|
||||
case acl.OpObjectSearch:
|
||||
return tok.AssertVerb(sessionSDK.VerbObjectSearch, sessionSDK.VerbObjectDelete)
|
||||
case eaclSDK.OperationRange:
|
||||
case acl.OpObjectRange:
|
||||
return tok.AssertVerb(sessionSDK.VerbObjectRange, sessionSDK.VerbObjectRangeHash)
|
||||
case eaclSDK.OperationRangeHash:
|
||||
case acl.OpObjectHash:
|
||||
return tok.AssertVerb(sessionSDK.VerbObjectRangeHash)
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
"github.com/nspcc-dev/neofs-api-go/v2/acl"
|
||||
"github.com/nspcc-dev/neofs-api-go/v2/session"
|
||||
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"
|
||||
sessiontest "github.com/nspcc-dev/neofs-sdk-go/session/test"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
@ -59,20 +59,20 @@ func testGenerateMetaHeader(depth uint32, b *acl.BearerToken, s *session.Token)
|
|||
|
||||
func TestIsVerbCompatible(t *testing.T) {
|
||||
// Source: https://nspcc.ru/upload/neofs-spec-latest.pdf#page=28
|
||||
table := map[eacl.Operation][]sessionSDK.ObjectVerb{
|
||||
eacl.OperationPut: {sessionSDK.VerbObjectPut, sessionSDK.VerbObjectDelete},
|
||||
eacl.OperationDelete: {sessionSDK.VerbObjectDelete},
|
||||
eacl.OperationGet: {sessionSDK.VerbObjectGet},
|
||||
eacl.OperationHead: {
|
||||
table := map[aclsdk.Op][]sessionSDK.ObjectVerb{
|
||||
aclsdk.OpObjectPut: {sessionSDK.VerbObjectPut, sessionSDK.VerbObjectDelete},
|
||||
aclsdk.OpObjectDelete: {sessionSDK.VerbObjectDelete},
|
||||
aclsdk.OpObjectGet: {sessionSDK.VerbObjectGet},
|
||||
aclsdk.OpObjectHead: {
|
||||
sessionSDK.VerbObjectHead,
|
||||
sessionSDK.VerbObjectGet,
|
||||
sessionSDK.VerbObjectDelete,
|
||||
sessionSDK.VerbObjectRange,
|
||||
sessionSDK.VerbObjectRangeHash,
|
||||
},
|
||||
eacl.OperationRange: {sessionSDK.VerbObjectRange, sessionSDK.VerbObjectRangeHash},
|
||||
eacl.OperationRangeHash: {sessionSDK.VerbObjectRangeHash},
|
||||
eacl.OperationSearch: {sessionSDK.VerbObjectSearch, sessionSDK.VerbObjectDelete},
|
||||
aclsdk.OpObjectRange: {sessionSDK.VerbObjectRange, sessionSDK.VerbObjectRangeHash},
|
||||
aclsdk.OpObjectHash: {sessionSDK.VerbObjectRangeHash},
|
||||
aclsdk.OpObjectSearch: {sessionSDK.VerbObjectSearch, sessionSDK.VerbObjectDelete},
|
||||
}
|
||||
|
||||
verbs := []sessionSDK.ObjectVerb{
|
||||
|
|
Loading…
Reference in a new issue