diff --git a/pkg/acl/eacl/enums.go b/pkg/acl/eacl/enums.go index f861d66..19fa5b9 100644 --- a/pkg/acl/eacl/enums.go +++ b/pkg/acl/eacl/enums.go @@ -5,60 +5,100 @@ import ( ) // Action taken if EACL record matched request. +// Action is compatible with v2 acl.Action enum. type Action uint32 const ( + // ActionUnknown is an Action value used to mark action as undefined. ActionUnknown Action = iota + + // ActionAllow is an Action value that allows access to the operation from context. ActionAllow + + // ActionDeny is an Action value that denies access to the operation from context. ActionDeny ) // Operation is a object service method to match request. +// Operation is compatible with v2 acl.Operation enum. type Operation uint32 const ( + // OperationUnknown is an Operation value used to mark operation as undefined. OperationUnknown Operation = iota + + // OperationGet is an object get Operation. OperationGet + + // OperationHead is an Operation of getting the object header. OperationHead + + // OperationPut is an object put Operation. OperationPut + + // OperationDelete is an object delete Operation. OperationDelete + + // OperationSearch is an object search Operation. OperationSearch + + // OperationRange is an object payload range retrieval Operation. OperationRange + + // OperationRangeHash is an object payload range hashing Operation. OperationRangeHash ) // Role is a group of request senders to match request. +// Role is compatible with v2 acl.Role enum. type Role uint32 const ( + // RoleUnknown is a Role value used to mark role as undefined. RoleUnknown Role = iota + // RoleUser is a group of senders that contains only key of container owner. RoleUser + // RoleSystem is a group of senders that contains keys of container nodes and // inner ring nodes. RoleSystem + // RoleOthers is a group of senders that contains none of above keys. RoleOthers ) // Match is binary operation on filer name and value to check if request is matched. +// Match is compatible with v2 acl.MatchType enum. type Match uint32 const ( + // MatchUnknown is a Match value used to mark matcher as undefined. MatchUnknown Match = iota + + // MatchStringEqual is a Match of string equality. MatchStringEqual + + // MatchStringNotEqual is a Match of string inequality. MatchStringNotEqual ) // FilterHeaderType indicates source of headers to make matches. +// FilterHeaderType is compatible with v2 acl.HeaderType enum. type FilterHeaderType uint32 const ( + // HeaderTypeUnknown is a FilterHeaderType value used to mark header type as undefined. HeaderTypeUnknown FilterHeaderType = iota + + // HeaderFromRequest is a FilterHeaderType for request X-Header. HeaderFromRequest + + // HeaderFromObject is a FilterHeaderType for object header. HeaderFromObject ) +// ToV2 converts Action to v2 Action enum value. func (a Action) ToV2() v2acl.Action { switch a { case ActionAllow: @@ -70,6 +110,7 @@ func (a Action) ToV2() v2acl.Action { } } +// ActionFromV2 converts v2 Action enum value to Action. func ActionFromV2(action v2acl.Action) (a Action) { switch action { case v2acl.ActionAllow: @@ -83,6 +124,7 @@ func ActionFromV2(action v2acl.Action) (a Action) { return a } +// ToV2 converts Operation to v2 Operation enum value. func (o Operation) ToV2() v2acl.Operation { switch o { case OperationGet: @@ -104,6 +146,7 @@ func (o Operation) ToV2() v2acl.Operation { } } +// OperationFromV2 converts v2 Operation enum value to Operation. func OperationFromV2(operation v2acl.Operation) (o Operation) { switch operation { case v2acl.OperationGet: @@ -127,6 +170,7 @@ func OperationFromV2(operation v2acl.Operation) (o Operation) { return o } +// ToV2 converts Role to v2 Role enum value. func (r Role) ToV2() v2acl.Role { switch r { case RoleUser: @@ -140,6 +184,7 @@ func (r Role) ToV2() v2acl.Role { } } +// RoleFromV2 converts v2 Role enum value to Role. func RoleFromV2(role v2acl.Role) (r Role) { switch role { case v2acl.RoleUser: @@ -155,6 +200,7 @@ func RoleFromV2(role v2acl.Role) (r Role) { return r } +// ToV2 converts Match to v2 MatchType enum value. func (m Match) ToV2() v2acl.MatchType { switch m { case MatchStringEqual: @@ -166,6 +212,7 @@ func (m Match) ToV2() v2acl.MatchType { } } +// MatchFromV2 converts v2 MatchType enum value to Match. func MatchFromV2(match v2acl.MatchType) (m Match) { switch match { case v2acl.MatchTypeStringEqual: @@ -179,6 +226,7 @@ func MatchFromV2(match v2acl.MatchType) (m Match) { return m } +// ToV2 converts FilterHeaderType to v2 HeaderType enum value. func (h FilterHeaderType) ToV2() v2acl.HeaderType { switch h { case HeaderFromRequest: @@ -190,6 +238,7 @@ func (h FilterHeaderType) ToV2() v2acl.HeaderType { } } +// FilterHeaderTypeFromV2 converts v2 HeaderType enum value to FilterHeaderType. func FilterHeaderTypeFromV2(header v2acl.HeaderType) (h FilterHeaderType) { switch header { case v2acl.HeaderTypeRequest: diff --git a/pkg/acl/eacl/filter.go b/pkg/acl/eacl/filter.go index efed7ef..d66978b 100644 --- a/pkg/acl/eacl/filter.go +++ b/pkg/acl/eacl/filter.go @@ -8,10 +8,12 @@ import ( // Filter defines check conditions if request header is matched or not. Matched // header means that request should be processed according to EACL action. +// +// Filter is compatible with v2 acl.EACLRecord.Filter message. type Filter struct { from FilterHeaderType - key filterKey matcher Match + key filterKey value fmt.Stringer } @@ -43,22 +45,27 @@ func (s staticStringer) String() string { return string(s) } +// Value returns filtered string value. func (f Filter) Value() string { return f.value.String() } +// Matcher returns filter Match type. func (f Filter) Matcher() Match { return f.matcher } +// Key returns key to the filtered header. func (f Filter) Key() string { return f.key.String() } +// From returns FilterHeaderType that defined which header will be filtered. func (f Filter) From() FilterHeaderType { return f.from } +// ToV2 converts Filter to v2 acl.EACLRecord.Filter message. func (f *Filter) ToV2() *v2acl.HeaderFilter { filter := new(v2acl.HeaderFilter) filter.SetValue(f.value.String()) @@ -94,10 +101,12 @@ func (k filterKey) String() string { } } +// NewFilter creates, initializes and returns blank Filter instance. func NewFilter() *Filter { return NewFilterFromV2(new(v2acl.HeaderFilter)) } +// NewFilterFromV2 converts v2 acl.EACLRecord.Filter message to Filter. func NewFilterFromV2(filter *v2acl.HeaderFilter) *Filter { f := new(Filter) diff --git a/pkg/acl/eacl/record.go b/pkg/acl/eacl/record.go index db077aa..dd4abbf 100644 --- a/pkg/acl/eacl/record.go +++ b/pkg/acl/eacl/record.go @@ -10,43 +10,50 @@ import ( v2acl "github.com/nspcc-dev/neofs-api-go/v2/acl" ) -type ( - // Record of the EACL rule, that defines EACL action, targets for this action, - // object service operation and filters for request headers. - Record struct { - action Action - operation Operation - filters []Filter - targets []Target - } -) +// Record of the EACL rule, that defines EACL action, targets for this action, +// object service operation and filters for request headers. +// +// Record is compatible with v2 acl.EACLRecord message. +type Record struct { + action Action + operation Operation + filters []*Filter + targets []*Target +} -func (r Record) Targets() []Target { +// Targets returns list of target subjects to apply ACL rule to. +func (r Record) Targets() []*Target { return r.targets } -func (r Record) Filters() []Filter { +// Filters returns list of filters to match and see if rule is applicable. +func (r Record) Filters() []*Filter { return r.filters } +// Operation returns NeoFS request verb to match. func (r Record) Operation() Operation { return r.operation } +// SetOperation sets NeoFS request verb to match. func (r *Record) SetOperation(operation Operation) { r.operation = operation } +// Action returns rule execution result. func (r Record) Action() Action { return r.action } +// SetAction sets rule execution result. func (r *Record) SetAction(action Action) { r.action = action } +// AddTarget adds target subject with specified Role and key list. func (r *Record) AddTarget(role Role, keys ...ecdsa.PublicKey) { - t := Target{ + t := &Target{ role: role, keys: make([]ecdsa.PublicKey, 0, len(keys)), } @@ -59,7 +66,7 @@ func (r *Record) AddTarget(role Role, keys ...ecdsa.PublicKey) { } func (r *Record) addFilter(from FilterHeaderType, m Match, keyTyp filterKeyType, key string, val fmt.Stringer) { - filter := Filter{ + filter := &Filter{ from: from, key: filterKey{ typ: keyTyp, @@ -80,28 +87,34 @@ func (r *Record) addObjectReservedFilter(m Match, typ filterKeyType, val fmt.Str r.addObjectFilter(m, typ, "", val) } +// AddFilter adds generic filter. func (r *Record) AddFilter(from FilterHeaderType, matcher Match, name, value string) { r.addFilter(from, matcher, 0, name, staticStringer(value)) } +// AddObjectAttributeFilter adds filter by object attribute. func (r *Record) AddObjectAttributeFilter(m Match, key, value string) { r.addObjectFilter(m, 0, key, staticStringer(value)) } +// AddObjectVersionFilter adds filter by object version. func (r *Record) AddObjectVersionFilter(m Match, v *pkg.Version) { r.addObjectReservedFilter(m, fKeyObjVersion, v) } +// AddObjectContainerIDFilter adds filter by object container ID. func (r *Record) AddObjectContainerIDFilter(m Match, id *container.ID) { r.addObjectReservedFilter(m, fKeyObjContainerID, id) } +// AddObjectOwnerIDFilter adds filter by object owner ID. func (r *Record) AddObjectOwnerIDFilter(m Match, id *owner.ID) { r.addObjectReservedFilter(m, fKeyObjOwnerID, id) } // TODO: add remaining filters after neofs-api#72 +// ToV2 converts Record to v2 acl.EACLRecord message. func (r *Record) ToV2() *v2acl.Record { targets := make([]*v2acl.Target, 0, len(r.targets)) for _, target := range r.targets { @@ -123,20 +136,23 @@ func (r *Record) ToV2() *v2acl.Record { return v2 } +// NewRecord creates and returns blank Record instance. func NewRecord() *Record { return new(Record) } +// CreateRecord creates, initializes with parameters and returns Record instance. func CreateRecord(action Action, operation Operation) *Record { r := NewRecord() r.action = action r.operation = operation - r.targets = []Target{} - r.filters = []Filter{} + r.targets = []*Target{} + r.filters = []*Filter{} return r } +// NewRecordFromV2 converts v2 acl.EACLRecord message to Record. func NewRecordFromV2(record *v2acl.Record) *Record { r := NewRecord() @@ -150,14 +166,14 @@ func NewRecordFromV2(record *v2acl.Record) *Record { v2targets := record.GetTargets() v2filters := record.GetFilters() - r.targets = make([]Target, 0, len(v2targets)) + r.targets = make([]*Target, 0, len(v2targets)) for i := range v2targets { - r.targets = append(r.targets, *NewTargetFromV2(v2targets[i])) + r.targets = append(r.targets, NewTargetFromV2(v2targets[i])) } - r.filters = make([]Filter, 0, len(v2filters)) + r.filters = make([]*Filter, 0, len(v2filters)) for i := range v2filters { - r.filters = append(r.filters, *NewFilterFromV2(v2filters[i])) + r.filters = append(r.filters, NewFilterFromV2(v2filters[i])) } return r diff --git a/pkg/acl/eacl/record_test.go b/pkg/acl/eacl/record_test.go index fef691d..87b5345 100644 --- a/pkg/acl/eacl/record_test.go +++ b/pkg/acl/eacl/record_test.go @@ -39,7 +39,7 @@ func TestRecord(t *testing.T) { } func TestRecord_AddTarget(t *testing.T) { - targets := []Target{ + targets := []*Target{ { role: RoleUnknown, keys: []ecdsa.PublicKey{test.DecodeKey(1).PublicKey}, @@ -59,9 +59,9 @@ func TestRecord_AddTarget(t *testing.T) { } func TestRecord_AddFilter(t *testing.T) { - filters := []Filter{ - *newObjectFilter(MatchStringEqual, "some name", "ContainerID"), - *newObjectFilter(MatchStringNotEqual, "X-Header-Name", "X-Header-Value"), + filters := []*Filter{ + newObjectFilter(MatchStringEqual, "some name", "ContainerID"), + newObjectFilter(MatchStringNotEqual, "X-Header-Name", "X-Header-Value"), } r := NewRecord() diff --git a/pkg/acl/eacl/table.go b/pkg/acl/eacl/table.go index ff14093..606d5b2 100644 --- a/pkg/acl/eacl/table.go +++ b/pkg/acl/eacl/table.go @@ -8,41 +8,48 @@ import ( v2acl "github.com/nspcc-dev/neofs-api-go/v2/acl" ) -type ( - // Table is a group of EACL records for single container. - Table struct { - version pkg.Version - cid *container.ID - records []Record - } -) +// Table is a group of EACL records for single container. +// +// Table is compatible with v2 acl.EACLTable message. +type Table struct { + version pkg.Version + cid *container.ID + records []*Record +} +// CID returns identifier of the container that should use given access control rules. func (t Table) CID() *container.ID { return t.cid } +// SetCID sets identifier of the container that should use given access control rules. func (t *Table) SetCID(cid *container.ID) { t.cid = cid } +// Version returns version of eACL format. func (t Table) Version() pkg.Version { return t.version } +// SetVersion sets version of eACL format. func (t *Table) SetVersion(version pkg.Version) { t.version = version } -func (t Table) Records() []Record { +// Records returns list of extended ACL rules. +func (t Table) Records() []*Record { return t.records } +// AddRecord adds single eACL rule. func (t *Table) AddRecord(r *Record) { if r != nil { - t.records = append(t.records, *r) + t.records = append(t.records, r) } } +// ToV2 converts Table to v2 acl.EACLTable message. func (t *Table) ToV2() *v2acl.Table { v2 := new(v2acl.Table) @@ -61,6 +68,7 @@ func (t *Table) ToV2() *v2acl.Table { return v2 } +// NewTable creates, initializes and returns blank Table instance. func NewTable() *Table { t := new(Table) t.SetVersion(*pkg.SDKVersion()) @@ -68,6 +76,7 @@ func NewTable() *Table { return t } +// CreateTable creates, initializes with parameters and returns Table instance. func CreateTable(cid container.ID) *Table { t := NewTable() t.SetCID(&cid) @@ -75,6 +84,7 @@ func CreateTable(cid container.ID) *Table { return t } +// NewTableFromV2 converts v2 acl.EACLTable message to Table. func NewTableFromV2(table *v2acl.Table) *Table { t := new(Table) @@ -98,15 +108,17 @@ func NewTableFromV2(table *v2acl.Table) *Table { } var h [sha256.Size]byte + copy(h[:], table.GetContainerID().GetValue()) t.cid.SetSHA256(h) } // set eacl records v2records := table.GetRecords() - t.records = make([]Record, 0, len(v2records)) + t.records = make([]*Record, 0, len(v2records)) + for i := range v2records { - t.records = append(t.records, *NewRecordFromV2(v2records[i])) + t.records = append(t.records, NewRecordFromV2(v2records[i])) } return t diff --git a/pkg/acl/eacl/table_test.go b/pkg/acl/eacl/table_test.go index cdf7a6a..7092aea 100644 --- a/pkg/acl/eacl/table_test.go +++ b/pkg/acl/eacl/table_test.go @@ -7,23 +7,9 @@ import ( "github.com/nspcc-dev/neofs-api-go/pkg" "github.com/nspcc-dev/neofs-api-go/pkg/acl/eacl" "github.com/nspcc-dev/neofs-api-go/pkg/container" - "github.com/nspcc-dev/neofs-crypto/test" "github.com/stretchr/testify/require" ) -// example how to create eACL tables in applications -func example() { - record := eacl.CreateRecord(eacl.ActionDeny, eacl.OperationPut) - record.AddFilter(eacl.HeaderFromObject, eacl.MatchStringEqual, "filename", "cat.jpg") - record.AddTarget(eacl.RoleOthers, test.DecodeKey(1).PublicKey, test.DecodeKey(2).PublicKey) - - var cid container.ID - cid.SetSHA256(sha256.Sum256([]byte("container id"))) - - table := eacl.CreateTable(cid) - table.AddRecord(record) -} - func TestTable(t *testing.T) { var ( v pkg.Version @@ -66,14 +52,14 @@ func TestTable(t *testing.T) { } func TestTable_AddRecord(t *testing.T) { - records := []eacl.Record{ - *eacl.CreateRecord(eacl.ActionDeny, eacl.OperationDelete), - *eacl.CreateRecord(eacl.ActionAllow, eacl.OperationPut), + records := []*eacl.Record{ + eacl.CreateRecord(eacl.ActionDeny, eacl.OperationDelete), + eacl.CreateRecord(eacl.ActionAllow, eacl.OperationPut), } table := eacl.NewTable() for _, record := range records { - table.AddRecord(&record) + table.AddRecord(record) } require.Equal(t, records, table.Records()) diff --git a/pkg/acl/eacl/target.go b/pkg/acl/eacl/target.go index ca6af2e..0d15bda 100644 --- a/pkg/acl/eacl/target.go +++ b/pkg/acl/eacl/target.go @@ -9,27 +9,34 @@ import ( // Target is a group of request senders to match EACL. Defined by role enum // and set of public keys. +// +// Target is compatible with v2 acl.EACLRecord.Target message. type Target struct { role Role keys []ecdsa.PublicKey } +// SetKeys sets list of public keys to identify target subject. func (t *Target) SetKeys(keys ...ecdsa.PublicKey) { t.keys = keys } +// Keys returns list of public keys to identify target subject. func (t Target) Keys() []ecdsa.PublicKey { return t.keys } +// SetRole sets target subject's role class. func (t *Target) SetRole(r Role) { t.role = r } +// Role returns target subject's role class. func (t Target) Role() Role { return t.role } +// ToV2 converts Target to v2 acl.EACLRecord.Target message. func (t *Target) ToV2() *v2acl.Target { keys := make([][]byte, 0, len(t.keys)) for i := range t.keys { @@ -45,10 +52,12 @@ func (t *Target) ToV2() *v2acl.Target { return target } +// NewTarget creates, initializes and returns blank Target instance. func NewTarget() *Target { return NewTargetFromV2(new(v2acl.Target)) } +// NewTargetFromV2 converts v2 acl.EACLRecord.Target message to Target. func NewTargetFromV2(target *v2acl.Target) *Target { t := new(Target) @@ -59,6 +68,7 @@ func NewTargetFromV2(target *v2acl.Target) *Target { t.role = RoleFromV2(target.GetRole()) v2keys := target.GetKeys() t.keys = make([]ecdsa.PublicKey, 0, len(v2keys)) + for i := range v2keys { key := crypto.UnmarshalPublicKey(v2keys[i]) t.keys = append(t.keys, *key)