Add first ACL list test and other refactoring
This commit is contained in:
parent
a17b102756
commit
0dcb95fbcd
@ -56,8 +56,9 @@ func newACLStateWithIdentity(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newACLState() *ACLState {
|
func newACLState(decoder keys.Decoder) *ACLState {
|
||||||
return &ACLState{
|
return &ACLState{
|
||||||
|
signingPubKeyDecoder: decoder,
|
||||||
userReadKeys: make(map[uint64]*symmetric.Key),
|
userReadKeys: make(map[uint64]*symmetric.Key),
|
||||||
userStates: make(map[string]*aclpb.ACLChangeUserState),
|
userStates: make(map[string]*aclpb.ACLChangeUserState),
|
||||||
userInvites: make(map[string]*aclpb.ACLChangeUserInvite),
|
userInvites: make(map[string]*aclpb.ACLChangeUserInvite),
|
||||||
|
|||||||
@ -20,8 +20,10 @@ func newACLStateBuilderWithIdentity(decoder keys.Decoder, accountData *account.A
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newACLStateBuilder() *aclStateBuilder {
|
func newACLStateBuilder(decoder keys.Decoder) *aclStateBuilder {
|
||||||
return &aclStateBuilder{}
|
return &aclStateBuilder{
|
||||||
|
decoder: decoder,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sb *aclStateBuilder) Build(records []*Record) (*ACLState, error) {
|
func (sb *aclStateBuilder) Build(records []*Record) (*ACLState, error) {
|
||||||
@ -30,10 +32,10 @@ func (sb *aclStateBuilder) Build(records []*Record) (*ACLState, error) {
|
|||||||
state *ACLState
|
state *ACLState
|
||||||
)
|
)
|
||||||
|
|
||||||
if sb.decoder != nil {
|
if sb.key != nil {
|
||||||
state = newACLStateWithIdentity(sb.identity, sb.key, sb.decoder)
|
state = newACLStateWithIdentity(sb.identity, sb.key, sb.decoder)
|
||||||
} else {
|
} else {
|
||||||
state = newACLState()
|
state = newACLState(sb.decoder)
|
||||||
}
|
}
|
||||||
for _, rec := range records {
|
for _, rec := range records {
|
||||||
err = state.applyChangeAndUpdate(rec)
|
err = state.applyChangeAndUpdate(rec)
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import (
|
|||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/account"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/account"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/storage"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/storage"
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -44,11 +45,24 @@ type aclList struct {
|
|||||||
|
|
||||||
func BuildACLListWithIdentity(acc *account.AccountData, storage storage.ListStorage) (ACLList, error) {
|
func BuildACLListWithIdentity(acc *account.AccountData, storage storage.ListStorage) (ACLList, error) {
|
||||||
builder := newACLStateBuilderWithIdentity(acc.Decoder, acc)
|
builder := newACLStateBuilderWithIdentity(acc.Decoder, acc)
|
||||||
|
return buildWithACLStateBuilder(builder, storage)
|
||||||
|
}
|
||||||
|
|
||||||
|
func BuildACLList(decoder keys.Decoder, storage storage.ListStorage) (ACLList, error) {
|
||||||
|
return buildWithACLStateBuilder(newACLStateBuilder(decoder), storage)
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildWithACLStateBuilder(builder *aclStateBuilder, storage storage.ListStorage) (ACLList, error) {
|
||||||
header, err := storage.Header()
|
header, err := storage.Header()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
id, err := storage.ID()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
rawRecord, err := storage.Head()
|
rawRecord, err := storage.Head()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -94,6 +108,7 @@ func BuildACLListWithIdentity(acc *account.AccountData, storage storage.ListStor
|
|||||||
indexes: indexes,
|
indexes: indexes,
|
||||||
builder: builder,
|
builder: builder,
|
||||||
aclState: state,
|
aclState: state,
|
||||||
|
id: id,
|
||||||
RWMutex: sync.RWMutex{},
|
RWMutex: sync.RWMutex{},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
@ -114,7 +129,7 @@ func (a *aclList) IsAfter(first string, second string) (bool, error) {
|
|||||||
firstRec, okFirst := a.indexes[first]
|
firstRec, okFirst := a.indexes[first]
|
||||||
secondRec, okSecond := a.indexes[second]
|
secondRec, okSecond := a.indexes[second]
|
||||||
if !okFirst || !okSecond {
|
if !okFirst || !okSecond {
|
||||||
return false, fmt.Errorf("not all entries are there: first (%b), second (%b)", okFirst, okSecond)
|
return false, fmt.Errorf("not all entries are there: first (%t), second (%t)", okFirst, okSecond)
|
||||||
}
|
}
|
||||||
return firstRec >= secondRec, nil
|
return firstRec >= secondRec, nil
|
||||||
}
|
}
|
||||||
|
|||||||
46
pkg/acl/list/list_test.go
Normal file
46
pkg/acl/list/list_test.go
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
package list
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb"
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/testutils/acllistbuilder"
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/signingkey"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAclList_ACLState_UserInviteAndJoin(t *testing.T) {
|
||||||
|
st, err := acllistbuilder.NewListStorageWithTestName("userjoinexample.yml")
|
||||||
|
require.NoError(t, err, "building storage should not result in error")
|
||||||
|
|
||||||
|
keychain := st.(*acllistbuilder.ACLListStorageBuilder).GetKeychain()
|
||||||
|
|
||||||
|
aclList, err := BuildACLList(signingkey.NewEDPubKeyDecoder(), st)
|
||||||
|
require.NoError(t, err, "building acl list should be without error")
|
||||||
|
|
||||||
|
idA := keychain.GetIdentity("A")
|
||||||
|
idB := keychain.GetIdentity("B")
|
||||||
|
idC := keychain.GetIdentity("C")
|
||||||
|
|
||||||
|
// checking final state
|
||||||
|
assert.Equal(t, aclList.ACLState().GetUserStates()[idA].Permissions, aclpb.ACLChange_Admin)
|
||||||
|
assert.Equal(t, aclList.ACLState().GetUserStates()[idB].Permissions, aclpb.ACLChange_Writer)
|
||||||
|
assert.Equal(t, aclList.ACLState().GetUserStates()[idC].Permissions, aclpb.ACLChange_Reader)
|
||||||
|
assert.Equal(t, aclList.ACLState().CurrentReadKeyHash(), aclList.Head().Content.CurrentReadKeyHash)
|
||||||
|
var records []*Record
|
||||||
|
aclList.Iterate(func(record *Record) (IsContinue bool) {
|
||||||
|
records = append(records, record)
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
// checking permissions at specific records
|
||||||
|
assert.Equal(t, 3, len(records))
|
||||||
|
_, err = aclList.ACLState().PermissionsAtRecord(records[1].Id, idB)
|
||||||
|
assert.Error(t, err, "B should have no permissions at record 1")
|
||||||
|
perm, err := aclList.ACLState().PermissionsAtRecord(records[2].Id, idB)
|
||||||
|
assert.NoError(t, err, "should have no error with permissions of B in the record 2")
|
||||||
|
assert.Equal(t, perm, UserPermissionPair{
|
||||||
|
Identity: idB,
|
||||||
|
Permission: aclpb.ACLChange_Writer,
|
||||||
|
})
|
||||||
|
}
|
||||||
@ -50,8 +50,8 @@ func (k *Keychain) ParseKeys(keys *Keys) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *Keychain) AddEncryptionKey(name string) {
|
func (k *Keychain) AddEncryptionKey(key *Key) {
|
||||||
if _, exists := k.EncryptionKeys[name]; exists {
|
if _, exists := k.EncryptionKeys[key.Name]; exists {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
newPrivKey, _, err := encryptionkey.GenerateRandomRSAKeyPair(2048)
|
newPrivKey, _, err := encryptionkey.GenerateRandomRSAKeyPair(2048)
|
||||||
@ -59,11 +59,11 @@ func (k *Keychain) AddEncryptionKey(name string) {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
k.EncryptionKeys[name] = newPrivKey
|
k.EncryptionKeys[key.Name] = newPrivKey
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *Keychain) AddSigningKey(name string) {
|
func (k *Keychain) AddSigningKey(key *Key) {
|
||||||
if _, exists := k.SigningKeys[name]; exists {
|
if _, exists := k.SigningKeys[key.Name]; exists {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
newPrivKey, pubKey, err := signingkey.GenerateRandomEd25519KeyPair()
|
newPrivKey, pubKey, err := signingkey.GenerateRandomEd25519KeyPair()
|
||||||
@ -71,48 +71,47 @@ func (k *Keychain) AddSigningKey(name string) {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
k.SigningKeys[name] = newPrivKey
|
k.SigningKeys[key.Name] = newPrivKey
|
||||||
res, err := k.coder.EncodeToString(pubKey)
|
res, err := k.coder.EncodeToString(pubKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
k.SigningKeysByIdentity[res] = newPrivKey
|
k.SigningKeysByIdentity[res] = newPrivKey
|
||||||
k.GeneratedIdentities[name] = res
|
k.GeneratedIdentities[key.Name] = res
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *Keychain) AddReadKey(name string) {
|
func (k *Keychain) AddReadKey(key *Key) {
|
||||||
if _, exists := k.ReadKeys[name]; exists {
|
if _, exists := k.ReadKeys[key.Name]; exists {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
key, _ := symmetric.NewRandom()
|
rkey, _ := symmetric.NewRandom()
|
||||||
|
|
||||||
hasher := fnv.New64()
|
hasher := fnv.New64()
|
||||||
hasher.Write(key.Bytes())
|
hasher.Write(rkey.Bytes())
|
||||||
|
|
||||||
k.ReadKeys[name] = &SymKey{
|
k.ReadKeys[key.Name] = &SymKey{
|
||||||
Hash: hasher.Sum64(),
|
Hash: hasher.Sum64(),
|
||||||
Key: key,
|
Key: rkey,
|
||||||
}
|
}
|
||||||
k.ReadKeysByHash[hasher.Sum64()] = &SymKey{
|
k.ReadKeysByHash[hasher.Sum64()] = &SymKey{
|
||||||
Hash: hasher.Sum64(),
|
Hash: hasher.Sum64(),
|
||||||
Key: key,
|
Key: rkey,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *Keychain) AddKey(key string) {
|
func (k *Keychain) AddKey(key *Key) {
|
||||||
parts := strings.Split(key, ".")
|
parts := strings.Split(key.Name, ".")
|
||||||
if len(parts) != 3 {
|
if len(parts) != 3 {
|
||||||
panic("cannot parse a key")
|
panic("cannot parse a key")
|
||||||
}
|
}
|
||||||
name := parts[2]
|
|
||||||
|
|
||||||
switch parts[1] {
|
switch parts[1] {
|
||||||
case "Sign":
|
case "Sign":
|
||||||
k.AddSigningKey(name)
|
k.AddSigningKey(key)
|
||||||
case "Enc":
|
case "Enc":
|
||||||
k.AddEncryptionKey(name)
|
k.AddEncryptionKey(key)
|
||||||
case "Read":
|
case "Read":
|
||||||
k.AddReadKey(name)
|
k.AddReadKey(key)
|
||||||
default:
|
default:
|
||||||
panic("incorrect format")
|
panic("incorrect format")
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb"
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/storage"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/testutils/yamltests"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/testutils/yamltests"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/cid"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/cid"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/encryptionkey"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/encryptionkey"
|
||||||
@ -34,7 +35,7 @@ func NewACLListStorageBuilder(keychain *Keychain) *ACLListStorageBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewACLListStorageBuilderWithTestName(name string) (*ACLListStorageBuilder, error) {
|
func NewListStorageWithTestName(name string) (storage.ListStorage, error) {
|
||||||
filePath := path.Join(yamltests.Path(), name)
|
filePath := path.Join(yamltests.Path(), name)
|
||||||
return NewACLListStorageBuilderFromFile(filePath)
|
return NewACLListStorageBuilderFromFile(filePath)
|
||||||
}
|
}
|
||||||
@ -89,7 +90,7 @@ func (t *ACLListStorageBuilder) Header() (*aclpb.Header, error) {
|
|||||||
return t.header, nil
|
return t.header, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *ACLListStorageBuilder) GetRecord(ctx context.Context, id string) (*aclpb.RawRecord, error) {
|
func (t *ACLListStorageBuilder) GetRawRecord(ctx context.Context, id string) (*aclpb.RawRecord, error) {
|
||||||
recIdx, ok := t.indexes[id]
|
recIdx, ok := t.indexes[id]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("no such record")
|
return nil, fmt.Errorf("no such record")
|
||||||
@ -97,7 +98,7 @@ func (t *ACLListStorageBuilder) GetRecord(ctx context.Context, id string) (*aclp
|
|||||||
return t.getRecord(recIdx), nil
|
return t.getRecord(recIdx), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *ACLListStorageBuilder) AddRecord(ctx context.Context, rec *aclpb.Record) error {
|
func (t *ACLListStorageBuilder) AddRawRecord(ctx context.Context, rec *aclpb.RawRecord) error {
|
||||||
panic("implement me")
|
panic("implement me")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,9 +1,14 @@
|
|||||||
package acllistbuilder
|
package acllistbuilder
|
||||||
|
|
||||||
|
type Key struct {
|
||||||
|
Name string `yaml:"name"`
|
||||||
|
Value string `yaml:"value"`
|
||||||
|
}
|
||||||
|
|
||||||
type Keys struct {
|
type Keys struct {
|
||||||
Enc []string `yaml:"Enc"`
|
Enc []*Key `yaml:"Enc"`
|
||||||
Sign []string `yaml:"Sign"`
|
Sign []*Key `yaml:"Sign"`
|
||||||
Read []string `yaml:"Read"`
|
Read []*Key `yaml:"Read"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ACLChange struct {
|
type ACLChange struct {
|
||||||
@ -50,7 +55,6 @@ type ACLChange struct {
|
|||||||
type Record struct {
|
type Record struct {
|
||||||
Identity string `yaml:"identity"`
|
Identity string `yaml:"identity"`
|
||||||
AclChanges []*ACLChange `yaml:"aclChanges"`
|
AclChanges []*ACLChange `yaml:"aclChanges"`
|
||||||
|
|
||||||
ReadKey string `yaml:"readKey"`
|
ReadKey string `yaml:"readKey"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func Test_YamlParse(t *testing.T) {
|
func Test_YamlParse(t *testing.T) {
|
||||||
tb, _ := NewACLListStorageBuilderWithTestName("userjoinexampleupdate.yml")
|
tb, _ := NewListStorageWithTestName("userjoinexampleupdate.yml")
|
||||||
gr, _ := tb.Graph()
|
gr, _ := tb.Graph()
|
||||||
fmt.Println(gr)
|
fmt.Println(gr)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,3 @@
|
|||||||
list:
|
|
||||||
author: A
|
|
||||||
records:
|
records:
|
||||||
- identity: A
|
- identity: A
|
||||||
aclChanges:
|
aclChanges:
|
||||||
@ -16,7 +14,7 @@ records:
|
|||||||
encryptionKey: key.Enc.Onetime1
|
encryptionKey: key.Enc.Onetime1
|
||||||
encryptedReadKeys: [key.Read.1]
|
encryptedReadKeys: [key.Read.1]
|
||||||
permissions: writer
|
permissions: writer
|
||||||
inviteIdx: A.1.2
|
inviteId: A.1.2
|
||||||
- userAdd:
|
- userAdd:
|
||||||
identity: C
|
identity: C
|
||||||
permission: reader
|
permission: reader
|
||||||
|
|||||||
@ -77,48 +77,27 @@ type docTree struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BuildDocTreeWithIdentity(t storage.TreeStorage, acc *account.AccountData, listener TreeUpdateListener, aclList list.ACLList) (DocTree, error) {
|
func BuildDocTreeWithIdentity(t storage.TreeStorage, acc *account.AccountData, listener TreeUpdateListener, aclList list.ACLList) (DocTree, error) {
|
||||||
treeBuilder := newTreeBuilder(t, acc.Decoder)
|
return buildDocTreeWithAccount(t, acc, acc.Decoder, listener, aclList)
|
||||||
validator := newTreeValidator()
|
|
||||||
|
|
||||||
docTree := &docTree{
|
|
||||||
treeStorage: t,
|
|
||||||
accountData: acc,
|
|
||||||
tree: nil,
|
|
||||||
treeBuilder: treeBuilder,
|
|
||||||
validator: validator,
|
|
||||||
updateListener: listener,
|
|
||||||
tmpChangesBuf: make([]*Change, 0, 10),
|
|
||||||
difSnapshotBuf: make([]*aclpb.RawChange, 0, 10),
|
|
||||||
notSeenIdxBuf: make([]int, 0, 10),
|
|
||||||
identityKeys: make(map[string]signingkey.PubKey),
|
|
||||||
}
|
|
||||||
err := docTree.rebuildFromStorage(aclList, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
docTree.id, err = t.ID()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
docTree.header, err = t.Header()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if listener != nil {
|
|
||||||
listener.Rebuild(docTree)
|
|
||||||
}
|
|
||||||
|
|
||||||
return docTree, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func BuildDocTree(t storage.TreeStorage, decoder keys.Decoder, listener TreeUpdateListener, aclList list.ACLList) (DocTree, error) {
|
func BuildDocTree(t storage.TreeStorage, decoder keys.Decoder, listener TreeUpdateListener, aclList list.ACLList) (DocTree, error) {
|
||||||
|
return buildDocTreeWithAccount(t, nil, decoder, listener, aclList)
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildDocTreeWithAccount(
|
||||||
|
t storage.TreeStorage,
|
||||||
|
acc *account.AccountData,
|
||||||
|
decoder keys.Decoder,
|
||||||
|
listener TreeUpdateListener,
|
||||||
|
aclList list.ACLList) (DocTree, error) {
|
||||||
|
|
||||||
treeBuilder := newTreeBuilder(t, decoder)
|
treeBuilder := newTreeBuilder(t, decoder)
|
||||||
validator := newTreeValidator()
|
validator := newTreeValidator()
|
||||||
|
|
||||||
docTree := &docTree{
|
docTree := &docTree{
|
||||||
treeStorage: t,
|
treeStorage: t,
|
||||||
tree: nil,
|
tree: nil,
|
||||||
|
accountData: acc,
|
||||||
treeBuilder: treeBuilder,
|
treeBuilder: treeBuilder,
|
||||||
validator: validator,
|
validator: validator,
|
||||||
updateListener: listener,
|
updateListener: listener,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user