From f27418127051905debb35870e6d8dd43677f8d05 Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Tue, 12 Jul 2022 21:27:27 +0200 Subject: [PATCH] Add test for thread utility --- acltree/aclstate.go | 11 +++++++- acltree/changebuilder.go | 34 ++++++++++++----------- acltree/threadutility.go | 9 ++++++- acltree/threadutility_test.go | 51 +++++++++++++++++++++++++++++++++++ 4 files changed, 88 insertions(+), 17 deletions(-) create mode 100644 acltree/threadutility_test.go diff --git a/acltree/aclstate.go b/acltree/aclstate.go index b56142b6..9f9a5347 100644 --- a/acltree/aclstate.go +++ b/acltree/aclstate.go @@ -110,8 +110,11 @@ func (st *ACLState) applyChange(change *pb.ACLChange) (err error) { } st.currentReadKeyHash = change.CurrentReadKeyHash }() + // we can't check this for the user which is joining, because it will not be in our list - if !st.isUserJoin(change) { + // the same is for the first change to be added + skipIdentityCheck := st.isUserJoin(change) || (st.currentReadKeyHash == 0 && st.isUserAdd(change)) + if !skipIdentityCheck { // we check signature when we add this to the Tree, so no need to do it here if _, exists := st.userStates[change.Identity]; !exists { err = ErrNoSuchUser @@ -320,6 +323,12 @@ func (st *ACLState) isUserJoin(ch *pb.ACLChange) bool { return ch.AclData.GetAclContent() != nil && ch.AclData.GetAclContent()[0].GetUserJoin() != nil } +func (st *ACLState) isUserAdd(ch *pb.ACLChange) bool { + // if we have a UserAdd, then it should always be the first one applied + userAdd := ch.AclData.GetAclContent()[0].GetUserAdd() + return ch.AclData.GetAclContent() != nil && userAdd != nil && userAdd.GetIdentity() == ch.Identity +} + func (st *ACLState) getPermissionDecreasedUsers(ch *pb.ACLChange) (identities []*pb.ACLChangeUserPermissionChange) { // this should be called after general checks are completed if ch.GetAclData().GetAclContent() == nil { diff --git a/acltree/changebuilder.go b/acltree/changebuilder.go index 4a31250e..1c97397c 100644 --- a/acltree/changebuilder.go +++ b/acltree/changebuilder.go @@ -8,6 +8,7 @@ import ( "github.com/gogo/protobuf/proto" "github.com/textileio/go-threads/crypto/symmetric" "hash/fnv" + "time" ) type MarshalledChange = []byte @@ -105,28 +106,16 @@ func (c *changeBuilder) UserAdd(identity string, encryptionKey keys.EncryptionPu } func (c *changeBuilder) BuildAndApply() (*Change, []byte, error) { - marshalled, err := c.changeContent.Marshal() - if err != nil { - return nil, nil, err - } - - encrypted, err := c.aclState.userReadKeys[c.aclState.currentReadKeyHash]. - Encrypt(marshalled) - if err != nil { - return nil, nil, err - } - aclChange := &pb.ACLChange{ TreeHeadIds: c.tree.Heads(), AclHeadIds: c.tree.ACLHeads(), SnapshotBaseId: c.tree.RootId(), AclData: c.aclData, - ChangesData: encrypted, - CurrentReadKeyHash: c.aclState.currentReadKeyHash, - Timestamp: 0, + CurrentReadKeyHash: c.readKeyHash, + Timestamp: int64(time.Now().Nanosecond()), Identity: c.acc.Identity, } - err = c.aclState.applyChange(aclChange) + err := c.aclState.applyChange(aclChange) if err != nil { return nil, nil, err } @@ -135,6 +124,21 @@ func (c *changeBuilder) BuildAndApply() (*Change, []byte, error) { c.aclData.AclSnapshot = c.aclState.makeSnapshot() } + var marshalled []byte + if c.changeContent != nil { + marshalled, err = c.changeContent.Marshal() + if err != nil { + return nil, nil, err + } + + encrypted, err := c.aclState.userReadKeys[c.aclState.currentReadKeyHash]. + Encrypt(marshalled) + if err != nil { + return nil, nil, err + } + aclChange.ChangesData = encrypted + } + fullMarshalledChange, err := proto.Marshal(aclChange) if err != nil { return nil, nil, err diff --git a/acltree/threadutility.go b/acltree/threadutility.go index 8b847bfe..034ea2fd 100644 --- a/acltree/threadutility.go +++ b/acltree/threadutility.go @@ -31,5 +31,12 @@ func BuildThreadWithACL( Signature: change.Signature(), Id: change.CID(), } - return create(rawChange) + + thr, err := create(rawChange) + if err != nil { + return nil, err + } + + thr.SetHeads([]string{change.CID()}) + return thr, nil } diff --git a/acltree/threadutility_test.go b/acltree/threadutility_test.go new file mode 100644 index 00000000..91258127 --- /dev/null +++ b/acltree/threadutility_test.go @@ -0,0 +1,51 @@ +package acltree + +import ( + "context" + "github.com/anytypeio/go-anytype-infrastructure-experiments/account" + "github.com/anytypeio/go-anytype-infrastructure-experiments/aclchanges/pb" + "github.com/anytypeio/go-anytype-infrastructure-experiments/testutils/threadbuilder" + "github.com/anytypeio/go-anytype-infrastructure-experiments/thread" + "github.com/stretchr/testify/assert" + "testing" +) + +func TestBuildThreadWithACL(t *testing.T) { + keychain := threadbuilder.NewKeychain() + keychain.AddSigningKey("A") + keychain.AddEncryptionKey("A") + data := &account.AccountData{ + Identity: keychain.GetIdentity("A"), + SignKey: keychain.SigningKeys["A"], + EncKey: keychain.EncryptionKeys["A"], + } + thr, err := BuildThreadWithACL( + data, + func(builder ChangeBuilder) error { + return builder.UserAdd( + keychain.GetIdentity("A"), + keychain.EncryptionKeys["A"].GetPublic(), + pb.ACLChange_Admin) + }, + thread.NewInMemoryThread) + if err != nil { + t.Fatalf("build should not return error") + } + if len(thr.Heads()) == 0 { + t.Fatalf("thread should have non-empty heads") + } + if thr.Header() == nil { + t.Fatalf("thread should have non-empty header") + } + assert.Equal(t, thr.Heads()[0], thr.Header().FirstChangeId) + assert.NotEmpty(t, thr.ID()) + ch, err := thr.GetChange(context.Background(), thr.Header().FirstChangeId) + if err != nil { + t.Fatalf("get change should not return error: %v", err) + } + + _, err = NewFromRawChange(ch) + if err != nil { + t.Fatalf("we should be able to unmarshall change: %v", err) + } +}