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 57 additions and 409 deletions
|
@ -20,7 +20,6 @@ import (
|
|||
objectTransportGRPC "github.com/nspcc-dev/neofs-node/pkg/network/transport/object/grpc"
|
||||
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/eacl"
|
||||
deletesvc "github.com/nspcc-dev/neofs-node/pkg/services/object/delete"
|
||||
deletesvcV2 "github.com/nspcc-dev/neofs-node/pkg/services/object/delete/v2"
|
||||
getsvc "github.com/nspcc-dev/neofs-node/pkg/services/object/get"
|
||||
|
@ -365,10 +364,7 @@ func initObjectService(c *cfg) {
|
|||
),
|
||||
acl.WithNextService(splitSvc),
|
||||
acl.WithLocalStorage(ls),
|
||||
acl.WithEACLValidatorOptions(
|
||||
eacl.WithEACLSource(c.cfgObject.eaclSource),
|
||||
eacl.WithLogger(c.log),
|
||||
),
|
||||
acl.WithEACLSource(c.cfgObject.eaclSource),
|
||||
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/nspcc-dev/hrw v1.0.9
|
||||
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-sdk-go v0.0.0-20220113123743-7f3162110659
|
||||
github.com/nspcc-dev/neofs-api-go/v2 v2.11.2-0.20220114101721-227a871a04ac
|
||||
github.com/nspcc-dev/neofs-sdk-go v0.0.0-20220119080627-f83ff628fb19
|
||||
github.com/nspcc-dev/tzhash v1.4.0
|
||||
github.com/panjf2000/ants/v2 v2.4.0
|
||||
github.com/paulmach/orb v0.2.2
|
||||
|
|
BIN
go.sum
BIN
go.sum
Binary file not shown.
|
@ -12,6 +12,7 @@ import (
|
|||
"github.com/nspcc-dev/neofs-api-go/v2/refs"
|
||||
"github.com/nspcc-dev/neofs-api-go/v2/session"
|
||||
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"
|
||||
"github.com/nspcc-dev/neofs-node/pkg/core/netmap"
|
||||
"github.com/nspcc-dev/neofs-node/pkg/local_object_storage/engine"
|
||||
|
@ -95,9 +96,9 @@ type cfg struct {
|
|||
}
|
||||
|
||||
type eACLCfg struct {
|
||||
eACLOpts []eacl.Option
|
||||
eaclSource eacl.Source
|
||||
|
||||
eACL *eacl.Validator
|
||||
eACL *acl.Validator
|
||||
|
||||
localStorage *engine.StorageEngine
|
||||
|
||||
|
@ -130,7 +131,7 @@ func New(opts ...Option) Service {
|
|||
opts[i](cfg)
|
||||
}
|
||||
|
||||
cfg.eACL = eacl.NewValidator(cfg.eACLOpts...)
|
||||
cfg.eACL = acl.NewValidator()
|
||||
|
||||
return Service{
|
||||
cfg: cfg,
|
||||
|
@ -610,6 +611,20 @@ func eACLCheck(msg interface{}, reqInfo requestInfo, cfg *eACLCfg) bool {
|
|||
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 !isValidBearer(reqInfo, cfg.state) {
|
||||
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).
|
||||
WithOperation(reqInfo.operation).
|
||||
WithContainerID(reqInfo.cid).
|
||||
|
@ -645,7 +660,7 @@ func eACLCheck(msg interface{}, reqInfo requestInfo, cfg *eACLCfg) bool {
|
|||
WithHeaderSource(
|
||||
eaclV2.NewMessageHeaderSource(hdrSrcOpts...),
|
||||
).
|
||||
WithBearerToken(reqInfo.bearer),
|
||||
WithEACLTable(table),
|
||||
)
|
||||
|
||||
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
|
||||
|
||||
import (
|
||||
bearer "github.com/nspcc-dev/neofs-api-go/v2/acl"
|
||||
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/eacl"
|
||||
)
|
||||
|
@ -18,83 +17,3 @@ type Source interface {
|
|||
// eACL table is not in source.
|
||||
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"
|
||||
"github.com/nspcc-dev/neofs-api-go/v2/session"
|
||||
"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"
|
||||
"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"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
@ -26,20 +24,6 @@ type testLocalStorage struct {
|
|||
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) {
|
||||
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)
|
||||
obj.SetAttributes(attr)
|
||||
|
||||
table := new(eacl.Table)
|
||||
table := new(eaclSDK.Table)
|
||||
|
||||
priv, err := keys.NewPrivateKey()
|
||||
require.NoError(t, err)
|
||||
senderKey := priv.PublicKey()
|
||||
|
||||
r := eacl.NewRecord()
|
||||
r.SetOperation(eacl.OperationHead)
|
||||
r.SetAction(eacl.ActionDeny)
|
||||
r.AddFilter(eacl.HeaderFromObject, eacl.MatchStringEqual, attrKey, attrVal)
|
||||
r.AddFilter(eacl.HeaderFromRequest, eacl.MatchStringEqual, xKey, xVal)
|
||||
eacl.AddFormedTarget(r, eacl.RoleUnknown, (ecdsa.PublicKey)(*senderKey))
|
||||
r := eaclSDK.NewRecord()
|
||||
r.SetOperation(eaclSDK.OperationHead)
|
||||
r.SetAction(eaclSDK.ActionDeny)
|
||||
r.AddFilter(eaclSDK.HeaderFromObject, eaclSDK.MatchStringEqual, attrKey, attrVal)
|
||||
r.AddFilter(eaclSDK.HeaderFromRequest, eaclSDK.MatchStringEqual, xKey, xVal)
|
||||
eaclSDK.AddFormedTarget(r, eaclSDK.RoleUnknown, (ecdsa.PublicKey)(*senderKey))
|
||||
|
||||
table.AddRecord(r)
|
||||
|
||||
|
@ -131,36 +115,29 @@ func TestHeadRequest(t *testing.T) {
|
|||
}
|
||||
|
||||
cid := addr.ContainerID()
|
||||
unit := new(eacl2.ValidationUnit).
|
||||
unit := new(eaclSDK.ValidationUnit).
|
||||
WithContainerID(cid).
|
||||
WithOperation(eacl.OperationHead).
|
||||
WithOperation(eaclSDK.OperationHead).
|
||||
WithSenderKey(senderKey.Bytes()).
|
||||
WithHeaderSource(
|
||||
NewMessageHeaderSource(
|
||||
WithObjectStorage(lStorage),
|
||||
WithServiceRequest(req),
|
||||
),
|
||||
)
|
||||
).
|
||||
WithEACLTable(table)
|
||||
|
||||
eStorage := &testEACLStorage{
|
||||
t: t,
|
||||
expCID: cid,
|
||||
table: table,
|
||||
}
|
||||
validator := eaclSDK.NewValidator()
|
||||
|
||||
validator := eacl2.NewValidator(
|
||||
eacl2.WithEACLSource(eStorage),
|
||||
)
|
||||
|
||||
require.Equal(t, eacl.ActionDeny, validator.CalculateAction(unit))
|
||||
require.Equal(t, eaclSDK.ActionDeny, validator.CalculateAction(unit))
|
||||
|
||||
meta.SetXHeaders(nil)
|
||||
|
||||
require.Equal(t, eacl.ActionAllow, validator.CalculateAction(unit))
|
||||
require.Equal(t, eaclSDK.ActionAllow, validator.CalculateAction(unit))
|
||||
|
||||
meta.SetXHeaders(xHdrs)
|
||||
|
||||
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/session"
|
||||
"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"
|
||||
eaclSDK "github.com/nspcc-dev/neofs-sdk-go/eacl"
|
||||
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()
|
||||
|
||||
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 {
|
||||
default:
|
||||
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()
|
||||
|
||||
res := make([]eacl.Header, 0, len(xHdrs))
|
||||
res := make([]eaclSDK.Header, 0, len(xHdrs))
|
||||
|
||||
for i := range xHdrs {
|
||||
res = append(res, sessionSDK.NewXHeaderFromV2(xHdrs[i]))
|
||||
|
@ -82,7 +81,7 @@ func requestHeaders(msg xHeaderSource) []eacl.Header {
|
|||
return res
|
||||
}
|
||||
|
||||
func (h *headerSource) objectHeaders() ([]eacl.Header, bool) {
|
||||
func (h *headerSource) objectHeaders() ([]eaclSDK.Header, bool) {
|
||||
var addr *objectSDK.Address
|
||||
if h.addr != nil {
|
||||
addr = objectSDK.NewAddressFromV2(h.addr)
|
||||
|
@ -119,7 +118,7 @@ func (h *headerSource) objectHeaders() ([]eacl.Header, bool) {
|
|||
return hs, true
|
||||
}
|
||||
case *objectV2.SearchRequest:
|
||||
return []eacl.Header{cidHeader(
|
||||
return []eaclSDK.Header{cidHeader(
|
||||
cid.NewFromV2(
|
||||
req.GetBody().GetContainerID()),
|
||||
)}, true
|
||||
|
@ -165,7 +164,7 @@ func (h *headerSource) objectHeaders() ([]eacl.Header, bool) {
|
|||
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)
|
||||
|
||||
obj, err := h.storage.Head(addr)
|
||||
|
@ -176,22 +175,22 @@ func (h *headerSource) localObjectHeaders(addrV2 *refs.Address) ([]eacl.Header,
|
|||
return addressHeaders(addr), false
|
||||
}
|
||||
|
||||
func cidHeader(cid *cid.ID) eacl.Header {
|
||||
func cidHeader(cid *cid.ID) eaclSDK.Header {
|
||||
return &sysObjHdr{
|
||||
k: acl.FilterObjectContainerID,
|
||||
v: cidValue(cid),
|
||||
}
|
||||
}
|
||||
|
||||
func oidHeader(oid *objectSDK.ID) eacl.Header {
|
||||
func oidHeader(oid *objectSDK.ID) eaclSDK.Header {
|
||||
return &sysObjHdr{
|
||||
k: acl.FilterObjectID,
|
||||
v: idValue(oid),
|
||||
}
|
||||
}
|
||||
|
||||
func addressHeaders(addr *objectSDK.Address) []eacl.Header {
|
||||
res := make([]eacl.Header, 1, 2)
|
||||
func addressHeaders(addr *objectSDK.Address) []eaclSDK.Header {
|
||||
res := make([]eaclSDK.Header, 1, 2)
|
||||
res[0] = cidHeader(addr.ContainerID())
|
||||
|
||||
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-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"
|
||||
eaclSDK "github.com/nspcc-dev/neofs-sdk-go/eacl"
|
||||
objectSDK "github.com/nspcc-dev/neofs-sdk-go/object"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/owner"
|
||||
)
|
||||
|
@ -39,9 +39,9 @@ func u64Value(v uint64) string {
|
|||
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
|
||||
res := make([]eacl.Header, 0)
|
||||
res := make([]eaclSDK.Header, 0)
|
||||
|
||||
for ; obj != nil; obj = obj.GetParent() {
|
||||
res = append(res,
|
||||
|
@ -85,7 +85,7 @@ func headersFromObject(obj *object.Object, addr *objectSDK.Address) []eacl.Heade
|
|||
)
|
||||
|
||||
attrs := obj.Attributes()
|
||||
hs := make([]eacl.Header, 0, len(attrs))
|
||||
hs := make([]eaclSDK.Header, 0, len(attrs))
|
||||
|
||||
for i := range attrs {
|
||||
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.
|
||||
func WithEACLValidatorOptions(v ...eacl.Option) Option {
|
||||
// WithEACLSource returns options to set eACL table source.
|
||||
func WithEACLSource(v eacl.Source) Option {
|
||||
return func(c *cfg) {
|
||||
c.eACLOpts = v
|
||||
c.eACLCfg.eaclSource = v
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue