forked from TrueCloudLab/frostfs-sdk-go
[#36] eacl: add eACL table to ValidationUnit
Improve SDK usability a bit: 1. Replace bearer and storage with a single eACL table. This way caller can implement it's own behaviour for missing eACL. 2. Remove logging. SDK library shouldn't be dependent on a specific logger. Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
This commit is contained in:
parent
8c5a596ea2
commit
f83ff628fb
4 changed files with 28 additions and 141 deletions
34
eacl/opts.go
34
eacl/opts.go
|
@ -1,34 +0,0 @@
|
||||||
package eacl
|
|
||||||
|
|
||||||
import (
|
|
||||||
"go.uber.org/zap"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Option represents Validator option.
|
|
||||||
type Option func(*cfg)
|
|
||||||
|
|
||||||
type cfg struct {
|
|
||||||
logger *zap.Logger
|
|
||||||
|
|
||||||
storage Source
|
|
||||||
}
|
|
||||||
|
|
||||||
func defaultCfg() *cfg {
|
|
||||||
return &cfg{
|
|
||||||
logger: zap.L(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithLogger configures the Validator to use logger v.
|
|
||||||
func WithLogger(v *zap.Logger) Option {
|
|
||||||
return func(c *cfg) {
|
|
||||||
c.logger = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithEACLSource configures the validator to use v as eACL source.
|
|
||||||
func WithEACLSource(v Source) Option {
|
|
||||||
return func(c *cfg) {
|
|
||||||
c.storage = v
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,25 +1,9 @@
|
||||||
package eacl
|
package eacl
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
|
|
||||||
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"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Source is the interface that wraps
|
|
||||||
// basic methods of extended ACL table source.
|
|
||||||
type Source interface {
|
|
||||||
// GetEACL reads the table from the source by identifier.
|
|
||||||
// It returns any error encountered.
|
|
||||||
//
|
|
||||||
// GetEACL must return exactly one non-nil value.
|
|
||||||
//
|
|
||||||
// Must return pkg/core/container.ErrEACLNotFound if requested
|
|
||||||
// eACL table is not in source.
|
|
||||||
GetEACL(*cid.ID) (*Table, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Header is an interface of string key-value header.
|
// Header is an interface of string key-value header.
|
||||||
type Header interface {
|
type Header interface {
|
||||||
Key() string
|
Key() string
|
||||||
|
@ -49,13 +33,9 @@ type ValidationUnit struct {
|
||||||
|
|
||||||
key []byte
|
key []byte
|
||||||
|
|
||||||
bearer *bearer.BearerToken
|
table *Table
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrEACLNotFound is returned by eACL storage implementations when
|
|
||||||
// requested eACL table is not in storage.
|
|
||||||
var ErrEACLNotFound = errors.New("extended ACL table is not set for this container")
|
|
||||||
|
|
||||||
// WithContainerID configures ValidationUnit to use v as request's container ID.
|
// WithContainerID configures ValidationUnit to use v as request's container ID.
|
||||||
func (u *ValidationUnit) WithContainerID(v *cid.ID) *ValidationUnit {
|
func (u *ValidationUnit) WithContainerID(v *cid.ID) *ValidationUnit {
|
||||||
if u != nil {
|
if u != nil {
|
||||||
|
@ -102,9 +82,9 @@ func (u *ValidationUnit) WithSenderKey(v []byte) *ValidationUnit {
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithBearerToken configures ValidationUnit to use v as request's bearer token.
|
// WithBearerToken configures ValidationUnit to use v as request's bearer token.
|
||||||
func (u *ValidationUnit) WithBearerToken(bearer *bearer.BearerToken) *ValidationUnit {
|
func (u *ValidationUnit) WithEACLTable(table *Table) *ValidationUnit {
|
||||||
if u != nil {
|
if u != nil {
|
||||||
u.bearer = bearer
|
u.table = table
|
||||||
}
|
}
|
||||||
|
|
||||||
return u
|
return u
|
||||||
|
|
|
@ -2,29 +2,17 @@ package eacl
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
|
||||||
|
|
||||||
"go.uber.org/zap"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Validator is a tool that calculates
|
// Validator is a tool that calculates
|
||||||
// the action on a request according
|
// the action on a request according
|
||||||
// to the extended ACL rule table.
|
// to the extended ACL rule table.
|
||||||
type Validator struct {
|
type Validator struct {
|
||||||
*cfg
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewValidator creates and initializes a new Validator using options.
|
// NewValidator creates and initializes a new Validator using options.
|
||||||
func NewValidator(opts ...Option) *Validator {
|
func NewValidator() *Validator {
|
||||||
cfg := defaultCfg()
|
return &Validator{}
|
||||||
|
|
||||||
for i := range opts {
|
|
||||||
opts[i](cfg)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &Validator{
|
|
||||||
cfg: cfg,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CalculateAction calculates action on the request according
|
// CalculateAction calculates action on the request according
|
||||||
|
@ -33,40 +21,9 @@ func NewValidator(opts ...Option) *Validator {
|
||||||
// The action is calculated according to the application of
|
// The action is calculated according to the application of
|
||||||
// eACL table of rules to the request.
|
// eACL table of rules to the request.
|
||||||
//
|
//
|
||||||
// If the eACL table is not available at the time of the call,
|
|
||||||
// ActionUnknown is returned.
|
|
||||||
//
|
|
||||||
// If no matching table entry is found, ActionAllow is returned.
|
// If no matching table entry is found, ActionAllow is returned.
|
||||||
func (v *Validator) CalculateAction(unit *ValidationUnit) Action {
|
func (v *Validator) CalculateAction(unit *ValidationUnit) Action {
|
||||||
var (
|
for _, record := range unit.table.Records() {
|
||||||
err error
|
|
||||||
table *Table
|
|
||||||
)
|
|
||||||
|
|
||||||
if unit.bearer != nil {
|
|
||||||
table = 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, ErrEACLNotFound) {
|
|
||||||
return ActionAllow
|
|
||||||
}
|
|
||||||
|
|
||||||
v.logger.Error("could not get eACL table",
|
|
||||||
zap.String("error", err.Error()),
|
|
||||||
)
|
|
||||||
|
|
||||||
return ActionUnknown
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return tableAction(unit, table)
|
|
||||||
}
|
|
||||||
|
|
||||||
// tableAction calculates action on the request based on the eACL rules.
|
|
||||||
func tableAction(unit *ValidationUnit, table *Table) Action {
|
|
||||||
for _, record := range table.Records() {
|
|
||||||
// check type of operation
|
// check type of operation
|
||||||
if record.Operation() != unit.op {
|
if record.Operation() != unit.op {
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -4,9 +4,7 @@ import (
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"go.uber.org/zap/zaptest"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestFilterMatch(t *testing.T) {
|
func TestFilterMatch(t *testing.T) {
|
||||||
|
@ -26,8 +24,8 @@ func TestFilterMatch(t *testing.T) {
|
||||||
|
|
||||||
tb.AddRecord(newRecord(ActionAllow, OperationUnknown, tgt))
|
tb.AddRecord(newRecord(ActionAllow, OperationUnknown, tgt))
|
||||||
|
|
||||||
v := newValidator(t, tb)
|
v := NewValidator()
|
||||||
vu := newValidationUnit(RoleOthers, nil)
|
vu := newValidationUnit(RoleOthers, nil, tb)
|
||||||
hs := headers{}
|
hs := headers{}
|
||||||
vu.hdrSrc = &hs
|
vu.hdrSrc = &hs
|
||||||
|
|
||||||
|
@ -55,8 +53,8 @@ func TestFilterMatch(t *testing.T) {
|
||||||
tb.AddRecord(r)
|
tb.AddRecord(r)
|
||||||
tb.AddRecord(newRecord(ActionAllow, OperationUnknown, tgt))
|
tb.AddRecord(newRecord(ActionAllow, OperationUnknown, tgt))
|
||||||
|
|
||||||
v := newValidator(t, tb)
|
v := NewValidator()
|
||||||
vu := newValidationUnit(RoleOthers, nil)
|
vu := newValidationUnit(RoleOthers, nil, tb)
|
||||||
hs := headers{}
|
hs := headers{}
|
||||||
vu.hdrSrc = &hs
|
vu.hdrSrc = &hs
|
||||||
|
|
||||||
|
@ -82,8 +80,8 @@ func TestFilterMatch(t *testing.T) {
|
||||||
|
|
||||||
tb.AddRecord(newRecord(ActionDeny, OperationUnknown, tgt))
|
tb.AddRecord(newRecord(ActionDeny, OperationUnknown, tgt))
|
||||||
|
|
||||||
v := newValidator(t, tb)
|
v := NewValidator()
|
||||||
vu := newValidationUnit(RoleOthers, nil)
|
vu := newValidationUnit(RoleOthers, nil, tb)
|
||||||
hs := headers{}
|
hs := headers{}
|
||||||
vu.hdrSrc = &hs
|
vu.hdrSrc = &hs
|
||||||
|
|
||||||
|
@ -104,8 +102,8 @@ func TestFilterMatch(t *testing.T) {
|
||||||
tb.AddRecord(r)
|
tb.AddRecord(r)
|
||||||
tb.AddRecord(newRecord(ActionDeny, OperationUnknown, tgt))
|
tb.AddRecord(newRecord(ActionDeny, OperationUnknown, tgt))
|
||||||
|
|
||||||
v := newValidator(t, tb)
|
v := NewValidator()
|
||||||
vu := newValidationUnit(RoleOthers, nil)
|
vu := newValidationUnit(RoleOthers, nil, tb)
|
||||||
hs := headers{}
|
hs := headers{}
|
||||||
vu.hdrSrc = &hs
|
vu.hdrSrc = &hs
|
||||||
|
|
||||||
|
@ -125,8 +123,8 @@ func TestOperationMatch(t *testing.T) {
|
||||||
tb.AddRecord(newRecord(ActionDeny, OperationPut, tgt))
|
tb.AddRecord(newRecord(ActionDeny, OperationPut, tgt))
|
||||||
tb.AddRecord(newRecord(ActionAllow, OperationGet, tgt))
|
tb.AddRecord(newRecord(ActionAllow, OperationGet, tgt))
|
||||||
|
|
||||||
v := newValidator(t, tb)
|
v := NewValidator()
|
||||||
vu := newValidationUnit(RoleOthers, nil)
|
vu := newValidationUnit(RoleOthers, nil, tb)
|
||||||
|
|
||||||
vu.op = OperationPut
|
vu.op = OperationPut
|
||||||
require.Equal(t, ActionDeny, v.CalculateAction(vu))
|
require.Equal(t, ActionDeny, v.CalculateAction(vu))
|
||||||
|
@ -140,8 +138,8 @@ func TestOperationMatch(t *testing.T) {
|
||||||
tb.AddRecord(newRecord(ActionDeny, OperationUnknown, tgt))
|
tb.AddRecord(newRecord(ActionDeny, OperationUnknown, tgt))
|
||||||
tb.AddRecord(newRecord(ActionAllow, OperationGet, tgt))
|
tb.AddRecord(newRecord(ActionAllow, OperationGet, tgt))
|
||||||
|
|
||||||
v := newValidator(t, tb)
|
v := NewValidator()
|
||||||
vu := newValidationUnit(RoleOthers, nil)
|
vu := newValidationUnit(RoleOthers, nil, tb)
|
||||||
|
|
||||||
// TODO discuss if both next tests should result in DENY
|
// TODO discuss if both next tests should result in DENY
|
||||||
vu.op = OperationPut
|
vu.op = OperationPut
|
||||||
|
@ -165,19 +163,19 @@ func TestTargetMatches(t *testing.T) {
|
||||||
r := NewRecord()
|
r := NewRecord()
|
||||||
r.SetTargets(tgt1, tgt2)
|
r.SetTargets(tgt1, tgt2)
|
||||||
|
|
||||||
u := newValidationUnit(RoleUser, pubs[0])
|
u := newValidationUnit(RoleUser, pubs[0], nil)
|
||||||
require.True(t, targetMatches(u, r))
|
require.True(t, targetMatches(u, r))
|
||||||
|
|
||||||
u = newValidationUnit(RoleUser, pubs[2])
|
u = newValidationUnit(RoleUser, pubs[2], nil)
|
||||||
require.False(t, targetMatches(u, r))
|
require.False(t, targetMatches(u, r))
|
||||||
|
|
||||||
u = newValidationUnit(RoleUnknown, pubs[1])
|
u = newValidationUnit(RoleUnknown, pubs[1], nil)
|
||||||
require.True(t, targetMatches(u, r))
|
require.True(t, targetMatches(u, r))
|
||||||
|
|
||||||
u = newValidationUnit(RoleOthers, pubs[2])
|
u = newValidationUnit(RoleOthers, pubs[2], nil)
|
||||||
require.True(t, targetMatches(u, r))
|
require.True(t, targetMatches(u, r))
|
||||||
|
|
||||||
u = newValidationUnit(RoleSystem, pubs[2])
|
u = newValidationUnit(RoleSystem, pubs[2], nil)
|
||||||
require.False(t, targetMatches(u, r))
|
require.False(t, targetMatches(u, r))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -234,23 +232,9 @@ func newRecord(a Action, op Operation, tgt ...*Target) *Record {
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
type dummySource struct {
|
func newValidationUnit(role Role, key []byte, table *Table) *ValidationUnit {
|
||||||
tb *Table
|
return new(ValidationUnit).
|
||||||
}
|
WithRole(role).
|
||||||
|
WithSenderKey(key).
|
||||||
func (d dummySource) GetEACL(*cid.ID) (*Table, error) {
|
WithEACLTable(table)
|
||||||
return d.tb, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func newValidator(t *testing.T, tb *Table) *Validator {
|
|
||||||
return NewValidator(
|
|
||||||
WithLogger(zaptest.NewLogger(t)),
|
|
||||||
WithEACLSource(dummySource{tb}))
|
|
||||||
}
|
|
||||||
|
|
||||||
func newValidationUnit(role Role, key []byte) *ValidationUnit {
|
|
||||||
return &ValidationUnit{
|
|
||||||
role: role,
|
|
||||||
key: key,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue