forked from TrueCloudLab/frostfs-node
[#1096] eacl: Use validator from SDK
Signed-off-by: Pavel Karpy <carpawell@nspcc.ru>
This commit is contained in:
parent
759421ebbf
commit
ed156cd738
12 changed files with 61 additions and 413 deletions
|
@ -20,7 +20,6 @@ import (
|
||||||
objectTransportGRPC "github.com/nspcc-dev/neofs-node/pkg/network/transport/object/grpc"
|
objectTransportGRPC "github.com/nspcc-dev/neofs-node/pkg/network/transport/object/grpc"
|
||||||
objectService "github.com/nspcc-dev/neofs-node/pkg/services/object"
|
objectService "github.com/nspcc-dev/neofs-node/pkg/services/object"
|
||||||
"github.com/nspcc-dev/neofs-node/pkg/services/object/acl"
|
"github.com/nspcc-dev/neofs-node/pkg/services/object/acl"
|
||||||
"github.com/nspcc-dev/neofs-node/pkg/services/object/acl/eacl"
|
|
||||||
deletesvc "github.com/nspcc-dev/neofs-node/pkg/services/object/delete"
|
deletesvc "github.com/nspcc-dev/neofs-node/pkg/services/object/delete"
|
||||||
deletesvcV2 "github.com/nspcc-dev/neofs-node/pkg/services/object/delete/v2"
|
deletesvcV2 "github.com/nspcc-dev/neofs-node/pkg/services/object/delete/v2"
|
||||||
getsvc "github.com/nspcc-dev/neofs-node/pkg/services/object/get"
|
getsvc "github.com/nspcc-dev/neofs-node/pkg/services/object/get"
|
||||||
|
@ -365,10 +364,7 @@ func initObjectService(c *cfg) {
|
||||||
),
|
),
|
||||||
acl.WithNextService(splitSvc),
|
acl.WithNextService(splitSvc),
|
||||||
acl.WithLocalStorage(ls),
|
acl.WithLocalStorage(ls),
|
||||||
acl.WithEACLValidatorOptions(
|
acl.WithEACLSource(c.cfgObject.eaclSource),
|
||||||
eacl.WithEACLSource(c.cfgObject.eaclSource),
|
|
||||||
eacl.WithLogger(c.log),
|
|
||||||
),
|
|
||||||
acl.WithNetmapState(c.cfgNetmap.state),
|
acl.WithNetmapState(c.cfgNetmap.state),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
4
go.mod
4
go.mod
|
@ -12,8 +12,8 @@ require (
|
||||||
github.com/multiformats/go-multiaddr v0.4.0
|
github.com/multiformats/go-multiaddr v0.4.0
|
||||||
github.com/nspcc-dev/hrw v1.0.9
|
github.com/nspcc-dev/hrw v1.0.9
|
||||||
github.com/nspcc-dev/neo-go v0.98.0
|
github.com/nspcc-dev/neo-go v0.98.0
|
||||||
github.com/nspcc-dev/neofs-api-go/v2 v2.11.1
|
github.com/nspcc-dev/neofs-api-go/v2 v2.11.2-0.20220114101721-227a871a04ac
|
||||||
github.com/nspcc-dev/neofs-sdk-go v0.0.0-20220113123743-7f3162110659
|
github.com/nspcc-dev/neofs-sdk-go v0.0.0-20220119080627-f83ff628fb19
|
||||||
github.com/nspcc-dev/tzhash v1.4.0
|
github.com/nspcc-dev/tzhash v1.4.0
|
||||||
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
|
||||||
|
|
8
go.sum
8
go.sum
|
@ -364,15 +364,15 @@ github.com/nspcc-dev/neo-go v0.73.1-pre.0.20200303142215-f5a1b928ce09/go.mod h1:
|
||||||
github.com/nspcc-dev/neo-go v0.98.0 h1:yyW4sgY88/pLf0949qmgfkQXzRKC3CI/WyhqXNnwMd8=
|
github.com/nspcc-dev/neo-go v0.98.0 h1:yyW4sgY88/pLf0949qmgfkQXzRKC3CI/WyhqXNnwMd8=
|
||||||
github.com/nspcc-dev/neo-go v0.98.0/go.mod h1:E3cc1x6RXSXrJb2nDWXTXjnXk3rIqVN8YdFyWv+FrqM=
|
github.com/nspcc-dev/neo-go v0.98.0/go.mod h1:E3cc1x6RXSXrJb2nDWXTXjnXk3rIqVN8YdFyWv+FrqM=
|
||||||
github.com/nspcc-dev/neofs-api-go/v2 v2.11.0-pre.0.20211201134523-3604d96f3fe1/go.mod h1:oS8dycEh8PPf2Jjp6+8dlwWyEv2Dy77h/XhhcdxYEFs=
|
github.com/nspcc-dev/neofs-api-go/v2 v2.11.0-pre.0.20211201134523-3604d96f3fe1/go.mod h1:oS8dycEh8PPf2Jjp6+8dlwWyEv2Dy77h/XhhcdxYEFs=
|
||||||
github.com/nspcc-dev/neofs-api-go/v2 v2.11.1 h1:SVqc523pZsSaS9vnPS1mm3VV6b6xY0gvdA0uYJ/GWZQ=
|
github.com/nspcc-dev/neofs-api-go/v2 v2.11.2-0.20220114101721-227a871a04ac h1:65C4z7pybLT2HjtY96abZj6kbgVp34AbrApn5DD+ZxY=
|
||||||
github.com/nspcc-dev/neofs-api-go/v2 v2.11.1/go.mod h1:oS8dycEh8PPf2Jjp6+8dlwWyEv2Dy77h/XhhcdxYEFs=
|
github.com/nspcc-dev/neofs-api-go/v2 v2.11.2-0.20220114101721-227a871a04ac/go.mod h1:oS8dycEh8PPf2Jjp6+8dlwWyEv2Dy77h/XhhcdxYEFs=
|
||||||
github.com/nspcc-dev/neofs-crypto v0.2.0/go.mod h1:F/96fUzPM3wR+UGsPi3faVNmFlA9KAEAUQR7dMxZmNA=
|
github.com/nspcc-dev/neofs-crypto v0.2.0/go.mod h1:F/96fUzPM3wR+UGsPi3faVNmFlA9KAEAUQR7dMxZmNA=
|
||||||
github.com/nspcc-dev/neofs-crypto v0.2.3/go.mod h1:8w16GEJbH6791ktVqHN9YRNH3s9BEEKYxGhlFnp0cDw=
|
github.com/nspcc-dev/neofs-crypto v0.2.3/go.mod h1:8w16GEJbH6791ktVqHN9YRNH3s9BEEKYxGhlFnp0cDw=
|
||||||
github.com/nspcc-dev/neofs-crypto v0.3.0 h1:zlr3pgoxuzrmGCxc5W8dGVfA9Rro8diFvVnBg0L4ifM=
|
github.com/nspcc-dev/neofs-crypto v0.3.0 h1:zlr3pgoxuzrmGCxc5W8dGVfA9Rro8diFvVnBg0L4ifM=
|
||||||
github.com/nspcc-dev/neofs-crypto v0.3.0/go.mod h1:8w16GEJbH6791ktVqHN9YRNH3s9BEEKYxGhlFnp0cDw=
|
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-20211201182451-a5b61c4f6477/go.mod h1:dfMtQWmBHYpl9Dez23TGtIUKiFvCIxUZq/CkSIhEpz4=
|
||||||
github.com/nspcc-dev/neofs-sdk-go v0.0.0-20220113123743-7f3162110659 h1:rpMCoRa7expLc9gMiOP724gz6YSykZzmMALR/CmiwnU=
|
github.com/nspcc-dev/neofs-sdk-go v0.0.0-20220119080627-f83ff628fb19 h1:VuOztHAvvu0ZK7ng6EillhlEunHy34TQr5/13k0fSUU=
|
||||||
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 v0.0.0-20220119080627-f83ff628fb19/go.mod h1:fhs4v6uts7bEgwYP05NXbAQlQ0YhK4WVjJRKQKFKBxY=
|
||||||
github.com/nspcc-dev/rfc6979 v0.1.0/go.mod h1:exhIh1PdpDC5vQmyEsGvc4YDM/lyQp/452QxGq/UEso=
|
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 h1:3e1WNxrN60/6N0DW7+UYisLeZJyfqZTNOjeV/toYvOE=
|
||||||
github.com/nspcc-dev/rfc6979 v0.2.0/go.mod h1:exhIh1PdpDC5vQmyEsGvc4YDM/lyQp/452QxGq/UEso=
|
github.com/nspcc-dev/rfc6979 v0.2.0/go.mod h1:exhIh1PdpDC5vQmyEsGvc4YDM/lyQp/452QxGq/UEso=
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"github.com/nspcc-dev/neofs-api-go/v2/refs"
|
"github.com/nspcc-dev/neofs-api-go/v2/refs"
|
||||||
"github.com/nspcc-dev/neofs-api-go/v2/session"
|
"github.com/nspcc-dev/neofs-api-go/v2/session"
|
||||||
v2signature "github.com/nspcc-dev/neofs-api-go/v2/signature"
|
v2signature "github.com/nspcc-dev/neofs-api-go/v2/signature"
|
||||||
|
"github.com/nspcc-dev/neofs-node/pkg/core/container"
|
||||||
core "github.com/nspcc-dev/neofs-node/pkg/core/container"
|
core "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/local_object_storage/engine"
|
"github.com/nspcc-dev/neofs-node/pkg/local_object_storage/engine"
|
||||||
|
@ -95,9 +96,9 @@ type cfg struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type eACLCfg struct {
|
type eACLCfg struct {
|
||||||
eACLOpts []eacl.Option
|
eaclSource eacl.Source
|
||||||
|
|
||||||
eACL *eacl.Validator
|
eACL *acl.Validator
|
||||||
|
|
||||||
localStorage *engine.StorageEngine
|
localStorage *engine.StorageEngine
|
||||||
|
|
||||||
|
@ -130,7 +131,7 @@ func New(opts ...Option) Service {
|
||||||
opts[i](cfg)
|
opts[i](cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg.eACL = eacl.NewValidator(cfg.eACLOpts...)
|
cfg.eACL = acl.NewValidator()
|
||||||
|
|
||||||
return Service{
|
return Service{
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
|
@ -610,6 +611,20 @@ func eACLCheck(msg interface{}, reqInfo requestInfo, cfg *eACLCfg) bool {
|
||||||
reqInfo.bearer = nil
|
reqInfo.bearer = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
table *acl.Table
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
if reqInfo.bearer == nil {
|
||||||
|
table, err = cfg.eaclSource.GetEACL(reqInfo.cid)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Is(err, container.ErrEACLNotFound)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
table = acl.NewTableFromV2(reqInfo.bearer.GetBody().GetEACL())
|
||||||
|
}
|
||||||
|
|
||||||
// if bearer token is not present, isValidBearer returns true
|
// if bearer token is not present, isValidBearer returns true
|
||||||
if !isValidBearer(reqInfo, cfg.state) {
|
if !isValidBearer(reqInfo, cfg.state) {
|
||||||
return false
|
return false
|
||||||
|
@ -637,7 +652,7 @@ func eACLCheck(msg interface{}, reqInfo requestInfo, cfg *eACLCfg) bool {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
action := cfg.eACL.CalculateAction(new(eacl.ValidationUnit).
|
action := cfg.eACL.CalculateAction(new(acl.ValidationUnit).
|
||||||
WithRole(reqInfo.requestRole).
|
WithRole(reqInfo.requestRole).
|
||||||
WithOperation(reqInfo.operation).
|
WithOperation(reqInfo.operation).
|
||||||
WithContainerID(reqInfo.cid).
|
WithContainerID(reqInfo.cid).
|
||||||
|
@ -645,7 +660,7 @@ func eACLCheck(msg interface{}, reqInfo requestInfo, cfg *eACLCfg) bool {
|
||||||
WithHeaderSource(
|
WithHeaderSource(
|
||||||
eaclV2.NewMessageHeaderSource(hdrSrcOpts...),
|
eaclV2.NewMessageHeaderSource(hdrSrcOpts...),
|
||||||
).
|
).
|
||||||
WithBearerToken(reqInfo.bearer),
|
WithEACLTable(table),
|
||||||
)
|
)
|
||||||
|
|
||||||
return action == acl.ActionAllow
|
return action == acl.ActionAllow
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
package eacl
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/nspcc-dev/neofs-node/pkg/util/logger"
|
|
||||||
)
|
|
||||||
|
|
||||||
func WithLogger(v *logger.Logger) Option {
|
|
||||||
return func(c *cfg) {
|
|
||||||
c.logger = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func WithEACLSource(v Source) Option {
|
|
||||||
return func(c *cfg) {
|
|
||||||
c.storage = v
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +1,6 @@
|
||||||
package eacl
|
package eacl
|
||||||
|
|
||||||
import (
|
import (
|
||||||
bearer "github.com/nspcc-dev/neofs-api-go/v2/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/eacl"
|
"github.com/nspcc-dev/neofs-sdk-go/eacl"
|
||||||
)
|
)
|
||||||
|
@ -18,83 +17,3 @@ type Source interface {
|
||||||
// eACL table is not in source.
|
// eACL table is not in source.
|
||||||
GetEACL(*cid.ID) (*eacl.Table, error)
|
GetEACL(*cid.ID) (*eacl.Table, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Header is an interface of string key-value header.
|
|
||||||
type Header interface {
|
|
||||||
Key() string
|
|
||||||
Value() string
|
|
||||||
}
|
|
||||||
|
|
||||||
// TypedHeaderSource is the interface that wraps
|
|
||||||
// method for selecting typed headers by type.
|
|
||||||
type TypedHeaderSource interface {
|
|
||||||
// HeadersOfType returns the list of key-value headers
|
|
||||||
// of particular type.
|
|
||||||
//
|
|
||||||
// It returns any problem encountered through the boolean
|
|
||||||
// false value.
|
|
||||||
HeadersOfType(eacl.FilterHeaderType) ([]Header, bool)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValidationUnit represents unit of check for Validator.
|
|
||||||
type ValidationUnit struct {
|
|
||||||
cid *cid.ID
|
|
||||||
|
|
||||||
role eacl.Role
|
|
||||||
|
|
||||||
op eacl.Operation
|
|
||||||
|
|
||||||
hdrSrc TypedHeaderSource
|
|
||||||
|
|
||||||
key []byte
|
|
||||||
|
|
||||||
bearer *bearer.BearerToken
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *ValidationUnit) WithContainerID(v *cid.ID) *ValidationUnit {
|
|
||||||
if u != nil {
|
|
||||||
u.cid = v
|
|
||||||
}
|
|
||||||
|
|
||||||
return u
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *ValidationUnit) WithRole(v eacl.Role) *ValidationUnit {
|
|
||||||
if u != nil {
|
|
||||||
u.role = v
|
|
||||||
}
|
|
||||||
|
|
||||||
return u
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *ValidationUnit) WithOperation(v eacl.Operation) *ValidationUnit {
|
|
||||||
if u != nil {
|
|
||||||
u.op = v
|
|
||||||
}
|
|
||||||
|
|
||||||
return u
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *ValidationUnit) WithHeaderSource(v TypedHeaderSource) *ValidationUnit {
|
|
||||||
if u != nil {
|
|
||||||
u.hdrSrc = v
|
|
||||||
}
|
|
||||||
|
|
||||||
return u
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *ValidationUnit) WithSenderKey(v []byte) *ValidationUnit {
|
|
||||||
if u != nil {
|
|
||||||
u.key = v
|
|
||||||
}
|
|
||||||
|
|
||||||
return u
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *ValidationUnit) WithBearerToken(bearer *bearer.BearerToken) *ValidationUnit {
|
|
||||||
if u != nil {
|
|
||||||
u.bearer = bearer
|
|
||||||
}
|
|
||||||
|
|
||||||
return u
|
|
||||||
}
|
|
||||||
|
|
|
@ -10,10 +10,8 @@ import (
|
||||||
objectV2 "github.com/nspcc-dev/neofs-api-go/v2/object"
|
objectV2 "github.com/nspcc-dev/neofs-api-go/v2/object"
|
||||||
"github.com/nspcc-dev/neofs-api-go/v2/session"
|
"github.com/nspcc-dev/neofs-api-go/v2/session"
|
||||||
"github.com/nspcc-dev/neofs-node/pkg/core/object"
|
"github.com/nspcc-dev/neofs-node/pkg/core/object"
|
||||||
eacl2 "github.com/nspcc-dev/neofs-node/pkg/services/object/acl/eacl"
|
|
||||||
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
|
|
||||||
cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test"
|
cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test"
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/eacl"
|
eaclSDK "github.com/nspcc-dev/neofs-sdk-go/eacl"
|
||||||
objectSDK "github.com/nspcc-dev/neofs-sdk-go/object"
|
objectSDK "github.com/nspcc-dev/neofs-sdk-go/object"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
@ -26,20 +24,6 @@ type testLocalStorage struct {
|
||||||
obj *object.Object
|
obj *object.Object
|
||||||
}
|
}
|
||||||
|
|
||||||
type testEACLStorage struct {
|
|
||||||
t *testing.T
|
|
||||||
|
|
||||||
expCID *cid.ID
|
|
||||||
|
|
||||||
table *eacl.Table
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *testEACLStorage) GetEACL(id *cid.ID) (*eacl.Table, error) {
|
|
||||||
require.True(s.t, s.expCID.Equal(id))
|
|
||||||
|
|
||||||
return s.table, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *testLocalStorage) Head(addr *objectSDK.Address) (*object.Object, error) {
|
func (s *testLocalStorage) Head(addr *objectSDK.Address) (*object.Object, error) {
|
||||||
require.True(s.t, addr.ContainerID().Equal(addr.ContainerID()) && addr.ObjectID().Equal(addr.ObjectID()))
|
require.True(s.t, addr.ContainerID().Equal(addr.ContainerID()) && addr.ObjectID().Equal(addr.ObjectID()))
|
||||||
|
|
||||||
|
@ -109,18 +93,18 @@ func TestHeadRequest(t *testing.T) {
|
||||||
attr.SetValue(attrVal)
|
attr.SetValue(attrVal)
|
||||||
obj.SetAttributes(attr)
|
obj.SetAttributes(attr)
|
||||||
|
|
||||||
table := new(eacl.Table)
|
table := new(eaclSDK.Table)
|
||||||
|
|
||||||
priv, err := keys.NewPrivateKey()
|
priv, err := keys.NewPrivateKey()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
senderKey := priv.PublicKey()
|
senderKey := priv.PublicKey()
|
||||||
|
|
||||||
r := eacl.NewRecord()
|
r := eaclSDK.NewRecord()
|
||||||
r.SetOperation(eacl.OperationHead)
|
r.SetOperation(eaclSDK.OperationHead)
|
||||||
r.SetAction(eacl.ActionDeny)
|
r.SetAction(eaclSDK.ActionDeny)
|
||||||
r.AddFilter(eacl.HeaderFromObject, eacl.MatchStringEqual, attrKey, attrVal)
|
r.AddFilter(eaclSDK.HeaderFromObject, eaclSDK.MatchStringEqual, attrKey, attrVal)
|
||||||
r.AddFilter(eacl.HeaderFromRequest, eacl.MatchStringEqual, xKey, xVal)
|
r.AddFilter(eaclSDK.HeaderFromRequest, eaclSDK.MatchStringEqual, xKey, xVal)
|
||||||
eacl.AddFormedTarget(r, eacl.RoleUnknown, (ecdsa.PublicKey)(*senderKey))
|
eaclSDK.AddFormedTarget(r, eaclSDK.RoleUnknown, (ecdsa.PublicKey)(*senderKey))
|
||||||
|
|
||||||
table.AddRecord(r)
|
table.AddRecord(r)
|
||||||
|
|
||||||
|
@ -131,36 +115,29 @@ func TestHeadRequest(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
cid := addr.ContainerID()
|
cid := addr.ContainerID()
|
||||||
unit := new(eacl2.ValidationUnit).
|
unit := new(eaclSDK.ValidationUnit).
|
||||||
WithContainerID(cid).
|
WithContainerID(cid).
|
||||||
WithOperation(eacl.OperationHead).
|
WithOperation(eaclSDK.OperationHead).
|
||||||
WithSenderKey(senderKey.Bytes()).
|
WithSenderKey(senderKey.Bytes()).
|
||||||
WithHeaderSource(
|
WithHeaderSource(
|
||||||
NewMessageHeaderSource(
|
NewMessageHeaderSource(
|
||||||
WithObjectStorage(lStorage),
|
WithObjectStorage(lStorage),
|
||||||
WithServiceRequest(req),
|
WithServiceRequest(req),
|
||||||
),
|
),
|
||||||
)
|
).
|
||||||
|
WithEACLTable(table)
|
||||||
|
|
||||||
eStorage := &testEACLStorage{
|
validator := eaclSDK.NewValidator()
|
||||||
t: t,
|
|
||||||
expCID: cid,
|
|
||||||
table: table,
|
|
||||||
}
|
|
||||||
|
|
||||||
validator := eacl2.NewValidator(
|
require.Equal(t, eaclSDK.ActionDeny, validator.CalculateAction(unit))
|
||||||
eacl2.WithEACLSource(eStorage),
|
|
||||||
)
|
|
||||||
|
|
||||||
require.Equal(t, eacl.ActionDeny, validator.CalculateAction(unit))
|
|
||||||
|
|
||||||
meta.SetXHeaders(nil)
|
meta.SetXHeaders(nil)
|
||||||
|
|
||||||
require.Equal(t, eacl.ActionAllow, validator.CalculateAction(unit))
|
require.Equal(t, eaclSDK.ActionAllow, validator.CalculateAction(unit))
|
||||||
|
|
||||||
meta.SetXHeaders(xHdrs)
|
meta.SetXHeaders(xHdrs)
|
||||||
|
|
||||||
obj.SetAttributes(nil)
|
obj.SetAttributes(nil)
|
||||||
|
|
||||||
require.Equal(t, eacl.ActionAllow, validator.CalculateAction(unit))
|
require.Equal(t, eaclSDK.ActionAllow, validator.CalculateAction(unit))
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@ import (
|
||||||
"github.com/nspcc-dev/neofs-api-go/v2/refs"
|
"github.com/nspcc-dev/neofs-api-go/v2/refs"
|
||||||
"github.com/nspcc-dev/neofs-api-go/v2/session"
|
"github.com/nspcc-dev/neofs-api-go/v2/session"
|
||||||
"github.com/nspcc-dev/neofs-node/pkg/core/object"
|
"github.com/nspcc-dev/neofs-node/pkg/core/object"
|
||||||
"github.com/nspcc-dev/neofs-node/pkg/services/object/acl/eacl"
|
|
||||||
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"
|
||||||
objectSDK "github.com/nspcc-dev/neofs-sdk-go/object"
|
objectSDK "github.com/nspcc-dev/neofs-sdk-go/object"
|
||||||
|
@ -47,7 +46,7 @@ func defaultCfg() *cfg {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMessageHeaderSource(opts ...Option) eacl.TypedHeaderSource {
|
func NewMessageHeaderSource(opts ...Option) eaclSDK.TypedHeaderSource {
|
||||||
cfg := defaultCfg()
|
cfg := defaultCfg()
|
||||||
|
|
||||||
for i := range opts {
|
for i := range opts {
|
||||||
|
@ -59,7 +58,7 @@ func NewMessageHeaderSource(opts ...Option) eacl.TypedHeaderSource {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *headerSource) HeadersOfType(typ eaclSDK.FilterHeaderType) ([]eacl.Header, bool) {
|
func (h *headerSource) HeadersOfType(typ eaclSDK.FilterHeaderType) ([]eaclSDK.Header, bool) {
|
||||||
switch typ {
|
switch typ {
|
||||||
default:
|
default:
|
||||||
return nil, true
|
return nil, true
|
||||||
|
@ -70,10 +69,10 @@ func (h *headerSource) HeadersOfType(typ eaclSDK.FilterHeaderType) ([]eacl.Heade
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func requestHeaders(msg xHeaderSource) []eacl.Header {
|
func requestHeaders(msg xHeaderSource) []eaclSDK.Header {
|
||||||
xHdrs := msg.GetXHeaders()
|
xHdrs := msg.GetXHeaders()
|
||||||
|
|
||||||
res := make([]eacl.Header, 0, len(xHdrs))
|
res := make([]eaclSDK.Header, 0, len(xHdrs))
|
||||||
|
|
||||||
for i := range xHdrs {
|
for i := range xHdrs {
|
||||||
res = append(res, sessionSDK.NewXHeaderFromV2(xHdrs[i]))
|
res = append(res, sessionSDK.NewXHeaderFromV2(xHdrs[i]))
|
||||||
|
@ -82,7 +81,7 @@ func requestHeaders(msg xHeaderSource) []eacl.Header {
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *headerSource) objectHeaders() ([]eacl.Header, bool) {
|
func (h *headerSource) objectHeaders() ([]eaclSDK.Header, bool) {
|
||||||
var addr *objectSDK.Address
|
var addr *objectSDK.Address
|
||||||
if h.addr != nil {
|
if h.addr != nil {
|
||||||
addr = objectSDK.NewAddressFromV2(h.addr)
|
addr = objectSDK.NewAddressFromV2(h.addr)
|
||||||
|
@ -119,7 +118,7 @@ func (h *headerSource) objectHeaders() ([]eacl.Header, bool) {
|
||||||
return hs, true
|
return hs, true
|
||||||
}
|
}
|
||||||
case *objectV2.SearchRequest:
|
case *objectV2.SearchRequest:
|
||||||
return []eacl.Header{cidHeader(
|
return []eaclSDK.Header{cidHeader(
|
||||||
cid.NewFromV2(
|
cid.NewFromV2(
|
||||||
req.GetBody().GetContainerID()),
|
req.GetBody().GetContainerID()),
|
||||||
)}, true
|
)}, true
|
||||||
|
@ -165,7 +164,7 @@ func (h *headerSource) objectHeaders() ([]eacl.Header, bool) {
|
||||||
return nil, true
|
return nil, true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *headerSource) localObjectHeaders(addrV2 *refs.Address) ([]eacl.Header, bool) {
|
func (h *headerSource) localObjectHeaders(addrV2 *refs.Address) ([]eaclSDK.Header, bool) {
|
||||||
addr := objectSDK.NewAddressFromV2(addrV2)
|
addr := objectSDK.NewAddressFromV2(addrV2)
|
||||||
|
|
||||||
obj, err := h.storage.Head(addr)
|
obj, err := h.storage.Head(addr)
|
||||||
|
@ -176,22 +175,22 @@ func (h *headerSource) localObjectHeaders(addrV2 *refs.Address) ([]eacl.Header,
|
||||||
return addressHeaders(addr), false
|
return addressHeaders(addr), false
|
||||||
}
|
}
|
||||||
|
|
||||||
func cidHeader(cid *cid.ID) eacl.Header {
|
func cidHeader(cid *cid.ID) eaclSDK.Header {
|
||||||
return &sysObjHdr{
|
return &sysObjHdr{
|
||||||
k: acl.FilterObjectContainerID,
|
k: acl.FilterObjectContainerID,
|
||||||
v: cidValue(cid),
|
v: cidValue(cid),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func oidHeader(oid *objectSDK.ID) eacl.Header {
|
func oidHeader(oid *objectSDK.ID) eaclSDK.Header {
|
||||||
return &sysObjHdr{
|
return &sysObjHdr{
|
||||||
k: acl.FilterObjectID,
|
k: acl.FilterObjectID,
|
||||||
v: idValue(oid),
|
v: idValue(oid),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func addressHeaders(addr *objectSDK.Address) []eacl.Header {
|
func addressHeaders(addr *objectSDK.Address) []eaclSDK.Header {
|
||||||
res := make([]eacl.Header, 1, 2)
|
res := make([]eaclSDK.Header, 1, 2)
|
||||||
res[0] = cidHeader(addr.ContainerID())
|
res[0] = cidHeader(addr.ContainerID())
|
||||||
|
|
||||||
if oid := addr.ObjectID(); oid != nil {
|
if oid := addr.ObjectID(); oid != nil {
|
||||||
|
|
|
@ -5,8 +5,8 @@ import (
|
||||||
|
|
||||||
"github.com/nspcc-dev/neofs-api-go/v2/acl"
|
"github.com/nspcc-dev/neofs-api-go/v2/acl"
|
||||||
"github.com/nspcc-dev/neofs-node/pkg/core/object"
|
"github.com/nspcc-dev/neofs-node/pkg/core/object"
|
||||||
"github.com/nspcc-dev/neofs-node/pkg/services/object/acl/eacl"
|
|
||||||
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"
|
||||||
objectSDK "github.com/nspcc-dev/neofs-sdk-go/object"
|
objectSDK "github.com/nspcc-dev/neofs-sdk-go/object"
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/owner"
|
"github.com/nspcc-dev/neofs-sdk-go/owner"
|
||||||
)
|
)
|
||||||
|
@ -39,9 +39,9 @@ func u64Value(v uint64) string {
|
||||||
return strconv.FormatUint(v, 10)
|
return strconv.FormatUint(v, 10)
|
||||||
}
|
}
|
||||||
|
|
||||||
func headersFromObject(obj *object.Object, addr *objectSDK.Address) []eacl.Header {
|
func headersFromObject(obj *object.Object, addr *objectSDK.Address) []eaclSDK.Header {
|
||||||
// TODO: optimize allocs
|
// TODO: optimize allocs
|
||||||
res := make([]eacl.Header, 0)
|
res := make([]eaclSDK.Header, 0)
|
||||||
|
|
||||||
for ; obj != nil; obj = obj.GetParent() {
|
for ; obj != nil; obj = obj.GetParent() {
|
||||||
res = append(res,
|
res = append(res,
|
||||||
|
@ -85,7 +85,7 @@ func headersFromObject(obj *object.Object, addr *objectSDK.Address) []eacl.Heade
|
||||||
)
|
)
|
||||||
|
|
||||||
attrs := obj.Attributes()
|
attrs := obj.Attributes()
|
||||||
hs := make([]eacl.Header, 0, len(attrs))
|
hs := make([]eaclSDK.Header, 0, len(attrs))
|
||||||
|
|
||||||
for i := range attrs {
|
for i := range attrs {
|
||||||
hs = append(hs, attrs[i])
|
hs = append(hs, attrs[i])
|
||||||
|
|
|
@ -1,189 +0,0 @@
|
||||||
package eacl
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"errors"
|
|
||||||
|
|
||||||
"github.com/nspcc-dev/neofs-node/pkg/core/container"
|
|
||||||
"github.com/nspcc-dev/neofs-node/pkg/util/logger"
|
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/eacl"
|
|
||||||
"go.uber.org/zap"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Validator is a tool that calculates
|
|
||||||
// the action on a request according
|
|
||||||
// to the extended ACL rule table.
|
|
||||||
type Validator struct {
|
|
||||||
*cfg
|
|
||||||
}
|
|
||||||
|
|
||||||
// Option represents Validator option.
|
|
||||||
type Option func(*cfg)
|
|
||||||
|
|
||||||
type cfg struct {
|
|
||||||
logger *logger.Logger
|
|
||||||
|
|
||||||
storage Source
|
|
||||||
}
|
|
||||||
|
|
||||||
func defaultCfg() *cfg {
|
|
||||||
return &cfg{
|
|
||||||
logger: zap.L(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewValidator creates and initializes a new Validator using options.
|
|
||||||
func NewValidator(opts ...Option) *Validator {
|
|
||||||
cfg := defaultCfg()
|
|
||||||
|
|
||||||
for i := range opts {
|
|
||||||
opts[i](cfg)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &Validator{
|
|
||||||
cfg: cfg,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// CalculateAction calculates action on the request according
|
|
||||||
// to its information represented in ValidationUnit.
|
|
||||||
//
|
|
||||||
// The action is calculated according to the application of
|
|
||||||
// eACL table of rules to the request.
|
|
||||||
//
|
|
||||||
// If the eACL table is not available at the time of the call,
|
|
||||||
// eacl.ActionUnknown is returned.
|
|
||||||
//
|
|
||||||
// If no matching table entry is found, ActionAllow is returned.
|
|
||||||
func (v *Validator) CalculateAction(unit *ValidationUnit) eacl.Action {
|
|
||||||
var (
|
|
||||||
err error
|
|
||||||
table *eacl.Table
|
|
||||||
)
|
|
||||||
|
|
||||||
if unit.bearer != nil {
|
|
||||||
table = eacl.NewTableFromV2(unit.bearer.GetBody().GetEACL())
|
|
||||||
} else {
|
|
||||||
// get eACL table by container ID
|
|
||||||
table, err = v.storage.GetEACL(unit.cid)
|
|
||||||
if err != nil {
|
|
||||||
if errors.Is(err, container.ErrEACLNotFound) {
|
|
||||||
return eacl.ActionAllow
|
|
||||||
}
|
|
||||||
|
|
||||||
v.logger.Error("could not get eACL table",
|
|
||||||
zap.String("error", err.Error()),
|
|
||||||
)
|
|
||||||
|
|
||||||
return eacl.ActionUnknown
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return tableAction(unit, table)
|
|
||||||
}
|
|
||||||
|
|
||||||
// calculates action on the request based on the eACL rules.
|
|
||||||
func tableAction(unit *ValidationUnit, table *eacl.Table) eacl.Action {
|
|
||||||
for _, record := range table.Records() {
|
|
||||||
// check type of operation
|
|
||||||
if record.Operation() != unit.op {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// check target
|
|
||||||
if !targetMatches(unit, record) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// check headers
|
|
||||||
switch val := matchFilters(unit.hdrSrc, record.Filters()); {
|
|
||||||
case val < 0:
|
|
||||||
// headers of some type could not be composed => allow
|
|
||||||
return eacl.ActionAllow
|
|
||||||
case val == 0:
|
|
||||||
return record.Action()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return eacl.ActionAllow
|
|
||||||
}
|
|
||||||
|
|
||||||
// returns:
|
|
||||||
// - positive value if no matching header is found for at least one filter;
|
|
||||||
// - zero if at least one suitable header is found for all filters;
|
|
||||||
// - negative value if the headers of at least one filter cannot be obtained.
|
|
||||||
func matchFilters(hdrSrc TypedHeaderSource, filters []*eacl.Filter) int {
|
|
||||||
matched := 0
|
|
||||||
|
|
||||||
for _, filter := range filters {
|
|
||||||
headers, ok := hdrSrc.HeadersOfType(filter.From())
|
|
||||||
if !ok {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
// get headers of filtering type
|
|
||||||
for _, header := range headers {
|
|
||||||
// prevent NPE
|
|
||||||
if header == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// check header name
|
|
||||||
if header.Key() != filter.Key() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// get match function
|
|
||||||
matchFn, ok := mMatchFns[filter.Matcher()]
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// check match
|
|
||||||
if !matchFn(header, filter) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// increment match counter
|
|
||||||
matched++
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return len(filters) - matched
|
|
||||||
}
|
|
||||||
|
|
||||||
// returns true if one of ExtendedACLTarget has
|
|
||||||
// suitable target OR suitable public key.
|
|
||||||
func targetMatches(unit *ValidationUnit, record *eacl.Record) bool {
|
|
||||||
for _, target := range record.Targets() {
|
|
||||||
// check public key match
|
|
||||||
if pubs := target.BinaryKeys(); len(pubs) != 0 {
|
|
||||||
for _, key := range pubs {
|
|
||||||
if bytes.Equal(key, unit.key) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// check target group match
|
|
||||||
if unit.role == target.Role() {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Maps match type to corresponding function.
|
|
||||||
var mMatchFns = map[eacl.Match]func(Header, *eacl.Filter) bool{
|
|
||||||
eacl.MatchStringEqual: func(header Header, filter *eacl.Filter) bool {
|
|
||||||
return header.Value() == filter.Value()
|
|
||||||
},
|
|
||||||
|
|
||||||
eacl.MatchStringNotEqual: func(header Header, filter *eacl.Filter) bool {
|
|
||||||
return header.Value() != filter.Value()
|
|
||||||
},
|
|
||||||
}
|
|
|
@ -1,52 +0,0 @@
|
||||||
package eacl
|
|
||||||
|
|
||||||
import (
|
|
||||||
"math/rand"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/eacl"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestTargetMatches(t *testing.T) {
|
|
||||||
pubs := make([][]byte, 3)
|
|
||||||
for i := range pubs {
|
|
||||||
pubs[i] = make([]byte, 33)
|
|
||||||
pubs[i][0] = 0x02
|
|
||||||
|
|
||||||
_, err := rand.Read(pubs[i][1:])
|
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
tgt1 := eacl.NewTarget()
|
|
||||||
tgt1.SetBinaryKeys(pubs[0:2])
|
|
||||||
tgt1.SetRole(eacl.RoleUser)
|
|
||||||
|
|
||||||
tgt2 := eacl.NewTarget()
|
|
||||||
tgt2.SetRole(eacl.RoleOthers)
|
|
||||||
|
|
||||||
r := eacl.NewRecord()
|
|
||||||
r.SetTargets(tgt1, tgt2)
|
|
||||||
|
|
||||||
u := newValidationUnit(eacl.RoleUser, pubs[0])
|
|
||||||
require.True(t, targetMatches(u, r))
|
|
||||||
|
|
||||||
u = newValidationUnit(eacl.RoleUser, pubs[2])
|
|
||||||
require.False(t, targetMatches(u, r))
|
|
||||||
|
|
||||||
u = newValidationUnit(eacl.RoleUnknown, pubs[1])
|
|
||||||
require.True(t, targetMatches(u, r))
|
|
||||||
|
|
||||||
u = newValidationUnit(eacl.RoleOthers, pubs[2])
|
|
||||||
require.True(t, targetMatches(u, r))
|
|
||||||
|
|
||||||
u = newValidationUnit(eacl.RoleSystem, pubs[2])
|
|
||||||
require.False(t, targetMatches(u, r))
|
|
||||||
}
|
|
||||||
|
|
||||||
func newValidationUnit(role eacl.Role, key []byte) *ValidationUnit {
|
|
||||||
return &ValidationUnit{
|
|
||||||
role: role,
|
|
||||||
key: key,
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -29,10 +29,10 @@ func WithNextService(v objectSvc.ServiceServer) Option {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithEACLValidatorOptions returns options to set eACL validator options.
|
// WithEACLSource returns options to set eACL table source.
|
||||||
func WithEACLValidatorOptions(v ...eacl.Option) Option {
|
func WithEACLSource(v eacl.Source) Option {
|
||||||
return func(c *cfg) {
|
return func(c *cfg) {
|
||||||
c.eACLOpts = v
|
c.eACLCfg.eaclSource = v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue