From 8cf5acd183e1124ced40023ece91b92b957a7357 Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Fri, 19 Aug 2022 22:31:13 +0200 Subject: [PATCH] Add some logic in validator --- pkg/acl/list/aclstate.go | 47 ++++++++++++++++++++++++++++++--- pkg/acl/tree/changevalidator.go | 46 +++++++++++++++++++++++++++++--- service/treecache/service.go | 3 ++- 3 files changed, 88 insertions(+), 8 deletions(-) diff --git a/pkg/acl/list/aclstate.go b/pkg/acl/list/aclstate.go index 31c0f464..e15b3ec6 100644 --- a/pkg/acl/list/aclstate.go +++ b/pkg/acl/list/aclstate.go @@ -22,6 +22,13 @@ var ErrFailedToDecrypt = errors.New("failed to decrypt key") var ErrUserRemoved = errors.New("user was removed from the document") var ErrDocumentForbidden = errors.New("your user was forbidden access to the document") var ErrUserAlreadyExists = errors.New("user already exists") +var ErrNoSuchRecord = errors.New("no such record") +var ErrInsufficientPermissions = errors.New("insufficient permissions") + +type UserPermissionPair struct { + Identity string + Permission aclpb.ACLChangeUserPermissions +} type ACLState struct { currentReadKeyHash uint64 @@ -31,6 +38,7 @@ type ACLState struct { signingPubKeyDecoder keys.Decoder encryptionKey encryptionkey.PrivKey identity string + permissionsAtRecord map[string][]UserPermissionPair } func newACLStateWithIdentity( @@ -44,14 +52,16 @@ func newACLStateWithIdentity( userStates: make(map[string]*aclpb.ACLChangeUserState), userInvites: make(map[string]*aclpb.ACLChangeUserInvite), signingPubKeyDecoder: decoder, + permissionsAtRecord: make(map[string][]UserPermissionPair), } } func newACLState() *ACLState { return &ACLState{ - userReadKeys: make(map[uint64]*symmetric.Key), - userStates: make(map[string]*aclpb.ACLChangeUserState), - userInvites: make(map[string]*aclpb.ACLChangeUserInvite), + userReadKeys: make(map[uint64]*symmetric.Key), + userStates: make(map[string]*aclpb.ACLChangeUserState), + userInvites: make(map[string]*aclpb.ACLChangeUserInvite), + permissionsAtRecord: make(map[string][]UserPermissionPair), } } @@ -63,7 +73,22 @@ func (st *ACLState) UserReadKeys() map[uint64]*symmetric.Key { return st.userReadKeys } +func (st *ACLState) PermissionsAtRecord(id string, identity string) (UserPermissionPair, error) { + permissions, ok := st.permissionsAtRecord[id] + if !ok { + return UserPermissionPair{}, ErrNoSuchRecord + } + + for _, perm := range permissions { + if perm.Identity == identity { + return perm, nil + } + } + return UserPermissionPair{}, ErrNoSuchUser +} + func (st *ACLState) applyRecord(record *aclpb.Record) (err error) { + // TODO: this should be probably changed aclData := &aclpb.ACLChangeACLData{} err = proto.Unmarshal(record.Data, aclData) @@ -95,7 +120,21 @@ func (st *ACLState) applyChangeAndUpdate(recordWrapper *Record) (err error) { recordWrapper.ParsedModel = aclData } - return st.applyChangeData(aclData, recordWrapper.Content.CurrentReadKeyHash, recordWrapper.Content.Identity) + err = st.applyChangeData(aclData, recordWrapper.Content.CurrentReadKeyHash, recordWrapper.Content.Identity) + if err != nil { + return err + } + + var permissions []UserPermissionPair + for _, state := range st.userStates { + permission := UserPermissionPair{ + Identity: state.Identity, + Permission: state.Permissions, + } + permissions = append(permissions, permission) + } + st.permissionsAtRecord[recordWrapper.Id] = permissions + return nil } func (st *ACLState) applyChangeData(changeData *aclpb.ACLChangeACLData, hash uint64, identity string) (err error) { diff --git a/pkg/acl/tree/changevalidator.go b/pkg/acl/tree/changevalidator.go index b1161878..2f0b5fe2 100644 --- a/pkg/acl/tree/changevalidator.go +++ b/pkg/acl/tree/changevalidator.go @@ -1,6 +1,10 @@ package tree -import "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/list" +import ( + "fmt" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/list" +) type DocTreeValidator interface { ValidateTree(tree *Tree, aclList list.ACLList) error @@ -11,8 +15,44 @@ type docTreeValidator struct{} func newTreeValidator() DocTreeValidator { return &docTreeValidator{} } -func (v *docTreeValidator) ValidateTree(tree *Tree, list list.ACLList) error { + +func (v *docTreeValidator) ValidateTree(tree *Tree, aclList list.ACLList) (err error) { // TODO: add validation logic where we check that the change refers to correct acl heads // that means that more recent changes should refer to more recent acl heads - return nil + var ( + perm list.UserPermissionPair + state = aclList.ACLState() + ) + + tree.Iterate(tree.RootId(), func(c *Change) (isContinue bool) { + // checking if the user could write + perm, err = state.PermissionsAtRecord(c.Id, c.Content.Identity) + if err != nil { + return false + } + + if perm.Permission != aclpb.ACLChange_Writer && perm.Permission != aclpb.ACLChange_Admin { + err = list.ErrInsufficientPermissions + return false + } + + // checking if the change refers to later acl heads than its previous ids + for _, id := range c.PreviousIds { + prevChange := tree.attached[id] + if prevChange.Content.AclHeadId == c.Content.AclHeadId { + continue + } + var after bool + after, err = aclList.IsAfter(c.Content.AclHeadId, prevChange.Content.AclHeadId) + if err != nil { + return false + } + if !after { + err = fmt.Errorf("current acl head id (%s) should be after each of the previous ones (%s)", c.Content.AclHeadId, prevChange.Content.AclHeadId) + return false + } + } + return true + }) + return err } diff --git a/service/treecache/service.go b/service/treecache/service.go index f442b5ab..ec6cae8a 100644 --- a/service/treecache/service.go +++ b/service/treecache/service.go @@ -6,6 +6,7 @@ import ( "github.com/anytypeio/go-anytype-infrastructure-experiments/app" "github.com/anytypeio/go-anytype-infrastructure-experiments/app/logger" "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/list" "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/tree" "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/ocache" "github.com/anytypeio/go-anytype-infrastructure-experiments/service/account" @@ -92,7 +93,7 @@ func (s *service) loadTree(ctx context.Context, id string) (ocache.Object, error switch header.DocType { case aclpb.Header_ACL: - return tree.BuildACLTreeWithIdentity(t, s.account.Account(), nil) + return list.BuildACLListWithIdentity(acc) case aclpb.Header_DocTree: break default: