[#217] pkg/eacl: Change interface for working with keys on Target and Record

In previous implementation Target provided Keys/SetKeys methods which
allowed working with ECDSA keys. There was also a bug in the NewTargetFromV2
function when the binary key differed in format from the ECDSA key. New
BinaryKeys/SetBinaryKeys methods work with binary keys. To work with ECDSA
keys added functions TargetECDSAKeys/SetTargetECDSAKeys. Old methods are
left and marked deprecated.

Type Record provided an interface for adding a Target by Role and a list of
ECDSA keys. New SetTargets method allows to set the list of Target's,
AddTarget function allows to add a single Target. AddFormedTarget works like
old AddTarget method, which is now deprecated.

Signed-off-by: Leonard Lyubich <leonard@nspcc.ru>
This commit is contained in:
Leonard Lyubich 2020-12-01 09:39:35 +03:00 committed by Alex Vanin
parent 2a94fdc5e7
commit c01024b553
4 changed files with 134 additions and 43 deletions

View file

@ -26,6 +26,11 @@ func (r Record) Targets() []*Target {
return r.targets return r.targets
} }
// SetTargets sets list of target subjects to apply ACL rule to.
func (r *Record) SetTargets(targets ...*Target) {
r.targets = targets
}
// Filters returns list of filters to match and see if rule is applicable. // Filters returns list of filters to match and see if rule is applicable.
func (r Record) Filters() []*Filter { func (r Record) Filters() []*Filter {
return r.filters return r.filters
@ -52,17 +57,25 @@ func (r *Record) SetAction(action Action) {
} }
// AddTarget adds target subject with specified Role and key list. // AddTarget adds target subject with specified Role and key list.
//
// Deprecated: use AddFormedTarget instead.
func (r *Record) AddTarget(role Role, keys ...ecdsa.PublicKey) { func (r *Record) AddTarget(role Role, keys ...ecdsa.PublicKey) {
t := &Target{ AddFormedTarget(r, role, keys...)
role: role,
keys: make([]ecdsa.PublicKey, 0, len(keys)),
} }
for i := range keys { // AddRecordTarget adds single Target to the Record.
t.keys = append(t.keys, keys[i]) func AddRecordTarget(r *Record, t *Target) {
r.SetTargets(append(r.Targets(), t)...)
} }
r.targets = append(r.targets, t) // AddFormedTarget forms Target with specified Role and list of
// ECDSA public keys and adds it to the Record.
func AddFormedTarget(r *Record, role Role, keys ...ecdsa.PublicKey) {
t := NewTarget()
t.SetRole(role)
SetTargetECDSAKeys(t, ecdsaKeysToPtrs(keys)...)
AddRecordTarget(r, t)
} }
func (r *Record) addFilter(from FilterHeaderType, m Match, keyTyp filterKeyType, key string, val fmt.Stringer) { func (r *Record) addFilter(from FilterHeaderType, m Match, keyTyp filterKeyType, key string, val fmt.Stringer) {

View file

@ -15,7 +15,10 @@ func TestRecord(t *testing.T) {
record.SetAction(ActionAllow) record.SetAction(ActionAllow)
record.AddFilter(HeaderFromRequest, MatchStringEqual, "A", "B") record.AddFilter(HeaderFromRequest, MatchStringEqual, "A", "B")
record.AddFilter(HeaderFromRequest, MatchStringNotEqual, "C", "D") record.AddFilter(HeaderFromRequest, MatchStringNotEqual, "C", "D")
record.AddTarget(RoleSystem)
target := NewTarget()
target.SetRole(RoleSystem)
AddRecordTarget(record, target)
v2 := record.ToV2() v2 := record.ToV2()
require.NotNil(t, v2) require.NotNil(t, v2)
@ -38,8 +41,11 @@ func TestRecord(t *testing.T) {
}) })
} }
func TestRecord_AddTarget(t *testing.T) { func TestAddFormedTarget(t *testing.T) {
targets := []*Target{ items := []struct {
role Role
keys []ecdsa.PublicKey
}{
{ {
role: RoleUnknown, role: RoleUnknown,
keys: []ecdsa.PublicKey{test.DecodeKey(1).PublicKey}, keys: []ecdsa.PublicKey{test.DecodeKey(1).PublicKey},
@ -50,12 +56,26 @@ func TestRecord_AddTarget(t *testing.T) {
}, },
} }
targets := make([]*Target, 0, len(items))
r := NewRecord() r := NewRecord()
for _, target := range targets {
r.AddTarget(target.Role(), target.Keys()...) for _, item := range items {
tgt := NewTarget()
tgt.SetRole(item.role)
SetTargetECDSAKeys(tgt, ecdsaKeysToPtrs(item.keys)...)
targets = append(targets, tgt)
AddFormedTarget(r, item.role, item.keys...)
} }
require.Equal(t, targets, r.Targets()) tgts := r.Targets()
require.Len(t, tgts, len(targets))
for _, tgt := range targets {
require.Contains(t, tgts, tgt)
}
} }
func TestRecord_AddFilter(t *testing.T) { func TestRecord_AddFilter(t *testing.T) {
@ -77,7 +97,7 @@ func TestRecordEncoding(t *testing.T) {
r.SetOperation(OperationHead) r.SetOperation(OperationHead)
r.SetAction(ActionDeny) r.SetAction(ActionDeny)
r.AddObjectAttributeFilter(MatchStringEqual, "key", "value") r.AddObjectAttributeFilter(MatchStringEqual, "key", "value")
r.AddTarget(RoleSystem, test.DecodeKey(-1).PublicKey) AddFormedTarget(r, RoleSystem, test.DecodeKey(-1).PublicKey)
t.Run("binary", func(t *testing.T) { t.Run("binary", func(t *testing.T) {
data, err := r.Marshal() data, err := r.Marshal()

View file

@ -13,17 +13,88 @@ import (
// Target is compatible with v2 acl.EACLRecord.Target message. // Target is compatible with v2 acl.EACLRecord.Target message.
type Target struct { type Target struct {
role Role role Role
keys []ecdsa.PublicKey keys [][]byte
} }
// SetKeys sets list of public keys to identify target subject. func ecdsaKeysToPtrs(keys []ecdsa.PublicKey) []*ecdsa.PublicKey {
keysPtr := make([]*ecdsa.PublicKey, len(keys))
for i := range keys {
keysPtr[i] = &keys[i]
}
return keysPtr
}
// SetKeys sets list of ECDSA public keys to identify target subject.
//
// Deprecated: use SetTargetECDSAKeys instead.
func (t *Target) SetKeys(keys ...ecdsa.PublicKey) { func (t *Target) SetKeys(keys ...ecdsa.PublicKey) {
SetTargetECDSAKeys(t, ecdsaKeysToPtrs(keys)...)
}
// Keys returns list of ECDSA public keys to identify target subject.
// If some key has a different format, it is ignored.
//
// Deprecated: use TargetECDSAKeys instead.
func (t *Target) Keys() []ecdsa.PublicKey {
keysPtr := TargetECDSAKeys(t)
keys := make([]ecdsa.PublicKey, 0, len(keysPtr))
for i := range keysPtr {
if keysPtr[i] != nil {
keys = append(keys, *keysPtr[i])
}
}
return keys
}
// BinaryKeys returns list of public keys to identify
// target subject in a binary format.
func (t *Target) BinaryKeys() [][]byte {
return t.keys
}
// SetBinaryKeys sets list of binary public keys to identify
// target subject.
func (t *Target) SetBinaryKeys(keys [][]byte) {
t.keys = keys t.keys = keys
} }
// Keys returns list of public keys to identify target subject. // SetTargetECDSAKeys converts ECDSA public keys to a binary
func (t Target) Keys() []ecdsa.PublicKey { // format and stores them in Target.
return t.keys func SetTargetECDSAKeys(t *Target, keys ...*ecdsa.PublicKey) {
binKeys := t.BinaryKeys()
ln := len(keys)
if cap(binKeys) >= ln {
binKeys = binKeys[:0]
} else {
binKeys = make([][]byte, 0, ln)
}
for i := 0; i < ln; i++ {
binKeys = append(binKeys, crypto.MarshalPublicKey(keys[i]))
}
t.SetBinaryKeys(binKeys)
}
// TargetECDSAKeys interprets binary public keys of Target
// as ECDSA public keys. If any key has a different format,
// the corresponding element will be nil.
func TargetECDSAKeys(t *Target) []*ecdsa.PublicKey {
binKeys := t.BinaryKeys()
ln := len(binKeys)
keys := make([]*ecdsa.PublicKey, ln)
for i := 0; i < ln; i++ {
keys[i] = crypto.UnmarshalPublicKey(binKeys[i])
}
return keys
} }
// SetRole sets target subject's role class. // SetRole sets target subject's role class.
@ -38,16 +109,10 @@ func (t Target) Role() Role {
// ToV2 converts Target to v2 acl.EACLRecord.Target message. // ToV2 converts Target to v2 acl.EACLRecord.Target message.
func (t *Target) ToV2() *v2acl.Target { func (t *Target) ToV2() *v2acl.Target {
keys := make([][]byte, 0, len(t.keys))
for i := range t.keys {
key := crypto.MarshalPublicKey(&t.keys[i])
keys = append(keys, key)
}
target := new(v2acl.Target) target := new(v2acl.Target)
target.SetRole(t.role.ToV2()) target.SetRole(t.role.ToV2())
target.SetKeys(keys) target.SetKeys(t.keys)
return target return target
} }
@ -66,13 +131,7 @@ func NewTargetFromV2(target *v2acl.Target) *Target {
} }
t.role = RoleFromV2(target.GetRole()) t.role = RoleFromV2(target.GetRole())
v2keys := target.GetKeys() t.keys = 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)
}
return t return t
} }

View file

@ -11,22 +11,21 @@ import (
) )
func TestTarget(t *testing.T) { func TestTarget(t *testing.T) {
keys := []ecdsa.PublicKey{ keys := []*ecdsa.PublicKey{
test.DecodeKey(1).PublicKey, &test.DecodeKey(1).PublicKey,
test.DecodeKey(2).PublicKey, &test.DecodeKey(2).PublicKey,
} }
target := &Target{ target := NewTarget()
role: RoleSystem, target.SetRole(RoleSystem)
keys: keys, SetTargetECDSAKeys(target, keys...)
}
v2 := target.ToV2() v2 := target.ToV2()
require.NotNil(t, v2) require.NotNil(t, v2)
require.Equal(t, v2acl.RoleSystem, v2.GetRole()) require.Equal(t, v2acl.RoleSystem, v2.GetRole())
require.Len(t, v2.GetKeys(), len(keys)) require.Len(t, v2.GetKeys(), len(keys))
for i, key := range v2.GetKeys() { for i, key := range v2.GetKeys() {
require.Equal(t, key, crypto.MarshalPublicKey(&keys[i])) require.Equal(t, key, crypto.MarshalPublicKey(keys[i]))
} }
newTarget := NewTargetFromV2(v2) newTarget := NewTargetFromV2(v2)
@ -40,7 +39,7 @@ func TestTarget(t *testing.T) {
func TestTargetEncoding(t *testing.T) { func TestTargetEncoding(t *testing.T) {
tar := NewTarget() tar := NewTarget()
tar.SetRole(RoleSystem) tar.SetRole(RoleSystem)
tar.SetKeys(test.DecodeKey(-1).PublicKey) SetTargetECDSAKeys(tar, &test.DecodeKey(-1).PublicKey)
t.Run("binary", func(t *testing.T) { t.Run("binary", func(t *testing.T) {
data, err := tar.Marshal() data, err := tar.Marshal()