From 550d1826a99b21581a65fc51e46f0727044e2031 Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Fri, 19 Aug 2022 18:17:02 +0200 Subject: [PATCH] Change list builder --- Makefile | 1 - pkg/acl/aclchanges/aclpb/aclchanges.pb.go | 472 ++++++- .../aclchanges/aclpb/protos/aclchanges.proto | 12 + pkg/acl/acltree/aclstate.go | 411 ------- pkg/acl/acltree/aclstatebuilder.go | 186 --- pkg/acl/acltree/acltree.go | 520 -------- pkg/acl/acltree/acltree_test.go | 262 ---- pkg/acl/acltree/acltreebuilder.go | 154 --- pkg/acl/acltree/acltreestorage.go | 70 -- pkg/acl/acltree/acltreestorage_test.go | 63 - pkg/acl/acltree/change.go | 97 -- pkg/acl/acltree/changebuilder.go | 163 --- pkg/acl/acltree/changeloader.go | 99 -- pkg/acl/acltree/doctree.go | 8 - pkg/acl/acltree/snapshotvalidator.go | 50 - pkg/acl/acltree/tree.go | 416 ------- pkg/acl/acltree/treebuilder.go | 307 ----- pkg/acl/acltree/treebuilder_test.go | 64 - pkg/acl/acltree/treegraph.go | 11 - pkg/acl/acltree/treegraph_nix.go | 152 --- pkg/acl/acltree/treeiterator.go | 158 --- pkg/acl/example/plaintextdocument/document.go | 169 --- .../plaintextdocument/document_test.go | 58 - .../plaintextdocument/plaintextdocstate.go | 59 - pkg/acl/list/changebuilder.go | 2 +- pkg/acl/list/list.go | 49 +- pkg/acl/list/storage.go | 14 + .../keychain.go | 2 +- .../acllistbuilder/liststoragebuilder.go | 320 +++++ .../treestoragebuildergraph.go | 4 +- .../treestoragebuildergraph_nix.go | 121 ++ .../ymlentities.go | 63 +- .../ymlentities_test.go | 4 +- .../protos/testdocumentchanges.proto | 26 - .../testchangepb/testdocumentchanges.pb.go | 1089 ----------------- .../treestoragebuilder/treestoragebuilder.go | 539 -------- .../treestoragebuildergraph_nix.go | 162 --- .../yamltests/invalidsnapshotexample.yml | 126 -- .../testutils/yamltests/userjoinexample.yml | 101 +- pkg/acl/tree/doctree.go | 2 +- pkg/acl/tree/treestorage.go | 2 +- 41 files changed, 958 insertions(+), 5630 deletions(-) delete mode 100644 pkg/acl/acltree/aclstate.go delete mode 100644 pkg/acl/acltree/aclstatebuilder.go delete mode 100644 pkg/acl/acltree/acltree.go delete mode 100644 pkg/acl/acltree/acltree_test.go delete mode 100644 pkg/acl/acltree/acltreebuilder.go delete mode 100644 pkg/acl/acltree/acltreestorage.go delete mode 100644 pkg/acl/acltree/acltreestorage_test.go delete mode 100644 pkg/acl/acltree/change.go delete mode 100644 pkg/acl/acltree/changebuilder.go delete mode 100644 pkg/acl/acltree/changeloader.go delete mode 100644 pkg/acl/acltree/doctree.go delete mode 100644 pkg/acl/acltree/snapshotvalidator.go delete mode 100644 pkg/acl/acltree/tree.go delete mode 100644 pkg/acl/acltree/treebuilder.go delete mode 100644 pkg/acl/acltree/treebuilder_test.go delete mode 100644 pkg/acl/acltree/treegraph.go delete mode 100644 pkg/acl/acltree/treegraph_nix.go delete mode 100644 pkg/acl/acltree/treeiterator.go delete mode 100644 pkg/acl/example/plaintextdocument/document.go delete mode 100644 pkg/acl/example/plaintextdocument/document_test.go delete mode 100644 pkg/acl/example/plaintextdocument/plaintextdocstate.go create mode 100644 pkg/acl/list/storage.go rename pkg/acl/testutils/{treestoragebuilder => acllistbuilder}/keychain.go (99%) create mode 100644 pkg/acl/testutils/acllistbuilder/liststoragebuilder.go rename pkg/acl/testutils/{treestoragebuilder => acllistbuilder}/treestoragebuildergraph.go (72%) create mode 100644 pkg/acl/testutils/acllistbuilder/treestoragebuildergraph_nix.go rename pkg/acl/testutils/{treestoragebuilder => acllistbuilder}/ymlentities.go (50%) rename pkg/acl/testutils/{treestoragebuilder => acllistbuilder}/ymlentities_test.go (51%) delete mode 100644 pkg/acl/testutils/testchanges/testchangepb/protos/testdocumentchanges.proto delete mode 100644 pkg/acl/testutils/testchanges/testchangepb/testdocumentchanges.pb.go delete mode 100644 pkg/acl/testutils/treestoragebuilder/treestoragebuilder.go delete mode 100644 pkg/acl/testutils/treestoragebuilder/treestoragebuildergraph_nix.go delete mode 100644 pkg/acl/testutils/yamltests/invalidsnapshotexample.yml diff --git a/Makefile b/Makefile index 82cb14de..66273458 100644 --- a/Makefile +++ b/Makefile @@ -30,7 +30,6 @@ protos-go: # use if needed $(eval PKGMAP := $$(P_TIMESTAMP),$$(P_STRUCT)) $(GOGO_START) protoc --gogofaster_out=:. $(P_ACL_CHANGES_PATH_PB)/protos/*.proto; mv $(P_ACL_CHANGES_PATH_PB)/protos/*.go $(P_ACL_CHANGES_PATH_PB) $(GOGO_START) protoc --gogofaster_out=:. $(P_TREE_STORAGE_PATH_PB)/protos/*.proto; mv $(P_TREE_STORAGE_PATH_PB)/protos/*.go $(P_TREE_STORAGE_PATH_PB) - $(GOGO_START) protoc --gogofaster_out=:. $(P_PLAINTEXT_CHANGES_PATH_PB)/protos/*.proto; mv $(P_PLAINTEXT_CHANGES_PATH_PB)/protos/*.go $(P_PLAINTEXT_CHANGES_PATH_PB) $(eval PKGMAP := $$(P_ACL_CHANGES),$$(P_TREE_CHANGES)) $(GOGO_START) protoc --gogofaster_out=$(PKGMAP):. $(P_SYNC_CHANGES_PATH_PB)/proto/*.proto $(GOGO_START) protoc --gogofaster_out=$(PKGMAP):. service/space/spacesync/protos/*.proto diff --git a/pkg/acl/aclchanges/aclpb/aclchanges.pb.go b/pkg/acl/aclchanges/aclpb/aclchanges.pb.go index 0f6fec52..c026319c 100644 --- a/pkg/acl/aclchanges/aclpb/aclchanges.pb.go +++ b/pkg/acl/aclchanges/aclpb/aclchanges.pb.go @@ -53,6 +53,31 @@ func (ACLChangeUserPermissions) EnumDescriptor() ([]byte, []int) { return fileDescriptor_37a022c841a51877, []int{2, 0} } +type HeaderDocType int32 + +const ( + Header_ACL HeaderDocType = 0 + Header_DocTree HeaderDocType = 1 +) + +var HeaderDocType_name = map[int32]string{ + 0: "ACL", + 1: "DocTree", +} + +var HeaderDocType_value = map[string]int32{ + "ACL": 0, + "DocTree": 1, +} + +func (x HeaderDocType) String() string { + return proto.EnumName(HeaderDocType_name, int32(x)) +} + +func (HeaderDocType) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_37a022c841a51877, []int{5, 0} +} + type RawChange struct { Payload []byte `protobuf:"bytes,1,opt,name=payload,proto3" json:"payload,omitempty"` Signature []byte `protobuf:"bytes,2,opt,name=signature,proto3" json:"signature,omitempty"` @@ -1259,8 +1284,77 @@ func (m *Record) GetTimestamp() int64 { return 0 } +type Header struct { + FirstId string `protobuf:"bytes,1,opt,name=firstId,proto3" json:"firstId,omitempty"` + AclListId string `protobuf:"bytes,2,opt,name=aclListId,proto3" json:"aclListId,omitempty"` + WorkspaceId string `protobuf:"bytes,3,opt,name=workspaceId,proto3" json:"workspaceId,omitempty"` + DocType HeaderDocType `protobuf:"varint,4,opt,name=docType,proto3,enum=acl.HeaderDocType" json:"docType,omitempty"` +} + +func (m *Header) Reset() { *m = Header{} } +func (m *Header) String() string { return proto.CompactTextString(m) } +func (*Header) ProtoMessage() {} +func (*Header) Descriptor() ([]byte, []int) { + return fileDescriptor_37a022c841a51877, []int{5} +} +func (m *Header) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Header) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Header.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Header) XXX_Merge(src proto.Message) { + xxx_messageInfo_Header.Merge(m, src) +} +func (m *Header) XXX_Size() int { + return m.Size() +} +func (m *Header) XXX_DiscardUnknown() { + xxx_messageInfo_Header.DiscardUnknown(m) +} + +var xxx_messageInfo_Header proto.InternalMessageInfo + +func (m *Header) GetFirstId() string { + if m != nil { + return m.FirstId + } + return "" +} + +func (m *Header) GetAclListId() string { + if m != nil { + return m.AclListId + } + return "" +} + +func (m *Header) GetWorkspaceId() string { + if m != nil { + return m.WorkspaceId + } + return "" +} + +func (m *Header) GetDocType() HeaderDocType { + if m != nil { + return m.DocType + } + return Header_ACL +} + func init() { proto.RegisterEnum("acl.ACLChangeUserPermissions", ACLChangeUserPermissions_name, ACLChangeUserPermissions_value) + proto.RegisterEnum("acl.HeaderDocType", HeaderDocType_name, HeaderDocType_value) proto.RegisterType((*RawChange)(nil), "acl.RawChange") proto.RegisterType((*RawRecord)(nil), "acl.RawRecord") proto.RegisterType((*ACLChange)(nil), "acl.ACLChange") @@ -1279,6 +1373,7 @@ func init() { proto.RegisterType((*ACLChangeUserPermissionChange)(nil), "acl.ACLChange.UserPermissionChange") proto.RegisterType((*Change)(nil), "acl.Change") proto.RegisterType((*Record)(nil), "acl.Record") + proto.RegisterType((*Header)(nil), "acl.Header") } func init() { @@ -1286,73 +1381,78 @@ func init() { } var fileDescriptor_37a022c841a51877 = []byte{ - // 1048 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x57, 0x4f, 0x6f, 0xe3, 0x54, - 0x10, 0x8f, 0x9d, 0x3f, 0x8e, 0xc7, 0xa1, 0x0d, 0x8f, 0xd5, 0xae, 0xb1, 0x4a, 0x88, 0xc2, 0x0a, - 0x45, 0x08, 0xa5, 0xab, 0xac, 0x90, 0x56, 0x80, 0x2a, 0xda, 0x82, 0x36, 0xa1, 0x1c, 0x56, 0xaf, - 0x5a, 0x10, 0xdc, 0x5e, 0xed, 0x47, 0x6b, 0xad, 0x63, 0x1b, 0xfb, 0xa5, 0x28, 0x17, 0x24, 0x4e, - 0x5c, 0x39, 0x23, 0xc4, 0x17, 0xe1, 0x0b, 0xec, 0xb1, 0x47, 0x6e, 0xa0, 0xf6, 0xce, 0x85, 0x2f, - 0x80, 0xde, 0x1f, 0x3b, 0x8e, 0xe3, 0x46, 0x41, 0xaa, 0x90, 0xf6, 0x10, 0xe9, 0xbd, 0xdf, 0xcc, - 0xbc, 0xcc, 0xcc, 0x6f, 0x66, 0x32, 0x81, 0x47, 0xf1, 0x8b, 0xf3, 0x7d, 0xe2, 0x06, 0xfc, 0xe3, - 0x5e, 0x90, 0xf0, 0x9c, 0xa6, 0xfc, 0x18, 0x9f, 0xed, 0xc7, 0x49, 0xc4, 0xa2, 0xb4, 0x80, 0x8f, - 0x04, 0x82, 0xea, 0xc4, 0x0d, 0x06, 0xa7, 0x60, 0x62, 0xf2, 0xfd, 0xb1, 0x10, 0x20, 0x1b, 0x8c, - 0x98, 0x2c, 0x82, 0x88, 0x78, 0xb6, 0xd6, 0xd7, 0x86, 0x1d, 0x9c, 0x5d, 0xd1, 0x1e, 0x98, 0xa9, - 0x7f, 0x1e, 0x12, 0x36, 0x4f, 0xa8, 0xad, 0x0b, 0xd9, 0x12, 0x40, 0x3b, 0xa0, 0xfb, 0x9e, 0x5d, - 0xef, 0x6b, 0x43, 0x13, 0xeb, 0xbe, 0xa7, 0x1e, 0xc5, 0xd4, 0x8d, 0x12, 0xef, 0xce, 0x1e, 0xfd, - 0xe7, 0x0d, 0x30, 0x0f, 0x8f, 0xbf, 0x50, 0xae, 0xf6, 0xc1, 0x62, 0x09, 0xa5, 0x13, 0x4a, 0xbc, - 0xa9, 0x97, 0xda, 0x5a, 0xbf, 0x3e, 0x34, 0x71, 0x11, 0x42, 0x3d, 0x00, 0xe2, 0x06, 0x99, 0x82, - 0x2e, 0x14, 0x0a, 0x08, 0x7a, 0x17, 0x76, 0xd2, 0x90, 0xc4, 0xe9, 0x45, 0xc4, 0x8e, 0x48, 0x4a, - 0xa7, 0xd9, 0x77, 0x95, 0x50, 0xf4, 0x08, 0x0c, 0xe2, 0x06, 0x9f, 0x12, 0x46, 0xec, 0x46, 0x5f, - 0x1b, 0x5a, 0xe3, 0xfb, 0x23, 0xe2, 0x06, 0xa3, 0xdc, 0x15, 0x7e, 0xe2, 0x52, 0x9c, 0xa9, 0x71, - 0xdf, 0x54, 0xa6, 0x85, 0x55, 0x53, 0x44, 0x56, 0x84, 0xd0, 0x08, 0x90, 0x3b, 0x4f, 0x12, 0x1a, - 0x32, 0x4c, 0x89, 0x77, 0x42, 0x17, 0x13, 0x92, 0x5e, 0xd8, 0xad, 0xbe, 0x36, 0x6c, 0xe0, 0x0a, - 0x09, 0xcf, 0x14, 0xf3, 0x67, 0x34, 0x65, 0x64, 0x16, 0xdb, 0x46, 0x5f, 0x1b, 0xd6, 0xf1, 0x12, - 0x40, 0x0e, 0xb4, 0x7d, 0x8f, 0x86, 0xcc, 0x67, 0x0b, 0xbb, 0x2d, 0x62, 0xc8, 0xef, 0xce, 0x2f, - 0x75, 0xd8, 0xe5, 0xae, 0x46, 0x21, 0xa3, 0x21, 0xfb, 0x92, 0x04, 0x73, 0x8a, 0xc6, 0x60, 0xcc, - 0x53, 0x9a, 0x1c, 0x7a, 0x92, 0x91, 0xf5, 0x88, 0x9e, 0x4b, 0xe9, 0xa4, 0x86, 0x33, 0x45, 0xf4, - 0x11, 0x00, 0x3f, 0x62, 0x3a, 0x8b, 0x2e, 0x25, 0x59, 0xd6, 0xf8, 0xcd, 0x0a, 0x33, 0xa9, 0x30, - 0xa9, 0xe1, 0x82, 0x3a, 0xfa, 0x1a, 0xee, 0xf1, 0xdb, 0x33, 0x9a, 0xcc, 0xfc, 0x34, 0xf5, 0xa3, - 0x50, 0x1a, 0x88, 0x84, 0x5b, 0xe3, 0x77, 0x2a, 0x9e, 0x29, 0xab, 0x4e, 0x6a, 0xb8, 0xf2, 0x89, - 0xcc, 0xaf, 0x69, 0x78, 0xe9, 0x33, 0xaa, 0x08, 0xaa, 0xf2, 0x4b, 0x2a, 0x64, 0x7e, 0xc9, 0x1b, - 0xfa, 0x00, 0xda, 0xfc, 0xf6, 0x79, 0xe4, 0x87, 0x82, 0x25, 0x6b, 0xfc, 0xa0, 0xc2, 0x94, 0x8b, - 0x27, 0x35, 0x9c, 0xab, 0xa2, 0x03, 0xb0, 0xf8, 0xf9, 0x38, 0x0a, 0xbf, 0xf5, 0x93, 0x99, 0xa0, - 0xcd, 0x1a, 0x3b, 0x15, 0x96, 0x4a, 0x63, 0x52, 0xc3, 0x45, 0x83, 0x23, 0x03, 0x9a, 0x97, 0x9c, - 0x08, 0xe7, 0x27, 0x0d, 0x0c, 0x55, 0x3d, 0xe8, 0x63, 0xb0, 0x88, 0x1b, 0x9c, 0xaa, 0xda, 0x53, - 0xc4, 0x38, 0xeb, 0xa5, 0x96, 0x69, 0xe0, 0xa2, 0x3a, 0x3a, 0x10, 0xc5, 0xae, 0x58, 0x16, 0xc5, - 0x6e, 0x8d, 0x7b, 0xeb, 0xc6, 0xc5, 0x32, 0xc0, 0x05, 0x0b, 0xe7, 0x08, 0xac, 0xc2, 0xdb, 0xe8, - 0x31, 0xb4, 0xf9, 0xeb, 0x8c, 0x30, 0xaa, 0x3c, 0x79, 0x50, 0xe1, 0x09, 0x17, 0xe3, 0x5c, 0xd1, - 0xf9, 0x51, 0x87, 0x76, 0x06, 0xa3, 0x87, 0xf0, 0x5a, 0xb2, 0x2c, 0x60, 0x2a, 0x3b, 0xb4, 0x81, - 0x57, 0x41, 0xf4, 0x44, 0xb2, 0x27, 0x4c, 0x52, 0xe5, 0xb6, 0x5d, 0x91, 0x48, 0xf9, 0x55, 0x05, - 0x5d, 0x74, 0x00, 0x86, 0x2f, 0x48, 0x4c, 0xed, 0xba, 0x30, 0x7b, 0x78, 0x8b, 0x83, 0x23, 0xc9, - 0x75, 0xfa, 0x59, 0xc8, 0x92, 0x05, 0xce, 0x8c, 0x9c, 0xe7, 0xd0, 0x29, 0x0a, 0x50, 0x17, 0xea, - 0x2f, 0xe8, 0x42, 0x04, 0x6b, 0x62, 0x7e, 0x44, 0xfb, 0x8a, 0xa5, 0x0d, 0xc5, 0x2e, 0x5f, 0xc0, - 0x52, 0xef, 0x43, 0xfd, 0x89, 0xe6, 0xfc, 0xa9, 0x81, 0x99, 0x3b, 0xbc, 0xd2, 0x98, 0xda, 0x6a, - 0x63, 0xf2, 0x04, 0xd1, 0xd0, 0x4d, 0x16, 0x31, 0xf3, 0xa3, 0xf0, 0x84, 0x2e, 0xd4, 0x00, 0x5c, - 0x05, 0xd1, 0xfb, 0xf0, 0xba, 0x02, 0xa8, 0xa7, 0x06, 0x82, 0x0c, 0xb8, 0x83, 0xd7, 0x05, 0xe8, - 0x13, 0xb0, 0xe2, 0xbc, 0x41, 0x52, 0xd1, 0x0d, 0x3b, 0x6b, 0x65, 0xb0, 0xda, 0x5e, 0x29, 0x2e, - 0x9a, 0xf0, 0xd1, 0x35, 0x4d, 0x55, 0x9d, 0x52, 0x4f, 0x34, 0x45, 0x1b, 0x17, 0x21, 0xe7, 0x77, - 0x0d, 0x0c, 0x35, 0x1f, 0x5e, 0xbd, 0xf8, 0x9c, 0xa7, 0x60, 0x15, 0x1a, 0x73, 0x63, 0x00, 0x7b, - 0x60, 0xaa, 0xe1, 0x37, 0xf5, 0x84, 0xf3, 0x26, 0x5e, 0x02, 0xce, 0xdf, 0x1a, 0xc0, 0xb2, 0x04, - 0xd0, 0x10, 0x76, 0x89, 0xeb, 0xd2, 0x98, 0x3d, 0x9b, 0x9f, 0x05, 0xbe, 0x7b, 0xa2, 0x4a, 0xa9, - 0x83, 0xcb, 0x30, 0x7a, 0x0f, 0xba, 0x2a, 0xb0, 0xa5, 0xaa, 0x4c, 0xcd, 0x1a, 0xfe, 0xbf, 0xb3, - 0xef, 0x40, 0x5b, 0xc6, 0x33, 0x95, 0xd4, 0x9b, 0x38, 0xbf, 0x3b, 0x2f, 0x35, 0x68, 0x67, 0xd3, - 0xf0, 0x0e, 0x88, 0xcf, 0x13, 0x76, 0x9a, 0x6f, 0x00, 0xf5, 0x62, 0xc2, 0x72, 0x18, 0x0d, 0xa0, - 0xb3, 0x1c, 0xd9, 0x53, 0x4f, 0xc4, 0x65, 0xe2, 0x15, 0xac, 0x3a, 0x51, 0xcd, 0x5b, 0x12, 0xe5, - 0x7c, 0x27, 0xa9, 0x53, 0x3f, 0x4e, 0x9b, 0x62, 0x79, 0x0a, 0xbb, 0x6a, 0x60, 0x61, 0x1a, 0x07, - 0xc4, 0xcd, 0xa7, 0xcd, 0x5b, 0xa5, 0xb4, 0xe2, 0x15, 0x2d, 0x5c, 0xb6, 0x72, 0x7e, 0x80, 0x9d, - 0x55, 0x95, 0x3b, 0x48, 0xe1, 0xb2, 0x92, 0xf2, 0xd8, 0x54, 0x0e, 0xd7, 0x70, 0x87, 0xc1, 0xbd, - 0xaa, 0x9f, 0xd5, 0x8d, 0x5e, 0x94, 0xea, 0x49, 0xff, 0xcf, 0xf5, 0x34, 0x38, 0x84, 0xdd, 0x92, - 0x1c, 0x99, 0xd0, 0x3c, 0xf4, 0x66, 0x7e, 0xd8, 0xad, 0x21, 0x80, 0xd6, 0x57, 0x89, 0xcf, 0x68, - 0xd2, 0xd5, 0xf8, 0x99, 0xbb, 0x4a, 0x93, 0xae, 0x8e, 0x2c, 0x30, 0x24, 0x35, 0x5e, 0xb7, 0x3e, - 0xf8, 0x55, 0x87, 0xd6, 0xd6, 0x2b, 0xdf, 0x1e, 0x98, 0xf9, 0x82, 0x97, 0xb5, 0x6c, 0x0e, 0x6c, - 0xbd, 0xf0, 0x95, 0xd6, 0xb7, 0xc6, 0xb6, 0xeb, 0x5b, 0x73, 0xbb, 0xf5, 0xad, 0xb5, 0x69, 0x7d, - 0x33, 0x4a, 0x1c, 0xf4, 0x00, 0xfc, 0x34, 0x5f, 0x0a, 0xda, 0x62, 0x1c, 0x17, 0x90, 0xc1, 0x6f, - 0x1a, 0x4f, 0x9c, 0xd8, 0xb3, 0xef, 0x43, 0x2b, 0x4e, 0xe8, 0xe5, 0xd4, 0x53, 0x44, 0xaa, 0xdb, - 0xca, 0xf3, 0x7a, 0xe9, 0x79, 0x04, 0x0d, 0x8f, 0xc7, 0x28, 0xcb, 0x46, 0x9c, 0x6f, 0x09, 0xae, - 0xb1, 0x5d, 0x70, 0xcd, 0x52, 0x70, 0x47, 0x6f, 0xbf, 0xbc, 0xee, 0x69, 0x57, 0xd7, 0x3d, 0xed, - 0xaf, 0xeb, 0x9e, 0xf6, 0xf3, 0x4d, 0xaf, 0x76, 0x75, 0xd3, 0xab, 0xfd, 0x71, 0xd3, 0xab, 0x7d, - 0xd3, 0x14, 0xff, 0x4e, 0xce, 0x5a, 0xe2, 0xcf, 0xc8, 0xe3, 0x7f, 0x03, 0x00, 0x00, 0xff, 0xff, - 0xb2, 0xbe, 0xb9, 0x2e, 0xc0, 0x0c, 0x00, 0x00, + // 1131 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x57, 0x4f, 0x6f, 0x1b, 0x45, + 0x14, 0xf7, 0xf8, 0xdf, 0x7a, 0xdf, 0x86, 0xc4, 0x4c, 0xab, 0xd6, 0xac, 0x8a, 0x6b, 0x99, 0x0a, + 0x59, 0x08, 0x9c, 0xca, 0x15, 0x52, 0x05, 0x28, 0x22, 0x49, 0x51, 0x6d, 0x92, 0x43, 0x35, 0xa1, + 0x20, 0xb8, 0x4d, 0x76, 0xa7, 0xc9, 0x2a, 0xf6, 0xee, 0xb2, 0x33, 0x49, 0xe5, 0x0b, 0x12, 0x27, + 0xae, 0x9c, 0x11, 0xe2, 0x43, 0x70, 0xe5, 0x0b, 0xf4, 0xd8, 0x23, 0x37, 0x50, 0x72, 0xe7, 0xc2, + 0x17, 0x40, 0xf3, 0x67, 0xd7, 0xeb, 0xf5, 0x26, 0x0a, 0x52, 0x84, 0xc4, 0x21, 0xd2, 0xcc, 0xef, + 0xfd, 0xde, 0xf8, 0xbd, 0xf7, 0x7b, 0xf3, 0x32, 0x0b, 0x0f, 0xe3, 0x93, 0xa3, 0x4d, 0xea, 0x4d, + 0xe5, 0x9f, 0x77, 0x4c, 0xc3, 0x23, 0xc6, 0xe5, 0x32, 0x3e, 0xdc, 0x8c, 0x93, 0x48, 0x44, 0x3c, + 0x87, 0x0f, 0x15, 0x82, 0x6b, 0xd4, 0x9b, 0xf6, 0x0f, 0xc0, 0x26, 0xf4, 0xe5, 0xae, 0x32, 0xe0, + 0x0e, 0x58, 0x31, 0x9d, 0x4f, 0x23, 0xea, 0x77, 0x50, 0x0f, 0x0d, 0xd6, 0x48, 0xba, 0xc5, 0xf7, + 0xc0, 0xe6, 0xc1, 0x51, 0x48, 0xc5, 0x69, 0xc2, 0x3a, 0x55, 0x65, 0x5b, 0x00, 0x78, 0x1d, 0xaa, + 0x81, 0xdf, 0xa9, 0xf5, 0xd0, 0xc0, 0x26, 0xd5, 0xc0, 0x37, 0x87, 0x12, 0xe6, 0x45, 0x89, 0x7f, + 0x63, 0x87, 0xfe, 0x7d, 0x0b, 0xec, 0xed, 0xdd, 0x7d, 0x13, 0x6a, 0x0f, 0x1c, 0x91, 0x30, 0x36, + 0x66, 0xd4, 0x9f, 0xf8, 0xbc, 0x83, 0x7a, 0xb5, 0x81, 0x4d, 0xf2, 0x10, 0xee, 0x02, 0x50, 0x6f, + 0x9a, 0x12, 0xaa, 0x8a, 0x90, 0x43, 0xf0, 0xbb, 0xb0, 0xce, 0x43, 0x1a, 0xf3, 0xe3, 0x48, 0xec, + 0x50, 0xce, 0x26, 0xe9, 0x6f, 0x15, 0x50, 0xfc, 0x10, 0x2c, 0xea, 0x4d, 0x9f, 0x50, 0x41, 0x3b, + 0xf5, 0x1e, 0x1a, 0x38, 0xa3, 0x3b, 0x43, 0xea, 0x4d, 0x87, 0x59, 0x28, 0x72, 0x25, 0xad, 0x24, + 0xa5, 0xc9, 0xd8, 0x4c, 0xa5, 0x95, 0x57, 0x43, 0x65, 0x96, 0x87, 0xf0, 0x10, 0xb0, 0x77, 0x9a, + 0x24, 0x2c, 0x14, 0x84, 0x51, 0x7f, 0x8f, 0xcd, 0xc7, 0x94, 0x1f, 0x77, 0x9a, 0x3d, 0x34, 0xa8, + 0x93, 0x12, 0x8b, 0xac, 0x94, 0x08, 0x66, 0x8c, 0x0b, 0x3a, 0x8b, 0x3b, 0x56, 0x0f, 0x0d, 0x6a, + 0x64, 0x01, 0x60, 0x17, 0x5a, 0x81, 0xcf, 0x42, 0x11, 0x88, 0x79, 0xa7, 0xa5, 0x72, 0xc8, 0xf6, + 0xee, 0x4f, 0x35, 0xd8, 0x90, 0xa1, 0x46, 0xa1, 0x60, 0xa1, 0xf8, 0x92, 0x4e, 0x4f, 0x19, 0x1e, + 0x81, 0x75, 0xca, 0x59, 0xb2, 0xed, 0x6b, 0x45, 0x56, 0x33, 0x7a, 0xae, 0xad, 0xe3, 0x0a, 0x49, + 0x89, 0xf8, 0x63, 0x00, 0xb9, 0x24, 0x6c, 0x16, 0x9d, 0x69, 0xb1, 0x9c, 0xd1, 0x5b, 0x25, 0x6e, + 0x9a, 0x30, 0xae, 0x90, 0x1c, 0x1d, 0x7f, 0x0d, 0xb7, 0xe5, 0xee, 0x19, 0x4b, 0x66, 0x01, 0xe7, + 0x41, 0x14, 0x6a, 0x07, 0x55, 0x70, 0x67, 0xf4, 0x4e, 0xc9, 0x31, 0x45, 0xea, 0xb8, 0x42, 0x4a, + 0x8f, 0x48, 0xe3, 0x9a, 0x84, 0x67, 0x81, 0x60, 0x46, 0xa0, 0xb2, 0xb8, 0x34, 0x21, 0x8d, 0x4b, + 0xef, 0xf0, 0x87, 0xd0, 0x92, 0xbb, 0xcf, 0xa3, 0x20, 0x54, 0x2a, 0x39, 0xa3, 0xbb, 0x25, 0xae, + 0xd2, 0x3c, 0xae, 0x90, 0x8c, 0x8a, 0xb7, 0xc0, 0x91, 0xeb, 0xdd, 0x28, 0x7c, 0x11, 0x24, 0x33, + 0x25, 0x9b, 0x33, 0x72, 0x4b, 0x3c, 0x0d, 0x63, 0x5c, 0x21, 0x79, 0x87, 0x1d, 0x0b, 0x1a, 0x67, + 0x52, 0x08, 0xf7, 0x07, 0x04, 0x96, 0xe9, 0x1e, 0xfc, 0x09, 0x38, 0xd4, 0x9b, 0x1e, 0x98, 0xde, + 0x33, 0xc2, 0xb8, 0xab, 0xad, 0x96, 0x32, 0x48, 0x9e, 0x8e, 0xb7, 0x54, 0xb3, 0x1b, 0x95, 0x55, + 0xb3, 0x3b, 0xa3, 0xee, 0xaa, 0x73, 0xbe, 0x0d, 0x48, 0xce, 0xc3, 0xdd, 0x01, 0x27, 0x77, 0x36, + 0x7e, 0x04, 0x2d, 0x79, 0xba, 0xa0, 0x82, 0x99, 0x48, 0xee, 0x96, 0x44, 0x22, 0xcd, 0x24, 0x23, + 0xba, 0xdf, 0x57, 0xa1, 0x95, 0xc2, 0xf8, 0x01, 0xbc, 0x91, 0x2c, 0x1a, 0x98, 0xe9, 0x1b, 0x5a, + 0x27, 0xcb, 0x20, 0x7e, 0xac, 0xd5, 0x53, 0x2e, 0xdc, 0x84, 0xdd, 0x29, 0x29, 0xa4, 0xfe, 0xa9, + 0x1c, 0x17, 0x6f, 0x81, 0x15, 0x28, 0x11, 0x79, 0xa7, 0xa6, 0xdc, 0x1e, 0x5c, 0x12, 0xe0, 0x50, + 0x6b, 0xcd, 0x3f, 0x0b, 0x45, 0x32, 0x27, 0xa9, 0x93, 0xfb, 0x1c, 0xd6, 0xf2, 0x06, 0xdc, 0x86, + 0xda, 0x09, 0x9b, 0xab, 0x64, 0x6d, 0x22, 0x97, 0x78, 0xd3, 0xa8, 0x74, 0x45, 0xb3, 0xeb, 0x13, + 0x88, 0xe6, 0x7d, 0x54, 0x7d, 0x8c, 0xdc, 0x3f, 0x10, 0xd8, 0x59, 0xc0, 0x4b, 0x17, 0x13, 0x2d, + 0x5f, 0x4c, 0x59, 0x20, 0x16, 0x7a, 0xc9, 0x3c, 0x16, 0x41, 0x14, 0xee, 0xb1, 0xb9, 0x19, 0x80, + 0xcb, 0x20, 0x7e, 0x1f, 0xde, 0x34, 0x00, 0xf3, 0xcd, 0x40, 0xd0, 0x09, 0xaf, 0x91, 0x55, 0x03, + 0xfe, 0x14, 0x9c, 0x38, 0xbb, 0x20, 0x5c, 0xdd, 0x86, 0xf5, 0x95, 0x36, 0x58, 0xbe, 0x5e, 0x9c, + 0xe4, 0x5d, 0xe4, 0xe8, 0x9a, 0x70, 0xd3, 0xa7, 0xcc, 0x57, 0x97, 0xa2, 0x45, 0xf2, 0x90, 0xfb, + 0x1b, 0x02, 0xcb, 0xcc, 0x87, 0xff, 0x5f, 0x7e, 0xee, 0x53, 0x70, 0x72, 0x17, 0xf3, 0xca, 0x04, + 0xee, 0x81, 0x6d, 0x86, 0xdf, 0xc4, 0x57, 0xc1, 0xdb, 0x64, 0x01, 0xb8, 0x7f, 0x21, 0x80, 0x45, + 0x0b, 0xe0, 0x01, 0x6c, 0x50, 0xcf, 0x63, 0xb1, 0x78, 0x76, 0x7a, 0x38, 0x0d, 0xbc, 0x3d, 0xd3, + 0x4a, 0x6b, 0xa4, 0x08, 0xe3, 0xf7, 0xa0, 0x6d, 0x12, 0x5b, 0x50, 0x75, 0x69, 0x56, 0xf0, 0xff, + 0x5c, 0x7d, 0x17, 0x5a, 0x3a, 0x9f, 0x89, 0x96, 0xde, 0x26, 0xd9, 0xde, 0x7d, 0x85, 0xa0, 0x95, + 0x4e, 0xc3, 0x1b, 0x10, 0x3e, 0x2b, 0xd8, 0x41, 0xf6, 0x02, 0xa8, 0xe5, 0x0b, 0x96, 0xc1, 0xb8, + 0x0f, 0x6b, 0x8b, 0x91, 0x3d, 0xf1, 0x55, 0x5e, 0x36, 0x59, 0xc2, 0xca, 0x0b, 0xd5, 0xb8, 0xa4, + 0x50, 0xee, 0xb7, 0x5a, 0x3a, 0xf3, 0xcf, 0xe9, 0xaa, 0x5c, 0x9e, 0xc2, 0x86, 0x19, 0x58, 0x84, + 0xc5, 0x53, 0xea, 0x65, 0xd3, 0xe6, 0xed, 0x42, 0x59, 0xc9, 0x12, 0x8b, 0x14, 0xbd, 0xdc, 0xef, + 0x60, 0x7d, 0x99, 0x72, 0x03, 0x25, 0x5c, 0x74, 0x52, 0x96, 0x9b, 0xa9, 0xe1, 0x0a, 0xee, 0x0a, + 0xb8, 0x5d, 0xf6, 0x6f, 0xf5, 0xca, 0x28, 0x0a, 0xfd, 0x54, 0xfd, 0xd7, 0xfd, 0xd4, 0xdf, 0x86, + 0x8d, 0x82, 0x1d, 0xdb, 0xd0, 0xd8, 0xf6, 0x67, 0x41, 0xd8, 0xae, 0x60, 0x80, 0xe6, 0x57, 0x49, + 0x20, 0x58, 0xd2, 0x46, 0x72, 0x2d, 0x43, 0x65, 0x49, 0xbb, 0x8a, 0x1d, 0xb0, 0xb4, 0x34, 0x7e, + 0xbb, 0xd6, 0xff, 0xb9, 0x0a, 0xcd, 0x6b, 0x3f, 0xf9, 0xee, 0x81, 0x9d, 0x3d, 0xf0, 0xd2, 0x2b, + 0x9b, 0x01, 0xd7, 0x7e, 0xf0, 0x15, 0x9e, 0x6f, 0xf5, 0xeb, 0x3e, 0xdf, 0x1a, 0xd7, 0x7b, 0xbe, + 0x35, 0xaf, 0x7a, 0xbe, 0x59, 0x05, 0x0d, 0xba, 0x00, 0x01, 0xcf, 0x1e, 0x05, 0x2d, 0x35, 0x8e, + 0x73, 0x48, 0xff, 0x17, 0x24, 0x0b, 0xa7, 0xde, 0xd9, 0x77, 0xa0, 0x19, 0x27, 0xec, 0x6c, 0xe2, + 0x1b, 0x21, 0xcd, 0x6e, 0xe9, 0xf8, 0x6a, 0xe1, 0x78, 0x0c, 0x75, 0x5f, 0xe6, 0xa8, 0xdb, 0x46, + 0xad, 0x2f, 0x49, 0xae, 0x7e, 0xbd, 0xe4, 0x1a, 0x85, 0xe4, 0xfa, 0xbf, 0x22, 0x68, 0x8e, 0x95, + 0xb2, 0xf2, 0x43, 0xe0, 0x45, 0x90, 0x70, 0x91, 0x45, 0x98, 0x6e, 0x8d, 0x6e, 0xfb, 0x81, 0xb2, + 0x2d, 0x74, 0xd3, 0x80, 0xd4, 0xe3, 0x65, 0x94, 0x9c, 0xf0, 0x98, 0x7a, 0x0b, 0xd1, 0xf2, 0x10, + 0xfe, 0x00, 0x2c, 0x3f, 0xf2, 0xbe, 0x98, 0xc7, 0xcc, 0x4c, 0xbd, 0x5b, 0xaa, 0x4b, 0xf5, 0xef, + 0x0e, 0x9f, 0x68, 0x13, 0x49, 0x39, 0xfd, 0xfb, 0x60, 0x19, 0x0c, 0x5b, 0x50, 0xdb, 0xde, 0xdd, + 0x6f, 0x57, 0x64, 0xd3, 0x49, 0x2c, 0x61, 0xac, 0x8d, 0x76, 0xee, 0xbf, 0x3a, 0xef, 0xa2, 0xd7, + 0xe7, 0x5d, 0xf4, 0xe7, 0x79, 0x17, 0xfd, 0x78, 0xd1, 0xad, 0xbc, 0xbe, 0xe8, 0x56, 0x7e, 0xbf, + 0xe8, 0x56, 0xbe, 0x69, 0xa8, 0x4f, 0xaa, 0xc3, 0xa6, 0xfa, 0x82, 0x7a, 0xf4, 0x4f, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x14, 0x6d, 0xee, 0xf8, 0x75, 0x0d, 0x00, 0x00, } func (m *RawChange) Marshal() (dAtA []byte, err error) { @@ -2374,6 +2474,55 @@ func (m *Record) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *Header) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Header) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Header) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.DocType != 0 { + i = encodeVarintAclchanges(dAtA, i, uint64(m.DocType)) + i-- + dAtA[i] = 0x20 + } + if len(m.WorkspaceId) > 0 { + i -= len(m.WorkspaceId) + copy(dAtA[i:], m.WorkspaceId) + i = encodeVarintAclchanges(dAtA, i, uint64(len(m.WorkspaceId))) + i-- + dAtA[i] = 0x1a + } + if len(m.AclListId) > 0 { + i -= len(m.AclListId) + copy(dAtA[i:], m.AclListId) + i = encodeVarintAclchanges(dAtA, i, uint64(len(m.AclListId))) + i-- + dAtA[i] = 0x12 + } + if len(m.FirstId) > 0 { + i -= len(m.FirstId) + copy(dAtA[i:], m.FirstId) + i = encodeVarintAclchanges(dAtA, i, uint64(len(m.FirstId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func encodeVarintAclchanges(dAtA []byte, offset int, v uint64) int { offset -= sovAclchanges(v) base := offset @@ -2877,6 +3026,30 @@ func (m *Record) Size() (n int) { return n } +func (m *Header) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.FirstId) + if l > 0 { + n += 1 + l + sovAclchanges(uint64(l)) + } + l = len(m.AclListId) + if l > 0 { + n += 1 + l + sovAclchanges(uint64(l)) + } + l = len(m.WorkspaceId) + if l > 0 { + n += 1 + l + sovAclchanges(uint64(l)) + } + if m.DocType != 0 { + n += 1 + sovAclchanges(uint64(m.DocType)) + } + return n +} + func sovAclchanges(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -5930,6 +6103,171 @@ func (m *Record) Unmarshal(dAtA []byte) error { } return nil } +func (m *Header) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Header: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Header: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FirstId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAclchanges + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAclchanges + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.FirstId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AclListId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAclchanges + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAclchanges + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AclListId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field WorkspaceId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAclchanges + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAclchanges + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.WorkspaceId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field DocType", wireType) + } + m.DocType = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAclchanges + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.DocType |= HeaderDocType(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipAclchanges(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthAclchanges + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipAclchanges(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/pkg/acl/aclchanges/aclpb/protos/aclchanges.proto b/pkg/acl/aclchanges/aclpb/protos/aclchanges.proto index 5609ca2f..76177a7c 100644 --- a/pkg/acl/aclchanges/aclpb/protos/aclchanges.proto +++ b/pkg/acl/aclchanges/aclpb/protos/aclchanges.proto @@ -134,3 +134,15 @@ message Record { uint64 currentReadKeyHash = 4; int64 timestamp = 5; } + +message Header { + string firstId = 1; + string aclListId = 2; + string workspaceId = 3; + DocType docType = 4; + + enum DocType { + ACL = 0; + DocTree = 1; + } +} diff --git a/pkg/acl/acltree/aclstate.go b/pkg/acl/acltree/aclstate.go deleted file mode 100644 index a7443f69..00000000 --- a/pkg/acl/acltree/aclstate.go +++ /dev/null @@ -1,411 +0,0 @@ -package acltree - -import ( - "bytes" - "errors" - "fmt" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb" - "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/encryptionkey" - "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/signingkey" - "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/symmetric" - "hash/fnv" -) - -var ErrNoSuchUser = errors.New("no such user") -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") - -type ACLState struct { - currentReadKeyHash uint64 - userReadKeys map[uint64]*symmetric.Key - userStates map[string]*aclpb.ACLChangeUserState - userInvites map[string]*aclpb.ACLChangeUserInvite - signingPubKeyDecoder signingkey.PubKeyDecoder - encryptionKey encryptionkey.PrivKey - identity string -} - -func newACLState( - identity string, - encryptionKey encryptionkey.PrivKey, - signingPubKeyDecoder signingkey.PubKeyDecoder) *ACLState { - return &ACLState{ - identity: identity, - encryptionKey: encryptionKey, - userReadKeys: make(map[uint64]*symmetric.Key), - userStates: make(map[string]*aclpb.ACLChangeUserState), - userInvites: make(map[string]*aclpb.ACLChangeUserInvite), - signingPubKeyDecoder: signingPubKeyDecoder, - } -} - -func newACLStateFromSnapshotChange( - snapshotChange *aclpb.ACLChange, - identity string, - encryptionKey encryptionkey.PrivKey, - signingPubKeyDecoder signingkey.PubKeyDecoder) (*ACLState, error) { - st := &ACLState{ - identity: identity, - encryptionKey: encryptionKey, - userReadKeys: make(map[uint64]*symmetric.Key), - userStates: make(map[string]*aclpb.ACLChangeUserState), - userInvites: make(map[string]*aclpb.ACLChangeUserInvite), - signingPubKeyDecoder: signingPubKeyDecoder, - } - err := st.recreateFromSnapshotChange(snapshotChange) - if err != nil { - return nil, err - } - return st, nil -} - -func (st *ACLState) recreateFromSnapshotChange(snapshotChange *aclpb.ACLChange) error { - snapshot := snapshotChange.GetAclData().GetAclSnapshot() - if snapshot == nil { - return fmt.Errorf("could not create state from snapshot, because it is nil") - } - state := snapshot.AclState - for _, userState := range state.UserStates { - st.userStates[userState.Identity] = userState - } - - userState, exists := st.userStates[st.identity] - if !exists { - return ErrNoSuchUser - } - for _, key := range userState.EncryptedReadKeys { - key, hash, err := st.decryptReadKeyAndHash(key) - if err != nil { - return ErrFailedToDecrypt - } - - st.userReadKeys[hash] = key - } - st.currentReadKeyHash = snapshotChange.CurrentReadKeyHash - if snapshot.GetAclState().GetInvites() != nil { - st.userInvites = snapshot.GetAclState().GetInvites() - } - return nil -} - -func (st *ACLState) makeSnapshot() *aclpb.ACLChangeACLSnapshot { - var userStates []*aclpb.ACLChangeUserState - for _, st := range st.userStates { - userStates = append(userStates, st) - } - - return &aclpb.ACLChangeACLSnapshot{AclState: &aclpb.ACLChangeACLState{ - ReadKeyHashes: nil, - UserStates: userStates, // TODO: make states and invites in same format - Invites: st.userInvites, - }} -} - -func (st *ACLState) applyChange(change *aclpb.ACLChange) (err error) { - defer func() { - if err != nil { - return - } - st.currentReadKeyHash = change.CurrentReadKeyHash - }() - - // we can't check this for the user which is joining, because it will not be in our list - // 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 - return - } - - if !st.hasPermission(change.Identity, aclpb.ACLChange_Admin) { - err = fmt.Errorf("user %s must have admin permissions", change.Identity) - return - } - } - - for _, ch := range change.GetAclData().GetAclContent() { - if err = st.applyChangeContent(ch); err != nil { - //log.Infof("error while applying changes: %v; ignore", err) - return err - } - } - - return nil -} - -// TODO: remove changeId, because it is not needed -func (st *ACLState) applyChangeContent(ch *aclpb.ACLChangeACLContentValue) error { - switch { - case ch.GetUserPermissionChange() != nil: - return st.applyUserPermissionChange(ch.GetUserPermissionChange()) - case ch.GetUserAdd() != nil: - return st.applyUserAdd(ch.GetUserAdd()) - case ch.GetUserRemove() != nil: - return st.applyUserRemove(ch.GetUserRemove()) - case ch.GetUserInvite() != nil: - return st.applyUserInvite(ch.GetUserInvite()) - case ch.GetUserJoin() != nil: - return st.applyUserJoin(ch.GetUserJoin()) - case ch.GetUserConfirm() != nil: - return st.applyUserConfirm(ch.GetUserConfirm()) - default: - return fmt.Errorf("unexpected change type: %v", ch) - } -} - -func (st *ACLState) applyUserPermissionChange(ch *aclpb.ACLChangeUserPermissionChange) error { - if _, exists := st.userStates[ch.Identity]; !exists { - return ErrNoSuchUser - } - - st.userStates[ch.Identity].Permissions = ch.Permissions - return nil -} - -func (st *ACLState) applyUserInvite(ch *aclpb.ACLChangeUserInvite) error { - st.userInvites[ch.InviteId] = ch - return nil -} - -func (st *ACLState) applyUserJoin(ch *aclpb.ACLChangeUserJoin) error { - invite, exists := st.userInvites[ch.UserInviteId] - if !exists { - return fmt.Errorf("no such invite with id %s", ch.UserInviteId) - } - - if _, exists = st.userStates[ch.Identity]; exists { - return ErrUserAlreadyExists - } - - // validating signature - signature := ch.GetAcceptSignature() - verificationKey, err := st.signingPubKeyDecoder.DecodeFromBytes(invite.AcceptPublicKey) - if err != nil { - return fmt.Errorf("public key verifying invite accepts is given in incorrect format: %v", err) - } - - rawSignedId, err := st.signingPubKeyDecoder.DecodeFromStringIntoBytes(ch.Identity) - if err != nil { - return fmt.Errorf("failed to decode signing identity as bytes") - } - - res, err := verificationKey.Verify(rawSignedId, signature) - if err != nil { - return fmt.Errorf("verification returned error: %w", err) - } - if !res { - return fmt.Errorf("signature is invalid") - } - - // if ourselves -> we need to decrypt the read keys - if st.identity == ch.Identity { - for _, key := range ch.EncryptedReadKeys { - key, hash, err := st.decryptReadKeyAndHash(key) - if err != nil { - return ErrFailedToDecrypt - } - - st.userReadKeys[hash] = key - } - } - - // adding user to the list - userState := &aclpb.ACLChangeUserState{ - Identity: ch.Identity, - EncryptionKey: ch.EncryptionKey, - EncryptedReadKeys: ch.EncryptedReadKeys, - Permissions: invite.Permissions, - IsConfirmed: true, - } - st.userStates[ch.Identity] = userState - return nil -} - -func (st *ACLState) applyUserAdd(ch *aclpb.ACLChangeUserAdd) error { - if _, exists := st.userStates[ch.Identity]; exists { - return ErrUserAlreadyExists - } - - st.userStates[ch.Identity] = &aclpb.ACLChangeUserState{ - Identity: ch.Identity, - EncryptionKey: ch.EncryptionKey, - Permissions: ch.Permissions, - EncryptedReadKeys: ch.EncryptedReadKeys, - } - - if ch.Identity == st.identity { - for _, key := range ch.EncryptedReadKeys { - key, hash, err := st.decryptReadKeyAndHash(key) - if err != nil { - return ErrFailedToDecrypt - } - - st.userReadKeys[hash] = key - } - } - - return nil -} - -func (st *ACLState) applyUserRemove(ch *aclpb.ACLChangeUserRemove) error { - if ch.Identity == st.identity { - return ErrDocumentForbidden - } - - if _, exists := st.userStates[ch.Identity]; !exists { - return ErrNoSuchUser - } - - delete(st.userStates, ch.Identity) - - for _, replace := range ch.ReadKeyReplaces { - userState, exists := st.userStates[replace.Identity] - if !exists { - continue - } - - userState.EncryptedReadKeys = append(userState.EncryptedReadKeys, replace.EncryptedReadKey) - // if this is our identity then we have to decrypt the key - if replace.Identity == st.identity { - key, hash, err := st.decryptReadKeyAndHash(replace.EncryptedReadKey) - if err != nil { - return ErrFailedToDecrypt - } - - st.currentReadKeyHash = hash - st.userReadKeys[st.currentReadKeyHash] = key - } - } - return nil -} - -func (st *ACLState) applyUserConfirm(ch *aclpb.ACLChangeUserConfirm) error { - if _, exists := st.userStates[ch.Identity]; !exists { - return ErrNoSuchUser - } - - userState := st.userStates[ch.Identity] - userState.IsConfirmed = true - return nil -} - -func (st *ACLState) decryptReadKeyAndHash(msg []byte) (*symmetric.Key, uint64, error) { - decrypted, err := st.encryptionKey.Decrypt(msg) - if err != nil { - return nil, 0, ErrFailedToDecrypt - } - - key, err := symmetric.FromBytes(decrypted) - if err != nil { - return nil, 0, ErrFailedToDecrypt - } - - hasher := fnv.New64() - hasher.Write(decrypted) - return key, hasher.Sum64(), nil -} - -func (st *ACLState) hasPermission(identity string, permission aclpb.ACLChangeUserPermissions) bool { - state, exists := st.userStates[identity] - if !exists { - return false - } - - return state.Permissions == permission -} - -func (st *ACLState) isUserJoin(ch *aclpb.ACLChange) bool { - // if we have a UserJoin, then it should always be the first one applied - return ch.AclData.GetAclContent() != nil && ch.AclData.GetAclContent()[0].GetUserJoin() != nil -} - -func (st *ACLState) isUserAdd(ch *aclpb.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 *aclpb.ACLChange) (identities []*aclpb.ACLChangeUserPermissionChange) { - // this should be called after general checks are completed - if ch.GetAclData().GetAclContent() == nil { - return nil - } - - contents := ch.GetAclData().GetAclContent() - for _, c := range contents { - if c.GetUserPermissionChange() != nil { - content := c.GetUserPermissionChange() - - currentState := st.userStates[content.Identity] - // the comparison works in different direction :-) - if content.Permissions > currentState.Permissions { - identities = append(identities, &aclpb.ACLChangeUserPermissionChange{ - Identity: content.Identity, - Permissions: content.Permissions, - }) - } - } - if c.GetUserRemove() != nil { - content := c.GetUserRemove() - identities = append(identities, &aclpb.ACLChangeUserPermissionChange{ - Identity: content.Identity, - Permissions: aclpb.ACLChange_Removed, - }) - } - } - - return identities -} - -func (st *ACLState) equal(other *ACLState) bool { - if st == nil && other == nil { - return true - } - - if st == nil || other == nil { - return false - } - - if st.currentReadKeyHash != other.currentReadKeyHash { - return false - } - - if st.identity != other.identity { - return false - } - - if len(st.userStates) != len(other.userStates) { - return false - } - - for _, st := range st.userStates { - otherSt, exists := other.userStates[st.Identity] - if !exists { - return false - } - - if st.Permissions != otherSt.Permissions { - return false - } - - if bytes.Compare(st.EncryptionKey, otherSt.EncryptionKey) != 0 { - return false - } - } - - if len(st.userInvites) != len(other.userInvites) { - return false - } - - // TODO: add detailed user invites comparison + compare other stuff - return true -} - -func (st *ACLState) GetUserStates() map[string]*aclpb.ACLChangeUserState { - // TODO: we should provide better API that would not allow to change this map from the outside - return st.userStates -} diff --git a/pkg/acl/acltree/aclstatebuilder.go b/pkg/acl/acltree/aclstatebuilder.go deleted file mode 100644 index d63d535c..00000000 --- a/pkg/acl/acltree/aclstatebuilder.go +++ /dev/null @@ -1,186 +0,0 @@ -package acltree - -import ( - "fmt" - "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/util/keys/asymmetric/encryptionkey" - "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/signingkey" -) - -type aclStateBuilder struct { - tree *Tree - identity string - key encryptionkey.PrivKey - decoder signingkey.PubKeyDecoder -} - -type decreasedPermissionsParameters struct { - users []*aclpb.ACLChangeUserPermissionChange - startChange string -} - -func newACLStateBuilder(decoder signingkey.PubKeyDecoder, accountData *account.AccountData) *aclStateBuilder { - return &aclStateBuilder{ - decoder: decoder, - identity: accountData.Identity, - key: accountData.EncKey, - } -} - -func (sb *aclStateBuilder) Init(tree *Tree) error { - sb.tree = tree - return nil -} - -func (sb *aclStateBuilder) Build() (*ACLState, error) { - state, _, err := sb.BuildBefore("") - return state, err -} - -// TODO: we can probably have only one state builder, because we can Build both at the same time -func (sb *aclStateBuilder) BuildBefore(beforeId string) (*ACLState, bool, error) { - var ( - err error - startChange = sb.tree.root - foundId bool - idSeenMap = make(map[string][]*Change) - decreasedPermissions *decreasedPermissionsParameters - ) - root := sb.tree.Root() - if !root.IsSnapshot { - return nil, false, fmt.Errorf("root should always be a snapshot") - } - - state, err := newACLStateFromSnapshotChange( - root.Content, - sb.identity, - sb.key, - sb.decoder) - if err != nil { - return nil, false, fmt.Errorf("could not build ACLState from snapshot: %w", err) - } - - idSeenMap[startChange.Content.Identity] = append(idSeenMap[startChange.Content.Identity], startChange) - - if startChange.Content.GetChangesData() != nil { - key, exists := state.userReadKeys[startChange.Content.CurrentReadKeyHash] - if !exists { - return nil, false, fmt.Errorf("no first snapshot") - } - - err = startChange.DecryptContents(key) - if err != nil { - return nil, false, fmt.Errorf("failed to decrypt contents of first snapshot") - } - } - - if beforeId == startChange.Id { - return state, true, nil - } - - for { - // TODO: we should optimize this method to just remember last state of iterator and not iterate from the start and skip if nothing was removed from the Tree - sb.tree.IterateSkip(sb.tree.root.Id, startChange.Id, func(c *Change) (isContinue bool) { - defer func() { - if err == nil { - startChange = c - } else if err != ErrDocumentForbidden { - //log.Errorf("marking change %s as invalid: %v", c.Id, err) - sb.tree.RemoveInvalidChange(c.Id) - } - }() - - // not applying root change - if c.Id == startChange.Id { - return true - } - - idSeenMap[c.Content.Identity] = append(idSeenMap[c.Content.Identity], c) - if c.Content.GetAclData() != nil { - err = state.applyChange(c.Content) - if err != nil { - return false - } - - // if we have some users who have less permissions now - users := state.getPermissionDecreasedUsers(c.Content) - if len(users) > 0 { - decreasedPermissions = &decreasedPermissionsParameters{ - users: users, - startChange: c.Id, - } - return false - } - } - - // the user can't make changes - if !state.hasPermission(c.Content.Identity, aclpb.ACLChange_Writer) && !state.hasPermission(c.Content.Identity, aclpb.ACLChange_Admin) { - err = fmt.Errorf("user %s cannot make changes", c.Content.Identity) - return false - } - - // decrypting contents on the fly - if c.Content.GetChangesData() != nil { - key, exists := state.userReadKeys[c.Content.CurrentReadKeyHash] - if !exists { - err = fmt.Errorf("failed to find key with hash: %d", c.Content.CurrentReadKeyHash) - return false - } - - err = c.DecryptContents(key) - if err != nil { - err = fmt.Errorf("failed to decrypt contents for hash: %d", c.Content.CurrentReadKeyHash) - return false - } - } - - if c.Id == beforeId { - foundId = true - return false - } - - return true - }) - - // if we have users with decreased permissions - if decreasedPermissions != nil { - var removed bool - validChanges := sb.tree.dfs(decreasedPermissions.startChange) - - for _, permChange := range decreasedPermissions.users { - seenChanges := idSeenMap[permChange.Identity] - - for _, seen := range seenChanges { - // if we find some invalid changes - if _, exists := validChanges[seen.Id]; !exists { - // if the user didn't have enough permission to make changes - if seen.IsACLChange() || permChange.Permissions > aclpb.ACLChange_Writer { - removed = true - sb.tree.RemoveInvalidChange(seen.Id) - } - } - } - } - - decreasedPermissions = nil - if removed { - // starting from the beginning but with updated Tree - return sb.BuildBefore(beforeId) - } - } else if err == nil { - // we can finish the acl state building process - break - } - - // the user is forbidden to access the document - if err == ErrDocumentForbidden { - return nil, foundId, err - } - - // otherwise we have to continue from the change which we had - err = nil - } - - return state, foundId, err -} diff --git a/pkg/acl/acltree/acltree.go b/pkg/acl/acltree/acltree.go deleted file mode 100644 index 07acee6c..00000000 --- a/pkg/acl/acltree/acltree.go +++ /dev/null @@ -1,520 +0,0 @@ -package acltree - -import ( - "context" - "errors" - "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/treestorage" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/treestorage/treepb" - "go.uber.org/zap" - "sync" -) - -type AddResultSummary int - -const ( - AddResultSummaryNothing AddResultSummary = iota - AddResultSummaryAppend - AddResultSummaryRebuild -) - -type AddResult struct { - OldHeads []string - Heads []string - Added []*aclpb.RawChange - // TODO: add summary for changes - Summary AddResultSummary -} - -type TreeUpdateListener interface { - Update(tree ACLTree) - Rebuild(tree ACLTree) -} - -type NoOpListener struct{} - -func (n NoOpListener) Update(tree ACLTree) {} - -func (n NoOpListener) Rebuild(tree ACLTree) {} - -type RWLocker interface { - sync.Locker - RLock() - RUnlock() -} - -var ErrNoCommonSnapshot = errors.New("trees doesn't have a common snapshot") - -type ACLTree interface { - RWLocker - ID() string - Header() *treepb.TreeHeader - ACLState() *ACLState - AddContent(ctx context.Context, f func(builder ChangeBuilder) error) (*aclpb.RawChange, error) - AddRawChanges(ctx context.Context, changes ...*aclpb.RawChange) (AddResult, error) - Heads() []string - Root() *Change - Iterate(func(change *Change) bool) - IterateFrom(string, func(change *Change) bool) - HasChange(string) bool - SnapshotPath() []string - ChangesAfterCommonSnapshot(snapshotPath []string) ([]*aclpb.RawChange, error) - Storage() treestorage.TreeStorage - DebugDump() (string, error) - - Close() error -} - -type aclTree struct { - treeStorage treestorage.TreeStorage - accountData *account.AccountData - updateListener TreeUpdateListener - - id string - header *treepb.TreeHeader - fullTree *Tree - aclTreeFromStart *Tree // TODO: right now we don't use it, we can probably have only local var for now. This tree is built from start of the document - aclState *ACLState - - treeBuilder *treeBuilder - aclTreeBuilder *aclTreeBuilder - aclStateBuilder *aclStateBuilder - snapshotValidator *snapshotValidator - changeBuilder *changeBuilder - - sync.RWMutex -} - -func BuildACLTree( - t treestorage.TreeStorage, - acc *account.AccountData, - listener TreeUpdateListener) (ACLTree, error) { - aclTreeBuilder := newACLTreeBuilder(t, acc.Decoder) - treeBuilder := newTreeBuilder(t, acc.Decoder) - snapshotValidator := newSnapshotValidator(acc.Decoder, acc) // TODO: this looks weird, change it - aclStateBuilder := newACLStateBuilder(acc.Decoder, acc) - changeBuilder := newChangeBuilder() - - aclTree := &aclTree{ - treeStorage: t, - accountData: acc, - fullTree: nil, - aclState: nil, - treeBuilder: treeBuilder, - aclTreeBuilder: aclTreeBuilder, - aclStateBuilder: aclStateBuilder, - snapshotValidator: snapshotValidator, - changeBuilder: changeBuilder, - updateListener: listener, - } - err := aclTree.rebuildFromStorage(false) - if err != nil { - return nil, err - } - err = aclTree.removeOrphans() - if err != nil { - return nil, err - } - err = t.SetHeads(aclTree.Heads()) - if err != nil { - return nil, err - } - aclTree.id, err = t.TreeID() - if err != nil { - return nil, err - } - aclTree.header, err = t.Header() - if err != nil { - return nil, err - } - - listener.Rebuild(aclTree) - - return aclTree, nil -} - -// TODO: this is not used for now, in future we should think about not making full tree rebuild -//func (a *aclTree) rebuildFromTree(validateSnapshot bool) (err error) { -// if validateSnapshot { -// err = a.snapshotValidator.Init(a.aclTreeFromStart) -// if err != nil { -// return err -// } -// -// valid, err := a.snapshotValidator.ValidateSnapshot(a.fullTree.root) -// if err != nil { -// return err -// } -// if !valid { -// return a.rebuildFromStorage(true) -// } -// } -// -// err = a.aclStateBuilder.Init(a.fullTree) -// if err != nil { -// return err -// } -// -// a.aclState, err = a.aclStateBuilder.Build() -// if err != nil { -// return err -// } -// -// return nil -//} - -func (a *aclTree) removeOrphans() error { - // removing attached or invalid orphans - var toRemove []string - - orphans, err := a.treeStorage.Orphans() - if err != nil { - return err - } - for _, orphan := range orphans { - if _, exists := a.fullTree.attached[orphan]; exists { - toRemove = append(toRemove, orphan) - } - if _, exists := a.fullTree.invalidChanges[orphan]; exists { - toRemove = append(toRemove, orphan) - } - } - return a.treeStorage.RemoveOrphans(toRemove...) -} - -func (a *aclTree) rebuildFromStorage(fromStart bool) error { - a.treeBuilder.Init() - a.aclTreeBuilder.Init() - - var err error - a.fullTree, err = a.treeBuilder.Build(fromStart) - if err != nil { - return err - } - - // TODO: remove this from context as this is used only to validate snapshot - a.aclTreeFromStart, err = a.aclTreeBuilder.Build() - if err != nil { - return err - } - - if !fromStart { - err = a.snapshotValidator.Init(a.aclTreeFromStart) - if err != nil { - return err - } - - valid, err := a.snapshotValidator.ValidateSnapshot(a.fullTree.root) - if err != nil { - return err - } - if !valid { - return a.rebuildFromStorage(true) - } - } - // TODO: there is a question how we can validate not only that the full tree is built correctly - // but also that the ACL prev ids are not messed up. I think we should probably compare the resulting - // acl state with the acl state which is built in aclTreeFromStart - - err = a.aclStateBuilder.Init(a.fullTree) - if err != nil { - return err - } - - a.aclState, err = a.aclStateBuilder.Build() - if err != nil { - return err - } - - return nil -} - -func (a *aclTree) ID() string { - return a.id -} - -func (a *aclTree) Header() *treepb.TreeHeader { - return a.header -} - -func (a *aclTree) ACLState() *ACLState { - return a.aclState -} - -func (a *aclTree) Storage() treestorage.TreeStorage { - return a.treeStorage -} - -func (a *aclTree) AddContent(ctx context.Context, build func(builder ChangeBuilder) error) (*aclpb.RawChange, error) { - // TODO: add snapshot creation logic - defer func() { - // TODO: should this be called in a separate goroutine to prevent accidental cycles (tree->updater->tree) - a.updateListener.Update(a) - }() - - a.changeBuilder.Init(a.aclState, a.fullTree, a.accountData) - err := build(a.changeBuilder) - if err != nil { - return nil, err - } - - ch, marshalled, err := a.changeBuilder.BuildAndApply() - if err != nil { - return nil, err - } - a.fullTree.AddFast(ch) - rawCh := &aclpb.RawChange{ - Payload: marshalled, - Signature: ch.Signature(), - Id: ch.Id, - } - - err = a.treeStorage.AddRawChange(rawCh) - if err != nil { - return nil, err - } - - err = a.treeStorage.SetHeads([]string{ch.Id}) - if err != nil { - return nil, err - } - return rawCh, nil -} - -func (a *aclTree) AddRawChanges(ctx context.Context, rawChanges ...*aclpb.RawChange) (AddResult, error) { - // TODO: make proper error handling, because there are a lot of corner cases where this will break - var err error - var mode Mode - - var changes []*Change // TODO: = addChangesBuf[:0] ... - for _, ch := range rawChanges { - change, err := NewFromRawChange(ch) - // TODO: think what if we will have incorrect signatures on rawChanges, how everything will work - if err != nil { - continue - } - changes = append(changes, change) - } - - defer func() { - if err != nil { - return - } - - err = a.removeOrphans() - if err != nil { - return - } - - err = a.treeStorage.SetHeads(a.fullTree.Heads()) - if err != nil { - return - } - - switch mode { - case Append: - a.updateListener.Update(a) - case Rebuild: - a.updateListener.Rebuild(a) - default: - break - } - }() - - getAddedChanges := func() []*aclpb.RawChange { - var added []*aclpb.RawChange - for _, ch := range rawChanges { - if _, exists := a.fullTree.attached[ch.Id]; exists { - added = append(added, ch) - } - } - return added - } - - for _, ch := range changes { - err = a.treeStorage.AddChange(ch) - if err != nil { - return AddResult{}, err - } - err = a.treeStorage.AddOrphans(ch.Id) - if err != nil { - return AddResult{}, err - } - } - - prevHeads := a.fullTree.Heads() - mode = a.fullTree.Add(changes...) - switch mode { - case Nothing: - return AddResult{ - OldHeads: prevHeads, - Heads: prevHeads, - Summary: AddResultSummaryNothing, - }, nil - - case Rebuild: - err = a.rebuildFromStorage(false) - if err != nil { - return AddResult{}, err - } - - return AddResult{ - OldHeads: prevHeads, - Heads: a.fullTree.Heads(), - Added: getAddedChanges(), - Summary: AddResultSummaryRebuild, - }, nil - default: - // just rebuilding the state from start without reloading everything from tree storage - // as an optimization we could've started from current heads, but I didn't implement that - a.aclState, err = a.aclStateBuilder.Build() - if err != nil { - return AddResult{}, err - } - - return AddResult{ - OldHeads: prevHeads, - Heads: a.fullTree.Heads(), - Added: getAddedChanges(), - Summary: AddResultSummaryAppend, - }, nil - } -} - -func (a *aclTree) Iterate(f func(change *Change) bool) { - a.fullTree.Iterate(a.fullTree.RootId(), f) -} - -func (a *aclTree) IterateFrom(s string, f func(change *Change) bool) { - a.fullTree.Iterate(s, f) -} - -func (a *aclTree) HasChange(s string) bool { - _, attachedExists := a.fullTree.attached[s] - _, unattachedExists := a.fullTree.unAttached[s] - _, invalidExists := a.fullTree.invalidChanges[s] - return attachedExists || unattachedExists || invalidExists -} - -func (a *aclTree) Heads() []string { - return a.fullTree.Heads() -} - -func (a *aclTree) Root() *Change { - return a.fullTree.Root() -} - -func (a *aclTree) Close() error { - return nil -} - -func (a *aclTree) SnapshotPath() []string { - // TODO: think about caching this - - var path []string - // TODO: think that the user may have not all of the snapshots locally - currentSnapshotId := a.fullTree.RootId() - for currentSnapshotId != "" { - sn, err := a.treeBuilder.loadChange(currentSnapshotId) - if err != nil { - break - } - path = append(path, currentSnapshotId) - currentSnapshotId = sn.SnapshotId - } - return path -} - -func (a *aclTree) ChangesAfterCommonSnapshot(theirPath []string) ([]*aclpb.RawChange, error) { - // TODO: think about when the clients will have their full acl tree and thus full snapshots - // but no changes after some of the snapshots - - var ( - isNewDocument = len(theirPath) == 0 - ourPath = a.SnapshotPath() - // by default returning everything we have - commonSnapshot = ourPath[len(ourPath)-1] // TODO: root snapshot, probably it is better to have a specific method in treestorage - err error - ) - - // if this is non-empty request - if !isNewDocument { - commonSnapshot, err = a.commonSnapshotForTwoPaths(ourPath, theirPath) - if err != nil { - return nil, err - } - } - var rawChanges []*aclpb.RawChange - // using custom load function to skip verification step and save raw changes - load := func(id string) (*Change, error) { - raw, err := a.treeStorage.GetChange(context.Background(), id) - if err != nil { - return nil, err - } - - aclChange, err := a.treeBuilder.makeUnverifiedACLChange(raw) - if err != nil { - return nil, err - } - - ch := NewChange(id, aclChange) - rawChanges = append(rawChanges, raw) - return ch, nil - } - // we presume that we have everything after the common snapshot, though this may not be the case in case of clients and only ACL tree changes - log.With( - zap.Strings("heads", a.fullTree.Heads()), - zap.String("breakpoint", commonSnapshot), - zap.String("id", a.id)). - Debug("getting all changes from common snapshot") - _, err = a.treeBuilder.dfs(a.fullTree.Heads(), commonSnapshot, load) - if err != nil { - return nil, err - } - if isNewDocument { - // adding snapshot to raw changes - _, err = load(commonSnapshot) - if err != nil { - return nil, err - } - } - log.With( - zap.Int("len(changes)", len(rawChanges)), - zap.String("id", a.id)). - Debug("returning all changes after common snapshot") - - return rawChanges, nil -} - -func (a *aclTree) DebugDump() (string, error) { - return a.fullTree.Graph() -} - -func (a *aclTree) commonSnapshotForTwoPaths(ourPath []string, theirPath []string) (string, error) { - var i int - var j int - log.With(zap.Strings("our path", ourPath), zap.Strings("their path", theirPath)). - Debug("finding common snapshot for two paths") -OuterLoop: - // find starting point from the right - for i = len(ourPath) - 1; i >= 0; i-- { - for j = len(theirPath) - 1; j >= 0; j-- { - // most likely there would be only one comparison, because mostly the snapshot path will start from the root for nodes - if ourPath[i] == theirPath[j] { - break OuterLoop - } - } - } - if i < 0 || j < 0 { - return "", ErrNoCommonSnapshot - } - // find last common element of the sequence moving from right to left - for i >= 0 && j >= 0 { - if ourPath[i] == theirPath[j] { - i-- - j-- - } - } - return ourPath[i+1], nil -} diff --git a/pkg/acl/acltree/acltree_test.go b/pkg/acl/acltree/acltree_test.go deleted file mode 100644 index 8d89bac3..00000000 --- a/pkg/acl/acltree/acltree_test.go +++ /dev/null @@ -1,262 +0,0 @@ -package acltree - -import ( - "context" - "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/testutils/treestoragebuilder" - "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/signingkey" - "testing" - - "github.com/stretchr/testify/assert" -) - -type mockListener struct{} - -func (m *mockListener) Update(tree ACLTree) {} - -func (m *mockListener) Rebuild(tree ACLTree) {} - -func TestACLTree_UserJoinBuild(t *testing.T) { - thr, err := treestoragebuilder.NewTreeStorageBuilderWithTestName("userjoinexample.yml") - if err != nil { - t.Fatal(err) - } - keychain := thr.GetKeychain() - accountData := &account.AccountData{ - Identity: keychain.GetIdentity("A"), - SignKey: keychain.SigningKeys["A"], - EncKey: keychain.EncryptionKeys["A"], - Decoder: signingkey.NewEd25519PubKeyDecoder(), - } - listener := &mockListener{} - tree, err := BuildACLTree(thr, accountData, listener) - if err != nil { - t.Fatalf("should Build acl ACLState without err: %v", err) - } - aclState := tree.ACLState() - aId := keychain.GeneratedIdentities["A"] - bId := keychain.GeneratedIdentities["B"] - cId := keychain.GeneratedIdentities["C"] - - assert.Equal(t, aclState.identity, aId) - assert.Equal(t, aclState.userStates[aId].Permissions, aclpb.ACLChange_Admin) - assert.Equal(t, aclState.userStates[bId].Permissions, aclpb.ACLChange_Writer) - assert.Equal(t, aclState.userStates[cId].Permissions, aclpb.ACLChange_Reader) - - var changeIds []string - tree.Iterate(func(c *Change) (isContinue bool) { - changeIds = append(changeIds, c.Id) - return true - }) - assert.Equal(t, changeIds, []string{"A.1.1", "A.1.2", "B.1.1", "B.1.2"}) -} - -func TestACLTree_UserJoinUpdate_Append(t *testing.T) { - thr, err := treestoragebuilder.NewTreeStorageBuilderWithTestName("userjoinexampleupdate.yml") - if err != nil { - t.Fatal(err) - } - keychain := thr.GetKeychain() - accountData := &account.AccountData{ - Identity: keychain.GetIdentity("A"), - SignKey: keychain.SigningKeys["A"], - EncKey: keychain.EncryptionKeys["A"], - Decoder: signingkey.NewEd25519PubKeyDecoder(), - } - - listener := &mockListener{} - tree, err := BuildACLTree(thr, accountData, listener) - if err != nil { - t.Fatalf("should Build acl ACLState without err: %v", err) - } - rawChanges := thr.GetUpdates("append") - res, err := tree.AddRawChanges(context.Background(), rawChanges...) - assert.Equal(t, res.Summary, AddResultSummaryAppend) - - aclState := tree.ACLState() - aId := keychain.GeneratedIdentities["A"] - bId := keychain.GeneratedIdentities["B"] - cId := keychain.GeneratedIdentities["C"] - dId := keychain.GeneratedIdentities["D"] - - assert.Equal(t, aclState.identity, aId) - assert.Equal(t, aclState.userStates[aId].Permissions, aclpb.ACLChange_Admin) - assert.Equal(t, aclState.userStates[bId].Permissions, aclpb.ACLChange_Writer) - assert.Equal(t, aclState.userStates[cId].Permissions, aclpb.ACLChange_Reader) - assert.Equal(t, aclState.userStates[dId].Permissions, aclpb.ACLChange_Writer) - - var changeIds []string - tree.Iterate(func(c *Change) (isContinue bool) { - changeIds = append(changeIds, c.Id) - return true - }) - assert.Equal(t, changeIds, []string{"A.1.1", "A.1.2", "B.1.1", "B.1.2", "B.1.3", "A.1.4"}) -} - -func TestACLTree_UserJoinUpdate_Rebuild(t *testing.T) { - thr, err := treestoragebuilder.NewTreeStorageBuilderWithTestName("userjoinexampleupdate.yml") - if err != nil { - t.Fatal(err) - } - keychain := thr.GetKeychain() - accountData := &account.AccountData{ - Identity: keychain.GetIdentity("A"), - SignKey: keychain.SigningKeys["A"], - EncKey: keychain.EncryptionKeys["A"], - Decoder: signingkey.NewEd25519PubKeyDecoder(), - } - listener := &mockListener{} - tree, err := BuildACLTree(thr, accountData, listener) - if err != nil { - t.Fatalf("should Build acl ACLState without err: %v", err) - } - rawChanges := thr.GetUpdates("rebuild") - res, err := tree.AddRawChanges(context.Background(), rawChanges...) - assert.Equal(t, res.Summary, AddResultSummaryRebuild) - - aclState := tree.ACLState() - aId := keychain.GeneratedIdentities["A"] - bId := keychain.GeneratedIdentities["B"] - cId := keychain.GeneratedIdentities["C"] - dId := keychain.GeneratedIdentities["D"] - - assert.Equal(t, aclState.identity, aId) - assert.Equal(t, aclState.userStates[aId].Permissions, aclpb.ACLChange_Admin) - assert.Equal(t, aclState.userStates[bId].Permissions, aclpb.ACLChange_Writer) - assert.Equal(t, aclState.userStates[cId].Permissions, aclpb.ACLChange_Reader) - assert.Equal(t, aclState.userStates[dId].Permissions, aclpb.ACLChange_Writer) - - var changeIds []string - - tree.Iterate(func(c *Change) (isContinue bool) { - changeIds = append(changeIds, c.Id) - return true - }) - assert.Equal(t, changeIds, []string{"A.1.1", "A.1.2", "B.1.1", "B.1.2", "A.1.4"}) -} - -func TestACLTree_UserRemoveBuild(t *testing.T) { - thr, err := treestoragebuilder.NewTreeStorageBuilderWithTestName("userremoveexample.yml") - if err != nil { - t.Fatal(err) - } - keychain := thr.GetKeychain() - accountData := &account.AccountData{ - Identity: keychain.GetIdentity("A"), - SignKey: keychain.SigningKeys["A"], - EncKey: keychain.EncryptionKeys["A"], - Decoder: signingkey.NewEd25519PubKeyDecoder(), - } - listener := &mockListener{} - tree, err := BuildACLTree(thr, accountData, listener) - if err != nil { - t.Fatalf("should Build acl ACLState without err: %v", err) - } - aclState := tree.ACLState() - aId := keychain.GeneratedIdentities["A"] - - assert.Equal(t, aclState.identity, aId) - assert.Equal(t, aclState.userStates[aId].Permissions, aclpb.ACLChange_Admin) - - var changeIds []string - tree.Iterate(func(c *Change) (isContinue bool) { - changeIds = append(changeIds, c.Id) - return true - }) - assert.Equal(t, changeIds, []string{"A.1.1", "A.1.2", "B.1.1", "A.1.3", "A.1.4"}) -} - -func TestACLTree_UserRemoveBeforeBuild(t *testing.T) { - thr, err := treestoragebuilder.NewTreeStorageBuilderWithTestName("userremovebeforeexample.yml") - if err != nil { - t.Fatal(err) - } - keychain := thr.GetKeychain() - accountData := &account.AccountData{ - Identity: keychain.GetIdentity("A"), - SignKey: keychain.SigningKeys["A"], - EncKey: keychain.EncryptionKeys["A"], - Decoder: signingkey.NewEd25519PubKeyDecoder(), - } - listener := &mockListener{} - tree, err := BuildACLTree(thr, accountData, listener) - if err != nil { - t.Fatalf("should Build acl ACLState without err: %v", err) - } - aclState := tree.ACLState() - for _, s := range []string{"A", "C", "E"} { - assert.Equal(t, aclState.userStates[keychain.GetIdentity(s)].Permissions, aclpb.ACLChange_Admin) - } - assert.Equal(t, aclState.identity, keychain.GetIdentity("A")) - assert.Nil(t, aclState.userStates[keychain.GetIdentity("B")]) - - var changeIds []string - tree.Iterate(func(c *Change) (isContinue bool) { - changeIds = append(changeIds, c.Id) - return true - }) - assert.Equal(t, changeIds, []string{"A.1.1", "B.1.1", "A.1.2", "A.1.3"}) -} - -func TestACLTree_InvalidSnapshotBuild(t *testing.T) { - thr, err := treestoragebuilder.NewTreeStorageBuilderWithTestName("invalidsnapshotexample.yml") - if err != nil { - t.Fatal(err) - } - keychain := thr.GetKeychain() - accountData := &account.AccountData{ - Identity: keychain.GetIdentity("A"), - SignKey: keychain.SigningKeys["A"], - EncKey: keychain.EncryptionKeys["A"], - Decoder: signingkey.NewEd25519PubKeyDecoder(), - } - listener := &mockListener{} - tree, err := BuildACLTree(thr, accountData, listener) - if err != nil { - t.Fatalf("should Build acl ACLState without err: %v", err) - } - aclState := tree.ACLState() - for _, s := range []string{"A", "B", "C", "D", "E", "F"} { - assert.Equal(t, aclState.userStates[keychain.GetIdentity(s)].Permissions, aclpb.ACLChange_Admin) - } - assert.Equal(t, aclState.identity, keychain.GetIdentity("A")) - - var changeIds []string - tree.Iterate(func(c *Change) (isContinue bool) { - changeIds = append(changeIds, c.Id) - return true - }) - assert.Equal(t, []string{"A.1.1", "B.1.1", "A.1.2", "A.1.3", "B.1.2"}, changeIds) -} - -func TestACLTree_ValidSnapshotBuild(t *testing.T) { - thr, err := treestoragebuilder.NewTreeStorageBuilderWithTestName("validsnapshotexample.yml") - if err != nil { - t.Fatal(err) - } - keychain := thr.GetKeychain() - accountData := &account.AccountData{ - Identity: keychain.GetIdentity("A"), - SignKey: keychain.SigningKeys["A"], - EncKey: keychain.EncryptionKeys["A"], - Decoder: signingkey.NewEd25519PubKeyDecoder(), - } - listener := &mockListener{} - tree, err := BuildACLTree(thr, accountData, listener) - if err != nil { - t.Fatalf("should Build acl ACLState without err: %v", err) - } - aclState := tree.ACLState() - for _, s := range []string{"A", "B", "C", "D", "E", "F"} { - assert.Equal(t, aclState.userStates[keychain.GetIdentity(s)].Permissions, aclpb.ACLChange_Admin) - } - assert.Equal(t, aclState.identity, keychain.GetIdentity("A")) - - var changeIds []string - tree.Iterate(func(c *Change) (isContinue bool) { - changeIds = append(changeIds, c.Id) - return true - }) - assert.Equal(t, []string{"A.1.2", "A.1.3", "B.1.2"}, changeIds) -} diff --git a/pkg/acl/acltree/acltreebuilder.go b/pkg/acl/acltree/acltreebuilder.go deleted file mode 100644 index 71b5b304..00000000 --- a/pkg/acl/acltree/acltreebuilder.go +++ /dev/null @@ -1,154 +0,0 @@ -package acltree - -import ( - "fmt" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/treestorage" - "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/signingkey" - - "github.com/anytypeio/go-anytype-infrastructure-experiments/util/slice" -) - -type aclTreeBuilder struct { - cache map[string]*Change - identityKeys map[string]signingkey.PubKey - signingPubKeyDecoder signingkey.PubKeyDecoder - tree *Tree - treeStorage treestorage.TreeStorage - - *changeLoader -} - -func newACLTreeBuilder(t treestorage.TreeStorage, decoder signingkey.PubKeyDecoder) *aclTreeBuilder { - return &aclTreeBuilder{ - signingPubKeyDecoder: decoder, - treeStorage: t, - changeLoader: newChangeLoader( - t, - decoder, - NewACLChange), - } -} - -func (tb *aclTreeBuilder) Init() { - tb.cache = make(map[string]*Change) - tb.identityKeys = make(map[string]signingkey.PubKey) - tb.tree = &Tree{} - tb.changeLoader.Init(tb.cache, tb.identityKeys) -} - -func (tb *aclTreeBuilder) Build() (*Tree, error) { - var headsAndOrphans []string - orphans, err := tb.treeStorage.Orphans() - if err != nil { - return nil, err - } - heads, err := tb.treeStorage.Heads() - if err != nil { - return nil, err - } - headsAndOrphans = append(headsAndOrphans, orphans...) - headsAndOrphans = append(headsAndOrphans, heads...) - aclHeads, err := tb.getACLHeads(headsAndOrphans) - - if err != nil { - return nil, err - } - - if err = tb.buildTreeFromStart(aclHeads); err != nil { - return nil, fmt.Errorf("buildTree error: %v", err) - } - tb.cache = nil - - return tb.tree, nil -} - -func (tb *aclTreeBuilder) buildTreeFromStart(heads []string) (err error) { - changes, root, err := tb.dfsFromStart(heads) - if err != nil { - return err - } - - tb.tree.AddFast(root) - tb.tree.AddFast(changes...) - return -} - -func (tb *aclTreeBuilder) dfsFromStart(heads []string) (buf []*Change, root *Change, err error) { - var possibleRoots []*Change - stack := make([]string, len(heads), len(heads)*2) - copy(stack, heads) - - buf = make([]*Change, 0, len(stack)*2) - uniqMap := make(map[string]struct{}) - for len(stack) > 0 { - id := stack[len(stack)-1] - stack = stack[:len(stack)-1] - if _, exists := uniqMap[id]; exists { - continue - } - - ch, err := tb.loadChange(id) - if err != nil { - continue - } - - uniqMap[id] = struct{}{} - buf = append(buf, ch) - - for _, prev := range ch.PreviousIds { - stack = append(stack, prev) - } - if len(ch.PreviousIds) == 0 { - possibleRoots = append(possibleRoots, ch) - } - } - header, err := tb.treeStorage.Header() - if err != nil { - return nil, nil, err - } - - for _, r := range possibleRoots { - if r.Id == header.FirstChangeId { - return buf, r, nil - } - } - - return nil, nil, fmt.Errorf("could not find root change") -} - -func (tb *aclTreeBuilder) getACLHeads(heads []string) (aclTreeHeads []string, err error) { - for _, head := range heads { - if slice.FindPos(aclTreeHeads, head) != -1 { // do not scan known heads - continue - } - precedingHeads, err := tb.getPrecedingACLHeads(head) - if err != nil { - continue - } - - for _, aclHead := range precedingHeads { - if slice.FindPos(aclTreeHeads, aclHead) != -1 { - continue - } - aclTreeHeads = append(aclTreeHeads, aclHead) - } - } - - if len(aclTreeHeads) == 0 { - return nil, fmt.Errorf("no usable ACL heads in tree storage") - } - return aclTreeHeads, nil -} - -func (tb *aclTreeBuilder) getPrecedingACLHeads(head string) ([]string, error) { - headChange, err := tb.loadChange(head) - if err != nil { - return nil, err - } - - if headChange.Content.GetAclData() != nil { - return []string{head}, nil - } else { - return headChange.PreviousIds, nil - } -} diff --git a/pkg/acl/acltree/acltreestorage.go b/pkg/acl/acltree/acltreestorage.go deleted file mode 100644 index df03898a..00000000 --- a/pkg/acl/acltree/acltreestorage.go +++ /dev/null @@ -1,70 +0,0 @@ -package acltree - -import ( - "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/treestorage" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/treestorage/treepb" - "github.com/anytypeio/go-anytype-infrastructure-experiments/util/cid" - "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/signingkey" - "github.com/gogo/protobuf/proto" -) - -func CreateNewTreeStorageWithACL( - acc *account.AccountData, - build func(builder ChangeBuilder) error, - create treestorage.CreatorFunc) (treestorage.TreeStorage, error) { - bld := newChangeBuilder() - bld.Init( - newACLState(acc.Identity, acc.EncKey, signingkey.NewEd25519PubKeyDecoder()), - &Tree{}, - acc) - err := build(bld) - if err != nil { - return nil, err - } - bld.SetMakeSnapshot(true) - - change, payload, err := bld.BuildAndApply() - if err != nil { - return nil, err - } - - rawChange := &aclpb.RawChange{ - Payload: payload, - Signature: change.Signature(), - Id: change.CID(), - } - header, id, err := createTreeHeaderAndId(rawChange) - if err != nil { - return nil, err - } - - thr, err := create(id, header, []*aclpb.RawChange{rawChange}) - if err != nil { - return nil, err - } - - err = thr.SetHeads([]string{change.CID()}) - if err != nil { - return nil, err - } - return thr, nil -} - -func createTreeHeaderAndId(change *aclpb.RawChange) (*treepb.TreeHeader, string, error) { - header := &treepb.TreeHeader{ - FirstChangeId: change.Id, - IsWorkspace: false, - } - marshalledHeader, err := proto.Marshal(header) - if err != nil { - return nil, "", err - } - treeId, err := cid.NewCIDFromBytes(marshalledHeader) - if err != nil { - return nil, "", err - } - - return header, treeId, nil -} diff --git a/pkg/acl/acltree/acltreestorage_test.go b/pkg/acl/acltree/acltreestorage_test.go deleted file mode 100644 index 8730a542..00000000 --- a/pkg/acl/acltree/acltreestorage_test.go +++ /dev/null @@ -1,63 +0,0 @@ -package acltree - -import ( - "context" - "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/testutils/treestoragebuilder" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/treestorage" - "github.com/stretchr/testify/assert" - "testing" -) - -func Test_BuildTreeStorageWithACL(t *testing.T) { - keychain := treestoragebuilder.NewKeychain() - keychain.AddSigningKey("A") - keychain.AddEncryptionKey("A") - data := &account.AccountData{ - Identity: keychain.GetIdentity("A"), - SignKey: keychain.SigningKeys["A"], - EncKey: keychain.EncryptionKeys["A"], - } - thr, err := CreateNewTreeStorageWithACL( - data, - func(builder ChangeBuilder) error { - return builder.UserAdd( - keychain.GetIdentity("A"), - keychain.EncryptionKeys["A"].GetPublic(), - aclpb.ACLChange_Admin) - }, - treestorage.NewInMemoryTreeStorage) - if err != nil { - t.Fatalf("build should not return error") - } - - heads, err := thr.Heads() - if err != nil { - t.Fatalf("should return heads: %v", err) - } - if len(heads) == 0 { - t.Fatalf("tree storage should have non-empty heads") - } - - header, err := thr.Header() - if err != nil { - t.Fatalf("tree storage header should return without error: %v", err) - } - assert.Equal(t, heads[0], header.FirstChangeId) - - treeId, err := thr.TreeID() - if err != nil { - t.Fatalf("tree id should return without error: %v", err) - } - assert.NotEmpty(t, treeId) - ch, err := thr.GetChange(context.Background(), 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) - } -} diff --git a/pkg/acl/acltree/change.go b/pkg/acl/acltree/change.go deleted file mode 100644 index 7ad71209..00000000 --- a/pkg/acl/acltree/change.go +++ /dev/null @@ -1,97 +0,0 @@ -package acltree - -import ( - "fmt" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb" - "github.com/gogo/protobuf/proto" - - "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/symmetric" -) - -type ChangeContent struct { - ChangesData proto.Marshaler - ACLData *aclpb.ACLChangeACLData - Id string // TODO: this is just for testing, because id should be created automatically from content -} - -// Change is an abstract type for all types of changes -type Change struct { - Next []*Change - Unattached []*Change - PreviousIds []string - Id string - SnapshotId string - IsSnapshot bool - DecryptedDocumentChange []byte - - Content *aclpb.ACLChange - Sign []byte -} - -func (ch *Change) DecryptContents(key *symmetric.Key) error { - if ch.Content.ChangesData == nil { - return nil - } - - decrypted, err := key.Decrypt(ch.Content.ChangesData) - if err != nil { - return fmt.Errorf("failed to decrypt changes data: %w", err) - } - - ch.DecryptedDocumentChange = decrypted - return nil -} - -func (ch *Change) IsACLChange() bool { - return ch.Content.GetAclData() != nil -} - -func NewFromRawChange(rawChange *aclpb.RawChange) (*Change, error) { - unmarshalled := &aclpb.ACLChange{} - err := proto.Unmarshal(rawChange.Payload, unmarshalled) - if err != nil { - return nil, err - } - - ch := NewChange(rawChange.Id, unmarshalled) - ch.Sign = rawChange.Signature - return ch, nil -} - -func NewChange(id string, ch *aclpb.ACLChange) *Change { - return &Change{ - Next: nil, - PreviousIds: ch.TreeHeadIds, - Id: id, - Content: ch, - SnapshotId: ch.SnapshotBaseId, - IsSnapshot: ch.GetAclData().GetAclSnapshot() != nil, - } -} - -func NewACLChange(id string, ch *aclpb.ACLChange) *Change { - return &Change{ - Next: nil, - PreviousIds: ch.AclHeadIds, - Id: id, - Content: ch, - SnapshotId: ch.SnapshotBaseId, - IsSnapshot: ch.GetAclData().GetAclSnapshot() != nil, - } -} - -func (ch *Change) ProtoChange() proto.Marshaler { - return ch.Content -} - -func (ch *Change) DecryptedChangeContent() []byte { - return ch.DecryptedDocumentChange -} - -func (ch *Change) Signature() []byte { - return ch.Sign -} - -func (ch *Change) CID() string { - return ch.Id -} diff --git a/pkg/acl/acltree/changebuilder.go b/pkg/acl/acltree/changebuilder.go deleted file mode 100644 index b5b26b63..00000000 --- a/pkg/acl/acltree/changebuilder.go +++ /dev/null @@ -1,163 +0,0 @@ -package acltree - -import ( - "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/util/cid" - "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/encryptionkey" - "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/symmetric" - "github.com/gogo/protobuf/proto" - "hash/fnv" - "time" -) - -type MarshalledChange = []byte - -type ACLChangeBuilder interface { - UserAdd(identity string, encryptionKey encryptionkey.PubKey, permissions aclpb.ACLChangeUserPermissions) error - AddId(id string) // TODO: this is only for testing - SetMakeSnapshot(bool) // TODO: who should decide this? probably ACLTree so we can delete it -} - -type ChangeBuilder interface { - ACLChangeBuilder - AddChangeContent(marshaler proto.Marshaler) // user code should be responsible for making regular snapshots -} - -type changeBuilder struct { - aclState *ACLState - tree *Tree - acc *account.AccountData - - aclData *aclpb.ACLChangeACLData - changeContent proto.Marshaler - id string - makeSnapshot bool - readKey *symmetric.Key - readKeyHash uint64 -} - -func newChangeBuilder() *changeBuilder { - return &changeBuilder{} -} - -func (c *changeBuilder) Init(state *ACLState, tree *Tree, acc *account.AccountData) { - c.aclState = state - c.tree = tree - c.acc = acc - - c.aclData = &aclpb.ACLChangeACLData{} - // setting read key for further encryption etc - if state.currentReadKeyHash == 0 { - c.readKey, _ = symmetric.NewRandom() - - hasher := fnv.New64() - hasher.Write(c.readKey.Bytes()) - c.readKeyHash = hasher.Sum64() - } else { - c.readKey = c.aclState.userReadKeys[c.aclState.currentReadKeyHash] - c.readKeyHash = c.aclState.currentReadKeyHash - } -} - -func (c *changeBuilder) AddId(id string) { - c.id = id -} - -func (c *changeBuilder) SetMakeSnapshot(b bool) { - c.makeSnapshot = b -} - -func (c *changeBuilder) UserAdd(identity string, encryptionKey encryptionkey.PubKey, permissions aclpb.ACLChangeUserPermissions) error { - var allKeys []*symmetric.Key - if c.aclState.currentReadKeyHash != 0 { - for _, key := range c.aclState.userReadKeys { - allKeys = append(allKeys, key) - } - } else { - allKeys = append(allKeys, c.readKey) - } - - var encryptedKeys [][]byte - for _, k := range allKeys { - res, err := encryptionKey.Encrypt(k.Bytes()) - if err != nil { - return err - } - - encryptedKeys = append(encryptedKeys, res) - } - rawKey, err := encryptionKey.Raw() - if err != nil { - return err - } - ch := &aclpb.ACLChangeACLContentValue{ - Value: &aclpb.ACLChangeACLContentValueValueOfUserAdd{ - UserAdd: &aclpb.ACLChangeUserAdd{ - Identity: identity, - EncryptionKey: rawKey, - EncryptedReadKeys: encryptedKeys, - Permissions: permissions, - }, - }, - } - c.aclData.AclContent = append(c.aclData.AclContent, ch) - return nil -} - -func (c *changeBuilder) BuildAndApply() (*Change, []byte, error) { - aclChange := &aclpb.ACLChange{ - TreeHeadIds: c.tree.Heads(), - AclHeadIds: c.tree.ACLHeads(), - SnapshotBaseId: c.tree.RootId(), - AclData: c.aclData, - CurrentReadKeyHash: c.readKeyHash, - Timestamp: int64(time.Now().Nanosecond()), - Identity: c.acc.Identity, - } - err := c.aclState.applyChange(aclChange) - if err != nil { - return nil, nil, err - } - - if c.makeSnapshot { - 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 - } - signature, err := c.acc.SignKey.Sign(fullMarshalledChange) - if err != nil { - return nil, nil, err - } - id, err := cid.NewCIDFromBytes(fullMarshalledChange) - if err != nil { - return nil, nil, err - } - ch := NewChange(id, aclChange) - ch.DecryptedDocumentChange = marshalled - ch.Sign = signature - - return ch, fullMarshalledChange, nil -} - -func (c *changeBuilder) AddChangeContent(marshaler proto.Marshaler) { - c.changeContent = marshaler -} diff --git a/pkg/acl/acltree/changeloader.go b/pkg/acl/acltree/changeloader.go deleted file mode 100644 index b308397a..00000000 --- a/pkg/acl/acltree/changeloader.go +++ /dev/null @@ -1,99 +0,0 @@ -package acltree - -import ( - "context" - "fmt" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/treestorage" - "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/signingkey" - "time" - - "github.com/gogo/protobuf/proto" -) - -type changeLoader struct { - cache map[string]*Change - identityKeys map[string]signingkey.PubKey - signingPubKeyDecoder signingkey.PubKeyDecoder - treeStorage treestorage.TreeStorage - changeCreator func(id string, ch *aclpb.ACLChange) *Change -} - -func newChangeLoader( - treeStorage treestorage.TreeStorage, - signingPubKeyDecoder signingkey.PubKeyDecoder, - changeCreator func(id string, ch *aclpb.ACLChange) *Change) *changeLoader { - return &changeLoader{ - signingPubKeyDecoder: signingPubKeyDecoder, - treeStorage: treeStorage, - changeCreator: changeCreator, - } -} - -func (c *changeLoader) Init(cache map[string]*Change, - identityKeys map[string]signingkey.PubKey) { - c.cache = cache - c.identityKeys = identityKeys -} - -func (c *changeLoader) loadChange(id string) (ch *Change, err error) { - if ch, ok := c.cache[id]; ok { - return ch, nil - } - - // TODO: Add virtual changes logic - ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) - defer cancel() - - change, err := c.treeStorage.GetChange(ctx, id) - if err != nil { - return nil, err - } - - aclChange, err := c.makeVerifiedACLChange(change) - if err != nil { - return nil, err - } - - ch = c.changeCreator(id, aclChange) - c.cache[id] = ch - - return ch, nil -} - -func (c *changeLoader) verify(identity string, payload, signature []byte) (isVerified bool, err error) { - identityKey, exists := c.identityKeys[identity] - if !exists { - identityKey, err = c.signingPubKeyDecoder.DecodeFromString(identity) - if err != nil { - return - } - c.identityKeys[identity] = identityKey - } - return identityKey.Verify(payload, signature) -} - -func (c *changeLoader) makeVerifiedACLChange(change *aclpb.RawChange) (aclChange *aclpb.ACLChange, err error) { - aclChange = new(aclpb.ACLChange) - - // TODO: think what should we do with such cases, because this can be used by attacker to break our Tree - if err = proto.Unmarshal(change.Payload, aclChange); err != nil { - return - } - var verified bool - verified, err = c.verify(aclChange.Identity, change.Payload, change.Signature) - if err != nil { - return - } - if !verified { - err = fmt.Errorf("the signature of the payload cannot be verified") - return - } - return -} - -func (c *changeLoader) makeUnverifiedACLChange(change *aclpb.RawChange) (aclChange *aclpb.ACLChange, err error) { - aclChange = new(aclpb.ACLChange) - err = proto.Unmarshal(change.Payload, aclChange) - return -} diff --git a/pkg/acl/acltree/doctree.go b/pkg/acl/acltree/doctree.go deleted file mode 100644 index 50b92e27..00000000 --- a/pkg/acl/acltree/doctree.go +++ /dev/null @@ -1,8 +0,0 @@ -package acltree - -type DocTree interface { -} - -type docTree struct { - tree *Tree -} diff --git a/pkg/acl/acltree/snapshotvalidator.go b/pkg/acl/acltree/snapshotvalidator.go deleted file mode 100644 index 8df94d32..00000000 --- a/pkg/acl/acltree/snapshotvalidator.go +++ /dev/null @@ -1,50 +0,0 @@ -package acltree - -import ( - "fmt" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/account" - "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/encryptionkey" - "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/signingkey" -) - -type snapshotValidator struct { - aclTree *Tree - identity string - key encryptionkey.PrivKey - decoder signingkey.PubKeyDecoder - stateBuilder *aclStateBuilder -} - -func newSnapshotValidator( - decoder signingkey.PubKeyDecoder, - accountData *account.AccountData) *snapshotValidator { - return &snapshotValidator{ - identity: accountData.Identity, - key: accountData.EncKey, - decoder: decoder, - stateBuilder: newACLStateBuilder(decoder, accountData), - } -} - -func (s *snapshotValidator) Init(aclTree *Tree) error { - s.aclTree = aclTree - return s.stateBuilder.Init(aclTree) -} - -func (s *snapshotValidator) ValidateSnapshot(ch *Change) (bool, error) { - st, found, err := s.stateBuilder.BuildBefore(ch.Id) - if err != nil { - return false, err - } - - if !found { - return false, fmt.Errorf("didn't find snapshot in ACL Tree") - } - - otherSt, err := newACLStateFromSnapshotChange(ch.Content, s.identity, s.key, s.decoder) - if err != nil { - return false, err - } - - return st.equal(otherSt), nil -} diff --git a/pkg/acl/acltree/tree.go b/pkg/acl/acltree/tree.go deleted file mode 100644 index e47ed212..00000000 --- a/pkg/acl/acltree/tree.go +++ /dev/null @@ -1,416 +0,0 @@ -package acltree - -import ( - "bytes" - "crypto/md5" - "fmt" - "github.com/anytypeio/go-anytype-infrastructure-experiments/util/slice" - "sort" -) - -type Mode int - -const ( - Append Mode = iota - Rebuild - Nothing -) - -// TODO: consider abstracting into separate package with iterator, remove -type Tree struct { - root *Change - headIds []string - metaHeadIds []string - attached map[string]*Change - unAttached map[string]*Change - // missed id -> list of dependency ids - waitList map[string][]string - invalidChanges map[string]struct{} - - // bufs - iterCompBuf []*Change - iterQueue []*Change - - duplicateEvents int -} - -func (t *Tree) GetUnattachedChanges(changes ...*Change) []*Change { - return nil -} - -func (t *Tree) RootId() string { - if t.root != nil { - return t.root.Id - } - return "" -} - -func (t *Tree) Root() *Change { - return t.root -} - -func (t *Tree) AddFast(changes ...*Change) { - for _, c := range changes { - // ignore existing - if _, ok := t.attached[c.Id]; ok { - continue - } else if _, ok := t.unAttached[c.Id]; ok { - continue - } - t.add(c) - } - t.updateHeads() -} - -func (t *Tree) Add(changes ...*Change) (mode Mode) { - var beforeHeadIds = t.headIds - var attached bool - var empty = t.Len() == 0 - for _, c := range changes { - // ignore existing - if _, ok := t.attached[c.Id]; ok { - continue - } else if _, ok := t.unAttached[c.Id]; ok { - continue - } - if t.add(c) { - attached = true - } - } - if !attached { - return Nothing - } - t.updateHeads() - if empty { - return Rebuild - } - for _, hid := range beforeHeadIds { - for _, newCh := range changes { - if _, ok := t.attached[newCh.Id]; ok { - if !t.after(newCh.Id, hid) { - return Rebuild - } - } - } - } - return Append -} - -func (t *Tree) RemoveInvalidChange(id string) { - stack := []string{id} - // removing all children of this id (either next or unattached) - for len(stack) > 0 { - var exists bool - top := stack[len(stack)-1] - stack = stack[:len(stack)-1] - - if _, exists = t.invalidChanges[top]; exists { - continue - } - - var rem *Change - t.invalidChanges[top] = struct{}{} - if rem, exists = t.unAttached[top]; exists { - delete(t.unAttached, top) - } else if rem, exists = t.attached[top]; exists { - // remove from all prev changes - for _, id := range rem.PreviousIds { - prev, exists := t.attached[id] - if !exists { - continue - } - for i, next := range prev.Next { - if next.Id == top { - prev.Next[i] = nil - prev.Next = append(prev.Next[:i], prev.Next[i+1:]...) - break - } - } - } - delete(t.attached, top) - } - for _, el := range rem.Unattached { - stack = append(stack, el.Id) - } - for _, el := range rem.Next { - stack = append(stack, el.Id) - } - } - t.updateHeads() -} - -func (t *Tree) add(c *Change) (attached bool) { - if c == nil { - return false - } - if _, exists := t.invalidChanges[c.Id]; exists { - return false - } - - if t.root == nil { // first element - t.root = c - t.attached = map[string]*Change{ - c.Id: c, - } - t.unAttached = make(map[string]*Change) - t.waitList = make(map[string][]string) - t.invalidChanges = make(map[string]struct{}) - return true - } - if len(c.PreviousIds) > 1 { - sort.Strings(c.PreviousIds) - } - // attaching only if all prev ids are attached - attached = true - for _, pid := range c.PreviousIds { - if prev, ok := t.attached[pid]; ok { - prev.Unattached = append(prev.Unattached, c) - continue - } - attached = false - if prev, ok := t.unAttached[pid]; ok { - prev.Unattached = append(prev.Unattached, c) - continue - } - wl := t.waitList[pid] - wl = append(wl, c.Id) - t.waitList[pid] = wl - } - if attached { - t.attach(c, true) - } else { - // clearing wait list - for _, wid := range t.waitList[c.Id] { - c.Unattached = append(c.Unattached, t.unAttached[wid]) - } - delete(t.waitList, c.Id) - t.unAttached[c.Id] = c - } - return -} - -func (t *Tree) canAttach(c *Change) (attach bool) { - if c == nil { - return false - } - attach = true - for _, id := range c.PreviousIds { - if _, exists := t.attached[id]; !exists { - attach = false - } - } - return -} - -func (t *Tree) attach(c *Change, newEl bool) { - t.attached[c.Id] = c - if !newEl { - delete(t.unAttached, c.Id) - } - - // add next to all prev changes - for _, id := range c.PreviousIds { - // prev id must be attached if we attach this id - prev := t.attached[id] - prev.Next = append(prev.Next, c) - if len(prev.Next) > 1 { - sort.Sort(sortChanges(prev.Next)) - } - for i, next := range prev.Unattached { - if next.Id == c.Id { - prev.Unattached[i] = nil - prev.Unattached = append(prev.Unattached[:i], prev.Unattached[i+1:]...) - break - } - } - } - - // clearing wait list - if waitIds, ok := t.waitList[c.Id]; ok { - for _, wid := range waitIds { - next := t.unAttached[wid] - if t.canAttach(next) { - t.attach(next, false) - } - } - delete(t.waitList, c.Id) - } - - for _, next := range c.Unattached { - if t.canAttach(next) { - t.attach(next, false) - } - } -} - -func (t *Tree) after(id1, id2 string) (found bool) { - t.iterate(t.attached[id2], func(c *Change) (isContinue bool) { - if c.Id == id1 { - found = true - return false - } - return true - }) - return -} - -func (t *Tree) dfs(startChange string) (uniqMap map[string]*Change) { - stack := make([]*Change, 0, 10) - stack = append(stack, t.attached[startChange]) - uniqMap = map[string]*Change{} - - for len(stack) > 0 { - ch := stack[len(stack)-1] - stack = stack[:len(stack)-1] - if _, exists := uniqMap[ch.Id]; exists { - continue - } - - uniqMap[ch.Id] = ch - - for _, prev := range ch.PreviousIds { - stack = append(stack, t.attached[prev]) - } - } - return uniqMap -} - -func (t *Tree) updateHeads() { - var newHeadIds, newMetaHeadIds []string - t.iterate(t.root, func(c *Change) (isContinue bool) { - if len(c.Next) == 0 { - newHeadIds = append(newHeadIds, c.Id) - } - return true - }) - t.headIds = newHeadIds - t.metaHeadIds = newMetaHeadIds - sort.Strings(t.headIds) - sort.Strings(t.metaHeadIds) -} - -func (t *Tree) ACLHeads() []string { - var aclTreeHeads []string - for _, head := range t.Heads() { - if slice.FindPos(aclTreeHeads, head) != -1 { // do not scan known heads - continue - } - precedingHeads := t.getPrecedingACLHeads(head) - - for _, aclHead := range precedingHeads { - if slice.FindPos(aclTreeHeads, aclHead) != -1 { - continue - } - aclTreeHeads = append(aclTreeHeads, aclHead) - } - } - return aclTreeHeads -} - -func (t *Tree) getPrecedingACLHeads(head string) []string { - headChange := t.attached[head] - - if headChange.Content.GetAclData() != nil { - return []string{head} - } else { - return headChange.Content.AclHeadIds - } -} - -func (t *Tree) iterate(start *Change, f func(c *Change) (isContinue bool)) { - it := newIterator() - defer freeIterator(it) - it.iterate(start, f) -} - -func (t *Tree) iterateSkip(start *Change, skipBefore *Change, f func(c *Change) (isContinue bool)) { - it := newIterator() - defer freeIterator(it) - it.iterateSkip(start, skipBefore, f) -} - -func (t *Tree) IterateSkip(startId string, skipBeforeId string, f func(c *Change) (isContinue bool)) { - it := newIterator() - defer freeIterator(it) - it.iterateSkip(t.attached[startId], t.attached[skipBeforeId], f) -} - -func (t *Tree) Iterate(startId string, f func(c *Change) (isContinue bool)) { - t.iterate(t.attached[startId], f) -} - -func (t *Tree) IterateBranching(startId string, f func(c *Change, branchLevel int) (isContinue bool)) { - // branchLevel indicates the number of parallel branches - var bc int - t.iterate(t.attached[startId], func(c *Change) (isContinue bool) { - if pl := len(c.PreviousIds); pl > 1 { - bc -= pl - 1 - } - bl := bc - if nl := len(c.Next); nl > 1 { - bc += nl - 1 - } - return f(c, bl) - }) -} - -func (t *Tree) Hash() string { - h := md5.New() - n := 0 - t.iterate(t.root, func(c *Change) (isContinue bool) { - n++ - fmt.Fprintf(h, "-%s", c.Id) - return true - }) - return fmt.Sprintf("%d-%x", n, h.Sum(nil)) -} - -func (t *Tree) GetDuplicateEvents() int { - return t.duplicateEvents -} - -func (t *Tree) ResetDuplicateEvents() { - t.duplicateEvents = 0 -} - -func (t *Tree) Len() int { - return len(t.attached) -} - -func (t *Tree) Heads() []string { - return t.headIds -} - -func (t *Tree) String() string { - var buf = bytes.NewBuffer(nil) - t.Iterate(t.RootId(), func(c *Change) (isContinue bool) { - buf.WriteString(c.Id) - if len(c.Next) > 1 { - buf.WriteString("-<") - } else if len(c.Next) > 0 { - buf.WriteString("->") - } else { - buf.WriteString("-|") - } - return true - }) - return buf.String() -} - -func (t *Tree) Get(id string) *Change { - return t.attached[id] -} - -type sortChanges []*Change - -func (s sortChanges) Len() int { - return len(s) -} - -func (s sortChanges) Less(i, j int) bool { - return s[i].Id < s[j].Id -} - -func (s sortChanges) Swap(i, j int) { - s[i], s[j] = s[j], s[i] -} diff --git a/pkg/acl/acltree/treebuilder.go b/pkg/acl/acltree/treebuilder.go deleted file mode 100644 index 9bb9a4b8..00000000 --- a/pkg/acl/acltree/treebuilder.go +++ /dev/null @@ -1,307 +0,0 @@ -package acltree - -import ( - "errors" - "fmt" - "github.com/anytypeio/go-anytype-infrastructure-experiments/app/logger" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/treestorage" - "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/signingkey" - "github.com/anytypeio/go-anytype-infrastructure-experiments/util/slice" -) - -var ( - log = logger.NewNamed("acltree").Sugar() - ErrEmpty = errors.New("logs empty") -) - -type treeBuilder struct { - cache map[string]*Change - identityKeys map[string]signingkey.PubKey - signingPubKeyDecoder signingkey.PubKeyDecoder - tree *Tree - treeStorage treestorage.TreeStorage - - *changeLoader -} - -func newTreeBuilder(t treestorage.TreeStorage, decoder signingkey.PubKeyDecoder) *treeBuilder { - return &treeBuilder{ - signingPubKeyDecoder: decoder, - treeStorage: t, - changeLoader: newChangeLoader( - t, - decoder, - NewChange), - } -} - -func (tb *treeBuilder) Init() { - tb.cache = make(map[string]*Change) - tb.identityKeys = make(map[string]signingkey.PubKey) - tb.tree = &Tree{} - tb.changeLoader.Init(tb.cache, tb.identityKeys) -} - -func (tb *treeBuilder) Build(fromStart bool) (*Tree, error) { - var headsAndOrphans []string - orphans, err := tb.treeStorage.Orphans() - if err != nil { - return nil, err - } - heads, err := tb.treeStorage.Heads() - if err != nil { - return nil, err - } - headsAndOrphans = append(headsAndOrphans, orphans...) - headsAndOrphans = append(headsAndOrphans, heads...) - - if fromStart { - if err := tb.buildTreeFromStart(headsAndOrphans); err != nil { - return nil, fmt.Errorf("buildTree error: %v", err) - } - } else { - breakpoint, err := tb.findBreakpoint(headsAndOrphans) - if err != nil { - return nil, fmt.Errorf("findBreakpoint error: %v", err) - } - - if err = tb.buildTree(headsAndOrphans, breakpoint); err != nil { - return nil, fmt.Errorf("buildTree error: %v", err) - } - } - - tb.cache = nil - - return tb.tree, nil -} - -func (tb *treeBuilder) buildTreeFromStart(heads []string) (err error) { - changes, root, err := tb.dfsFromStart(heads) - if err != nil { - return err - } - - tb.tree.AddFast(root) - tb.tree.AddFast(changes...) - return -} - -func (tb *treeBuilder) dfsFromStart(heads []string) (buf []*Change, root *Change, err error) { - var possibleRoots []*Change - stack := make([]string, len(heads), len(heads)*2) - copy(stack, heads) - - buf = make([]*Change, 0, len(stack)*2) - uniqMap := make(map[string]struct{}) - for len(stack) > 0 { - id := stack[len(stack)-1] - stack = stack[:len(stack)-1] - if _, exists := uniqMap[id]; exists { - continue - } - - ch, err := tb.loadChange(id) - if err != nil { - continue - } - - uniqMap[id] = struct{}{} - buf = append(buf, ch) - - for _, prev := range ch.PreviousIds { - stack = append(stack, prev) - } - if len(ch.PreviousIds) == 0 { - possibleRoots = append(possibleRoots, ch) - } - } - header, err := tb.treeStorage.Header() - if err != nil { - return nil, nil, err - } - for _, r := range possibleRoots { - if r.Id == header.FirstChangeId { - return buf, r, nil - } - } - - return nil, nil, fmt.Errorf("could not find root change") -} - -func (tb *treeBuilder) buildTree(heads []string, breakpoint string) (err error) { - ch, err := tb.loadChange(breakpoint) - if err != nil { - return - } - tb.tree.AddFast(ch) - changes, err := tb.dfs(heads, breakpoint, tb.loadChange) - - tb.tree.AddFast(changes...) - return -} - -func (tb *treeBuilder) dfs( - heads []string, - breakpoint string, - load func(string) (*Change, error)) (buf []*Change, err error) { - stack := make([]string, len(heads), len(heads)*2) - copy(stack, heads) - - buf = make([]*Change, 0, len(stack)*2) - uniqMap := map[string]struct{}{breakpoint: {}} - for len(stack) > 0 { - id := stack[len(stack)-1] - stack = stack[:len(stack)-1] - if _, exists := uniqMap[id]; exists { - continue - } - - ch, err := load(id) - if err != nil { - continue - } - - uniqMap[id] = struct{}{} - buf = append(buf, ch) - - for _, prev := range ch.PreviousIds { - stack = append(stack, prev) - } - } - return buf, nil -} - -func (tb *treeBuilder) findBreakpoint(heads []string) (breakpoint string, err error) { - var ( - ch *Change - snapshotIds []string - ) - for _, head := range heads { - if ch, err = tb.loadChange(head); err != nil { - return - } - shId := ch.SnapshotId - if ch.IsSnapshot { - shId = ch.Id - } - if slice.FindPos(snapshotIds, shId) == -1 { - snapshotIds = append(snapshotIds, shId) - } - } - return tb.findCommonSnapshot(snapshotIds) -} - -func (tb *treeBuilder) findCommonSnapshot(snapshotIds []string) (snapshotId string, err error) { - if len(snapshotIds) == 1 { - return snapshotIds[0], nil - } else if len(snapshotIds) == 0 { - return "", fmt.Errorf("snapshots not found") - } - - for len(snapshotIds) > 1 { - l := len(snapshotIds) - shId, e := tb.findCommonForTwoSnapshots(snapshotIds[l-2], snapshotIds[l-1]) - if e != nil { - return "", e - } - snapshotIds[l-2] = shId - snapshotIds = snapshotIds[:l-1] - } - return snapshotIds[0], nil -} - -func (tb *treeBuilder) findCommonForTwoSnapshots(s1, s2 string) (s string, err error) { - // fast cases - if s1 == s2 { - return s1, nil - } - ch1, err := tb.loadChange(s1) - if err != nil { - return "", err - } - if ch1.SnapshotId == s2 { - return s2, nil - } - ch2, err := tb.loadChange(s2) - if err != nil { - return "", err - } - if ch2.SnapshotId == s1 { - return s1, nil - } - if ch1.SnapshotId == ch2.SnapshotId && ch1.SnapshotId != "" { - return ch1.SnapshotId, nil - } - // traverse - var t1 = make([]string, 0, 5) - var t2 = make([]string, 0, 5) - t1 = append(t1, ch1.Id, ch1.SnapshotId) - t2 = append(t2, ch2.Id, ch2.SnapshotId) - for { - lid1 := t1[len(t1)-1] - if lid1 != "" { - l1, e := tb.loadChange(lid1) - if e != nil { - return "", e - } - if l1.SnapshotId != "" { - if slice.FindPos(t2, l1.SnapshotId) != -1 { - return l1.SnapshotId, nil - } - } - t1 = append(t1, l1.SnapshotId) - } - lid2 := t2[len(t2)-1] - if lid2 != "" { - l2, e := tb.loadChange(t2[len(t2)-1]) - if e != nil { - return "", e - } - if l2.SnapshotId != "" { - if slice.FindPos(t1, l2.SnapshotId) != -1 { - return l2.SnapshotId, nil - } - } - t2 = append(t2, l2.SnapshotId) - } - if lid1 == "" && lid2 == "" { - break - } - } - - log.Warnf("changes build Tree: possible versions split") - - // prefer not first snapshot - if len(ch1.PreviousIds) == 0 && len(ch2.PreviousIds) > 0 { - log.Warnf("changes build Tree: prefer %s(%d prevIds) over %s(%d prevIds)", s2, len(ch2.PreviousIds), s1, len(ch1.PreviousIds)) - return s2, nil - } else if len(ch1.PreviousIds) > 0 && len(ch2.PreviousIds) == 0 { - log.Warnf("changes build Tree: prefer %s(%d prevIds) over %s(%d prevIds)", s1, len(ch1.PreviousIds), s2, len(ch2.PreviousIds)) - return s1, nil - } - - isEmptySnapshot := func(ch *Change) bool { - // TODO: add more sophisticated checks in Change for snapshots - return !ch.IsSnapshot - } - - // TODO: can we even have empty snapshots? - // prefer not empty snapshot - if isEmptySnapshot(ch1) && !isEmptySnapshot(ch2) { - log.Warnf("changes build Tree: prefer %s(not empty) over %s(empty)", s2, s1) - return s2, nil - } else if isEmptySnapshot(ch2) && !isEmptySnapshot(ch1) { - log.Warnf("changes build Tree: prefer %s(not empty) over %s(empty)", s1, s2) - return s1, nil - } - - // TODO: add virtual change mechanics - // unexpected behavior - just return lesser id - if s1 < s2 { - log.Warnf("changes build Tree: prefer %s (%s<%s)", s1, s1, s2) - return s1, nil - } - log.Warnf("changes build Tree: prefer %s (%s<%s)", s2, s2, s1) - - return s2, nil -} diff --git a/pkg/acl/acltree/treebuilder_test.go b/pkg/acl/acltree/treebuilder_test.go deleted file mode 100644 index cc586148..00000000 --- a/pkg/acl/acltree/treebuilder_test.go +++ /dev/null @@ -1,64 +0,0 @@ -package acltree - -//func createTreeFromThread(t thread.Thread, fromStart bool) (*Tree, error) { -// treeBuilder := newTreeBuilder(t, keys.NewEd25519Decoder()) -// treeBuilder.Init() -// return treeBuilder.Build(fromStart) -//} -// -//func TestACLTreeBuilder_UserJoinCorrectHeadsAndLen(t *testing.T) { -// thread, err := threadbuilder.NewThreadBuilderWithTestName("threadbuilder/userjoinexample.yml") -// if err != nil { -// t.Fatal(err) -// } -// -// res, err := createTreeFromThread(thread) -// if err != nil { -// t.Fatalf("build Tree should not result in an error: %v", res) -// } -// -// assert.equal(t, res.Heads(), []string{"C.1.1"}) -// assert.equal(t, res.Len(), 4) -//} -// -//func TestTreeBuilder_UserJoinTestTreeIterate(t *testing.T) { -// thread, err := threadbuilder.NewThreadBuilderWithTestName("threadbuilder/userjoinexample.yml") -// if err != nil { -// t.Fatal(err) -// } -// -// res, err := createTreeFromThread(thread) -// if err != nil { -// t.Fatalf("build Tree should not result in an error: %v", res) -// } -// -// assert.equal(t, res.Heads(), []string{"C.1.1"}) -// assert.equal(t, res.Len(), 4) -// var changeIds []string -// res.iterate(res.root, func(c *Change) (isContinue bool) { -// changeIds = append(changeIds, c.Id) -// return true -// }) -// assert.equal(t, changeIds, []string{"A.1.1", "A.1.2", "B.1.1", "C.1.1"}) -//} -// -//func TestTreeBuilder_UserRemoveTestTreeIterate(t *testing.T) { -// thread, err := threadbuilder.NewThreadBuilderWithTestName("threadbuilder/userremoveexample.yml") -// if err != nil { -// t.Fatal(err) -// } -// -// res, err := createTreeFromThread(thread) -// if err != nil { -// t.Fatalf("build Tree should not result in an error: %v", res) -// } -// -// assert.equal(t, res.Heads(), []string{"A.1.3"}) -// assert.equal(t, res.Len(), 4) -// var changeIds []string -// res.iterate(res.root, func(c *Change) (isContinue bool) { -// changeIds = append(changeIds, c.Id) -// return true -// }) -// assert.equal(t, changeIds, []string{"A.1.1", "A.1.2", "B.1.1", "A.1.3"}) -//} diff --git a/pkg/acl/acltree/treegraph.go b/pkg/acl/acltree/treegraph.go deleted file mode 100644 index 3b37fa2e..00000000 --- a/pkg/acl/acltree/treegraph.go +++ /dev/null @@ -1,11 +0,0 @@ -//go:build ((!linux && !darwin) || android || ios || nographviz) && !amd64 -// +build !linux,!darwin android ios nographviz -// +build !amd64 - -package acltree - -import "fmt" - -func (t *Tree) Graph() (data string, err error) { - return "", fmt.Errorf("not supported") -} diff --git a/pkg/acl/acltree/treegraph_nix.go b/pkg/acl/acltree/treegraph_nix.go deleted file mode 100644 index 4d798939..00000000 --- a/pkg/acl/acltree/treegraph_nix.go +++ /dev/null @@ -1,152 +0,0 @@ -//go:build (linux || darwin) && !android && !ios && !nographviz && (amd64 || arm64) -// +build linux darwin -// +build !android -// +build !ios -// +build !nographviz -// +build amd64 arm64 - -package acltree - -import ( - "bytes" - "fmt" - "strings" - "time" - "unicode" - - "github.com/goccy/go-graphviz" - "github.com/goccy/go-graphviz/cgraph" -) - -func (t *Tree) Graph() (data string, err error) { - var order = make(map[string]string) - var seq = 0 - t.Iterate(t.RootId(), func(c *Change) (isContinue bool) { - v := order[c.Id] - if v == "" { - order[c.Id] = fmt.Sprint(seq) - } else { - order[c.Id] = fmt.Sprintf("%s,%d", v, seq) - } - seq++ - return true - }) - g := graphviz.New() - defer g.Close() - graph, err := g.Graph() - if err != nil { - return - } - defer func() { - err = graph.Close() - }() - var nodes = make(map[string]*cgraph.Node) - var addChange = func(c *Change) error { - n, e := graph.CreateNode(c.Id) - if e != nil { - return e - } - if c.Content.GetAclData() != nil { - n.SetStyle(cgraph.FilledNodeStyle) - } else if c.IsSnapshot { - n.SetStyle(cgraph.DashedNodeStyle) - } - nodes[c.Id] = n - ord := order[c.Id] - if ord == "" { - ord = "miss" - } - var chSymbs []string - if c.Content.AclData != nil { - for _, chc := range c.Content.AclData.AclContent { - tp := fmt.Sprintf("%T", chc.Value) - tp = strings.Replace(tp, "ACLChangeACLContentValueValueOf", "", 1) - res := "" - for _, ts := range tp { - if unicode.IsUpper(ts) { - res += string(ts) - } - } - chSymbs = append(chSymbs, res) - } - } - if c.DecryptedDocumentChange != nil { - // TODO: add some parser to provide custom unmarshalling for the document change - //for _, chc := range c.DecryptedDocumentChange.Content { - // tp := fmt.Sprintf("%T", chc.Value) - // tp = strings.Replace(tp, "ChangeContentValueOf", "", 1) - // res := "" - // for _, ts := range tp { - // if unicode.IsUpper(ts) { - // res += string(ts) - // } - // } - // chSymbs = append(chSymbs, res) - //} - chSymbs = append(chSymbs, "DEC") - } - - shortId := c.Id - label := fmt.Sprintf("Id: %s\nOrd: %s\nTime: %s\nChanges: %s\n", - shortId, - ord, - time.Unix(c.Content.Timestamp, 0).Format("02.01.06 15:04:05"), - strings.Join(chSymbs, ","), - ) - n.SetLabel(label) - return nil - } - for _, c := range t.attached { - if err = addChange(c); err != nil { - return - } - } - for _, c := range t.unAttached { - if err = addChange(c); err != nil { - return - } - } - var getNode = func(id string) (*cgraph.Node, error) { - if n, ok := nodes[id]; ok { - return n, nil - } - n, err := graph.CreateNode(fmt.Sprintf("%s: not in Tree", id)) - if err != nil { - return nil, err - } - nodes[id] = n - return n, nil - } - var addLinks = func(c *Change) error { - for _, prevId := range c.PreviousIds { - self, e := getNode(c.Id) - if e != nil { - return e - } - prev, e := getNode(prevId) - if e != nil { - return e - } - _, e = graph.CreateEdge("", self, prev) - if e != nil { - return e - } - } - return nil - } - for _, c := range t.attached { - if err = addLinks(c); err != nil { - return - } - } - for _, c := range t.unAttached { - if err = addLinks(c); err != nil { - return - } - } - var buf bytes.Buffer - if err = g.Render(graph, "dot", &buf); err != nil { - return - } - return buf.String(), nil -} diff --git a/pkg/acl/acltree/treeiterator.go b/pkg/acl/acltree/treeiterator.go deleted file mode 100644 index 19a20095..00000000 --- a/pkg/acl/acltree/treeiterator.go +++ /dev/null @@ -1,158 +0,0 @@ -package acltree - -import "sync" - -var itPool = &sync.Pool{ - New: func() interface{} { - return &iterator{} - }, -} - -func newIterator() *iterator { - return itPool.Get().(*iterator) -} - -func freeIterator(i *iterator) { - itPool.Put(i) -} - -type iterator struct { - compBuf []*Change - queue []*Change - doneMap map[*Change]struct{} - breakpoint *Change - f func(c *Change) bool -} - -func (i *iterator) iterateSkip(start *Change, skipBefore *Change, f func(c *Change) (isContinue bool)) { - skipping := true - i.iterate(start, func(c *Change) (isContinue bool) { - if skipping && c != skipBefore { - return true - } - skipping = false - return f(c) - }) -} - -func (i *iterator) iterate(start *Change, f func(c *Change) (isContinue bool)) { - if start == nil { - return - } - // reset - i.queue = i.queue[:0] - i.compBuf = i.compBuf[:0] - i.doneMap = make(map[*Change]struct{}) - i.queue = append(i.queue, start) - i.breakpoint = nil - i.f = f - - for len(i.queue) > 0 { - c := i.queue[0] - i.queue = i.queue[1:] - nl := len(c.Next) - if nl == 1 { - if !i.iterateLin(c) { - return - } - if i.breakpoint != nil { - i.toQueue(i.breakpoint) - i.breakpoint = nil - } - } else { - _, done := i.doneMap[c] - if !done { - if !f(c) { - return - } - i.doneMap[c] = struct{}{} - } - if nl != 0 { - for _, next := range c.Next { - i.toQueue(next) - } - } - } - } -} - -func (i *iterator) iterateLin(c *Change) bool { - for len(c.Next) == 1 { - _, done := i.doneMap[c] - if !done { - if !i.f(c) { - return false - } - i.doneMap[c] = struct{}{} - } - - c = c.Next[0] - if len(c.PreviousIds) > 1 { - break - } - } - if len(c.Next) == 0 && len(c.PreviousIds) <= 1 { - if !i.f(c) { - return false - } - i.doneMap[c] = struct{}{} - } else { - i.breakpoint = c - } - - return true -} - -func (i *iterator) comp(c1, c2 *Change) uint8 { - if c1.Id == c2.Id { - return 0 - } - i.compBuf = i.compBuf[:0] - i.compBuf = append(i.compBuf, c1.Next...) - var uniq = make(map[*Change]struct{}) - var appendUniqueToBuf = func(next []*Change) { - for _, n := range next { - if _, ok := uniq[n]; !ok { - i.compBuf = append(i.compBuf, n) - uniq[n] = struct{}{} - } - } - } - var used int - for len(i.compBuf)-used > 0 { - l := len(i.compBuf) - used - for _, n := range i.compBuf[used:] { - delete(uniq, n) - if n.Id == c2.Id { - return 1 - } else { - appendUniqueToBuf(n.Next) - } - } - used += l - } - return 2 -} - -func (i *iterator) toQueue(c *Change) { - var pos = -1 -For: - for idx, qc := range i.queue { - switch i.comp(c, qc) { - // exists - case 0: - return - // - case 1: - pos = idx - break For - } - } - if pos == -1 { - i.queue = append(i.queue, c) - } else if pos == 0 { - i.queue = append([]*Change{c}, i.queue...) - } else { - i.queue = append(i.queue[:pos], append([]*Change{c}, i.queue[pos:]...)...) - } -} diff --git a/pkg/acl/example/plaintextdocument/document.go b/pkg/acl/example/plaintextdocument/document.go deleted file mode 100644 index d97bf430..00000000 --- a/pkg/acl/example/plaintextdocument/document.go +++ /dev/null @@ -1,169 +0,0 @@ -package plaintextdocument - -import ( - "context" - "fmt" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/account" - aclpb "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/acltree" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/testutils/testchanges/testchangepb" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/treestorage" - - "github.com/gogo/protobuf/proto" -) - -type PlainTextDocument interface { - Text() string - AddText(ctx context.Context, text string) error -} - -// TODO: this struct is not thread-safe, so use it wisely :-) -type plainTextDocument struct { - heads []string - aclTree acltree.ACLTree - state *DocumentState -} - -func (p *plainTextDocument) Text() string { - if p.state != nil { - return p.state.Text - } - return "" -} - -func (p *plainTextDocument) AddText(ctx context.Context, text string) error { - _, err := p.aclTree.AddContent(ctx, func(builder acltree.ChangeBuilder) error { - builder.AddChangeContent( - &testchangepb.PlainTextChangeData{ - Content: []*testchangepb.PlainTextChangeContent{ - createAppendTextChangeContent(text), - }, - }) - return nil - }) - return err -} - -func (p *plainTextDocument) Update(tree acltree.ACLTree) { - p.aclTree = tree - var err error - defer func() { - if err != nil { - fmt.Println("rebuild has returned error:", err) - } - }() - - prevHeads := p.heads - p.heads = tree.Heads() - startId := prevHeads[0] - tree.IterateFrom(startId, func(change *acltree.Change) (isContinue bool) { - if change.Id == startId { - return true - } - if change.DecryptedDocumentChange != nil { - p.state, err = p.state.ApplyChange(change.DecryptedDocumentChange, change.Id) - if err != nil { - return false - } - } - return true - }) -} - -func (p *plainTextDocument) Rebuild(tree acltree.ACLTree) { - p.aclTree = tree - p.heads = tree.Heads() - var startId string - var err error - defer func() { - if err != nil { - fmt.Println("rebuild has returned error:", err) - } - }() - - rootChange := tree.Root() - - if rootChange.DecryptedDocumentChange == nil { - err = fmt.Errorf("root doesn't have decrypted change") - return - } - - state, err := BuildDocumentStateFromChange(rootChange.DecryptedDocumentChange, rootChange.Id) - if err != nil { - return - } - - startId = rootChange.Id - tree.Iterate(func(change *acltree.Change) (isContinue bool) { - if startId == change.Id { - return true - } - if change.DecryptedDocumentChange != nil { - state, err = state.ApplyChange(change.DecryptedDocumentChange, change.Id) - if err != nil { - return false - } - } - return true - }) - if err != nil { - return - } - p.state = state -} - -func NewInMemoryPlainTextDocument(acc *account.AccountData, text string) (PlainTextDocument, error) { - return NewPlainTextDocument(acc, treestorage.NewInMemoryTreeStorage, text) -} - -func NewPlainTextDocument( - acc *account.AccountData, - create treestorage.CreatorFunc, - text string) (PlainTextDocument, error) { - changeBuilder := func(builder acltree.ChangeBuilder) error { - err := builder.UserAdd(acc.Identity, acc.EncKey.GetPublic(), aclpb.ACLChange_Admin) - if err != nil { - return err - } - builder.AddChangeContent(createInitialChangeContent(text)) - return nil - } - t, err := acltree.CreateNewTreeStorageWithACL( - acc, - changeBuilder, - create) - if err != nil { - return nil, err - } - - doc := &plainTextDocument{ - heads: nil, - aclTree: nil, - state: nil, - } - tree, err := acltree.BuildACLTree(t, acc, doc) - if err != nil { - return nil, err - } - doc.aclTree = tree - return doc, nil -} - -func createInitialChangeContent(text string) proto.Marshaler { - return &testchangepb.PlainTextChangeData{ - Content: []*testchangepb.PlainTextChangeContent{ - createAppendTextChangeContent(text), - }, - Snapshot: &testchangepb.PlainTextChangeSnapshot{Text: text}, - } -} - -func createAppendTextChangeContent(text string) *testchangepb.PlainTextChangeContent { - return &testchangepb.PlainTextChangeContent{ - Value: &testchangepb.PlainTextChangeContentValueOfTextAppend{ - TextAppend: &testchangepb.PlainTextChangeTextAppend{ - Text: text, - }, - }, - } -} diff --git a/pkg/acl/example/plaintextdocument/document_test.go b/pkg/acl/example/plaintextdocument/document_test.go deleted file mode 100644 index 7cabb4af..00000000 --- a/pkg/acl/example/plaintextdocument/document_test.go +++ /dev/null @@ -1,58 +0,0 @@ -package plaintextdocument - -import ( - "context" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/account" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/testutils/treestoragebuilder" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/treestorage" - "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/signingkey" - "github.com/stretchr/testify/assert" - "testing" -) - -func TestDocument_NewPlainTextDocument(t *testing.T) { - keychain := treestoragebuilder.NewKeychain() - keychain.AddSigningKey("A") - keychain.AddEncryptionKey("A") - data := &account.AccountData{ - Identity: keychain.GetIdentity("A"), - SignKey: keychain.SigningKeys["A"], - EncKey: keychain.EncryptionKeys["A"], - Decoder: signingkey.NewEd25519PubKeyDecoder(), - } - - doc, err := NewPlainTextDocument(data, treestorage.NewInMemoryTreeStorage, "Some text") - if err != nil { - t.Fatalf("should not create document with error: %v", err) - } - assert.Equal(t, doc.Text(), "Some text") -} - -func TestDocument_PlainTextDocument_AddText(t *testing.T) { - keychain := treestoragebuilder.NewKeychain() - keychain.AddSigningKey("A") - keychain.AddEncryptionKey("A") - data := &account.AccountData{ - Identity: keychain.GetIdentity("A"), - SignKey: keychain.SigningKeys["A"], - EncKey: keychain.EncryptionKeys["A"], - Decoder: signingkey.NewEd25519PubKeyDecoder(), - } - - doc, err := NewPlainTextDocument(data, treestorage.NewInMemoryTreeStorage, "Some text") - if err != nil { - t.Fatalf("should not create document with error: %v", err) - } - - err = doc.AddText(context.Background(), "Next") - if err != nil { - t.Fatalf("should be able to add document: %v", err) - } - assert.Equal(t, doc.Text(), "Some text|Next") - - err = doc.AddText(context.Background(), "Shmext") - if err != nil { - t.Fatalf("should be able to add document: %v", err) - } - assert.Equal(t, doc.Text(), "Some text|Next|Shmext") -} diff --git a/pkg/acl/example/plaintextdocument/plaintextdocstate.go b/pkg/acl/example/plaintextdocument/plaintextdocstate.go deleted file mode 100644 index 3a3afec3..00000000 --- a/pkg/acl/example/plaintextdocument/plaintextdocstate.go +++ /dev/null @@ -1,59 +0,0 @@ -package plaintextdocument - -import ( - "fmt" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/testutils/testchanges/testchangepb" - - "github.com/gogo/protobuf/proto" -) - -type DocumentState struct { - LastChangeId string - Text string -} - -func NewDocumentState(text string, id string) *DocumentState { - return &DocumentState{ - LastChangeId: id, - Text: text, - } -} - -func BuildDocumentStateFromChange(change []byte, id string) (*DocumentState, error) { - var changesData testchangepb.PlainTextChangeData - err := proto.Unmarshal(change, &changesData) - if err != nil { - return nil, err - } - - if changesData.GetSnapshot() == nil { - return nil, fmt.Errorf("could not create state from empty snapshot") - } - return NewDocumentState(changesData.GetSnapshot().GetText(), id), nil -} - -func (p *DocumentState) ApplyChange(change []byte, id string) (*DocumentState, error) { - var changesData testchangepb.PlainTextChangeData - err := proto.Unmarshal(change, &changesData) - if err != nil { - return nil, err - } - - for _, content := range changesData.GetContent() { - err = p.applyChange(content) - if err != nil { - return nil, err - } - } - p.LastChangeId = id - return p, nil -} - -func (p *DocumentState) applyChange(ch *testchangepb.PlainTextChangeContent) error { - switch { - case ch.GetTextAppend() != nil: - text := ch.GetTextAppend().GetText() - p.Text += "|" + text - } - return nil -} diff --git a/pkg/acl/list/changebuilder.go b/pkg/acl/list/changebuilder.go index c5420b50..bcdabe62 100644 --- a/pkg/acl/list/changebuilder.go +++ b/pkg/acl/list/changebuilder.go @@ -95,7 +95,7 @@ func (c *aclChangeBuilder) UserAdd(identity string, encryptionKey encryptionkey. func (c *aclChangeBuilder) BuildAndApply() (*Record, []byte, error) { aclRecord := &aclpb.Record{ - PrevId: c.list.Last().Id, + PrevId: c.list.Head().Id, CurrentReadKeyHash: c.readKeyHash, Timestamp: int64(time.Now().Nanosecond()), Identity: c.acc.Identity, diff --git a/pkg/acl/list/list.go b/pkg/acl/list/list.go index 420f7e84..c7fa37d8 100644 --- a/pkg/acl/list/list.go +++ b/pkg/acl/list/list.go @@ -1,16 +1,61 @@ package list -import "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/tree" +import ( + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/tree" +) type IterFunc = func(record *Record) (IsContinue bool) type ACLList interface { tree.RWLocker ID() string + Header() *aclpb.Header ACLState() ACLState IsAfter(first string, second string) (bool, error) - Last() *Record + Head() *Record Get(id string) (*Record, error) Iterate(iterFunc IterFunc) IterateFrom(startId string, iterFunc IterFunc) } + +//func (t *ACLListStorageBuilder) IsAfter(first string, second string) (bool, error) { +// firstRec, okFirst := t.indexes[first] +// secondRec, okSecond := t.indexes[second] +// if !okFirst || !okSecond { +// return false, fmt.Errorf("not all entries are there: first (%b), second (%b)", okFirst, okSecond) +// } +// return firstRec > secondRec, nil +//} +// +//func (t *ACLListStorageBuilder) Head() *list.Record { +// return t.records[len(t.records)-1] +//} +// +//func (t *ACLListStorageBuilder) Get(id string) (*list.Record, error) { +// recIdx, ok := t.indexes[id] +// if !ok { +// return nil, fmt.Errorf("no such record") +// } +// return t.records[recIdx], nil +//} +// +//func (t *ACLListStorageBuilder) Iterate(iterFunc list.IterFunc) { +// for _, rec := range t.records { +// if !iterFunc(rec) { +// return +// } +// } +//} +// +//func (t *ACLListStorageBuilder) IterateFrom(startId string, iterFunc list.IterFunc) { +// recIdx, ok := t.indexes[startId] +// if !ok { +// return +// } +// for i := recIdx; i < len(t.records); i++ { +// if !iterFunc(t.records[i]) { +// return +// } +// } +//} diff --git a/pkg/acl/list/storage.go b/pkg/acl/list/storage.go new file mode 100644 index 00000000..22fcb732 --- /dev/null +++ b/pkg/acl/list/storage.go @@ -0,0 +1,14 @@ +package list + +import ( + "context" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb" +) + +type Storage interface { + ID() string + Head() (*aclpb.RawRecord, error) + Header() (*aclpb.Header, error) + GetRecord(ctx context.Context, id string) (*aclpb.RawRecord, error) + AddRecord(ctx context.Context, rec *aclpb.RawRecord) error +} diff --git a/pkg/acl/testutils/treestoragebuilder/keychain.go b/pkg/acl/testutils/acllistbuilder/keychain.go similarity index 99% rename from pkg/acl/testutils/treestoragebuilder/keychain.go rename to pkg/acl/testutils/acllistbuilder/keychain.go index 5635f072..a1567f6a 100644 --- a/pkg/acl/testutils/treestoragebuilder/keychain.go +++ b/pkg/acl/testutils/acllistbuilder/keychain.go @@ -1,4 +1,4 @@ -package treestoragebuilder +package acllistbuilder import ( "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/encryptionkey" diff --git a/pkg/acl/testutils/acllistbuilder/liststoragebuilder.go b/pkg/acl/testutils/acllistbuilder/liststoragebuilder.go new file mode 100644 index 00000000..e78a7df2 --- /dev/null +++ b/pkg/acl/testutils/acllistbuilder/liststoragebuilder.go @@ -0,0 +1,320 @@ +package acllistbuilder + +import ( + "context" + "fmt" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb" + "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/keys/asymmetric/encryptionkey" + "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/signingkey" + "io/ioutil" + "path" + "sync" + "time" + + "github.com/gogo/protobuf/proto" + "gopkg.in/yaml.v3" +) + +type ACLListStorageBuilder struct { + aclList string + records []*aclpb.Record + rawRecords []*aclpb.RawRecord + indexes map[string]int + keychain *Keychain + header *aclpb.Header + id string + sync.RWMutex +} + +func NewACLListStorageBuilder(keychain *Keychain) *ACLListStorageBuilder { + return &ACLListStorageBuilder{ + records: make([]*aclpb.Record, 0), + indexes: make(map[string]int), + keychain: keychain, + } +} + +func NewACLListStorageBuilderWithTestName(name string) (*ACLListStorageBuilder, error) { + filePath := path.Join(yamltests.Path(), name) + return NewACLListStorageBuilderFromFile(filePath) +} + +func NewACLListStorageBuilderFromFile(file string) (*ACLListStorageBuilder, error) { + content, err := ioutil.ReadFile(file) + if err != nil { + return nil, err + } + + ymlTree := YMLList{} + err = yaml.Unmarshal(content, &ymlTree) + if err != nil { + return nil, err + } + + tb := NewACLListStorageBuilder(NewKeychain()) + tb.Parse(&ymlTree) + + return tb, nil +} + +func (t *ACLListStorageBuilder) createRaw(rec *aclpb.Record) *aclpb.RawRecord { + aclMarshaled, err := proto.Marshal(rec) + if err != nil { + panic("should be able to marshal final acl message!") + } + + signature, err := t.keychain.SigningKeysByIdentity[rec.Identity].Sign(aclMarshaled) + if err != nil { + panic("should be able to sign final acl message!") + } + + id, _ := cid.NewCIDFromBytes(aclMarshaled) + + return &aclpb.RawRecord{ + Payload: aclMarshaled, + Signature: signature, + Id: id, + } +} + +func (t *ACLListStorageBuilder) getRecord(idx int) *aclpb.RawRecord { + return t.rawRecords[idx] +} + +func (t *ACLListStorageBuilder) Head() (*aclpb.RawRecord, error) { + return t.getRecord(len(t.records) - 1), nil +} + +func (t *ACLListStorageBuilder) Header() (*aclpb.Header, error) { + return t.header, nil +} + +func (t *ACLListStorageBuilder) GetRecord(ctx context.Context, id string) (*aclpb.RawRecord, error) { + recIdx, ok := t.indexes[id] + if !ok { + return nil, fmt.Errorf("no such record") + } + return t.getRecord(recIdx), nil +} + +func (t *ACLListStorageBuilder) AddRecord(ctx context.Context, rec *aclpb.Record) error { + panic("implement me") +} + +func (t *ACLListStorageBuilder) ID() string { + return t.id +} + +func (t *ACLListStorageBuilder) GetKeychain() *Keychain { + return t.keychain +} + +func (t *ACLListStorageBuilder) Parse(tree *YMLList) { + // Just to clarify - we are generating new identities for the ones that + // are specified in the yml file, because our identities should be Ed25519 + // the same thing is happening for the encryption keys + t.keychain.ParseKeys(&tree.Keys) + prevId := "" + for idx, rec := range tree.Records { + newRecord := t.parseRecord(rec, prevId) + rawRecord := t.createRaw(newRecord) + t.records = append(t.records, newRecord) + t.rawRecords = append(t.rawRecords, t.createRaw(newRecord)) + t.indexes[rawRecord.Id] = idx + prevId = rawRecord.Id + } + + t.createHeaderAndId() +} + +func (t *ACLListStorageBuilder) parseRecord(rec *Record, prevId string) *aclpb.Record { + k := t.keychain.GetKey(rec.ReadKey).(*SymKey) + var aclChangeContents []*aclpb.ACLChangeACLContentValue + for _, ch := range rec.AclChanges { + aclChangeContent := t.parseACLChange(ch) + aclChangeContents = append(aclChangeContents, aclChangeContent) + } + data := &aclpb.ACLChangeACLData{ + AclContent: aclChangeContents, + } + bytes, _ := data.Marshal() + + return &aclpb.Record{ + PrevId: prevId, + Identity: t.keychain.GetIdentity(rec.Identity), + Data: bytes, + CurrentReadKeyHash: k.Hash, + Timestamp: time.Now().Unix(), + } +} + +func (t *ACLListStorageBuilder) parseACLChange(ch *ACLChange) (convCh *aclpb.ACLChangeACLContentValue) { + switch { + case ch.UserAdd != nil: + add := ch.UserAdd + + encKey := t.keychain. + GetKey(add.EncryptionKey).(encryptionkey.PrivKey) + rawKey, _ := encKey.GetPublic().Raw() + + convCh = &aclpb.ACLChangeACLContentValue{ + Value: &aclpb.ACLChangeACLContentValueValueOfUserAdd{ + UserAdd: &aclpb.ACLChangeUserAdd{ + Identity: t.keychain.GetIdentity(add.Identity), + EncryptionKey: rawKey, + EncryptedReadKeys: t.encryptReadKeys(add.EncryptedReadKeys, encKey), + Permissions: t.convertPermission(add.Permission), + }, + }, + } + case ch.UserJoin != nil: + join := ch.UserJoin + + encKey := t.keychain. + GetKey(join.EncryptionKey).(encryptionkey.PrivKey) + rawKey, _ := encKey.GetPublic().Raw() + + idKey, _ := t.keychain.SigningKeys[join.Identity].GetPublic().Raw() + signKey := t.keychain.GetKey(join.AcceptSignature).(signingkey.PrivKey) + signature, err := signKey.Sign(idKey) + if err != nil { + panic(err) + } + + convCh = &aclpb.ACLChangeACLContentValue{ + Value: &aclpb.ACLChangeACLContentValueValueOfUserJoin{ + UserJoin: &aclpb.ACLChangeUserJoin{ + Identity: t.keychain.GetIdentity(join.Identity), + EncryptionKey: rawKey, + AcceptSignature: signature, + UserInviteId: join.InviteId, + EncryptedReadKeys: t.encryptReadKeys(join.EncryptedReadKeys, encKey), + }, + }, + } + case ch.UserInvite != nil: + invite := ch.UserInvite + rawAcceptKey, _ := t.keychain.GetKey(invite.AcceptKey).(signingkey.PrivKey).GetPublic().Raw() + encKey := t.keychain. + GetKey(invite.EncryptionKey).(encryptionkey.PrivKey) + rawEncKey, _ := encKey.GetPublic().Raw() + + convCh = &aclpb.ACLChangeACLContentValue{ + Value: &aclpb.ACLChangeACLContentValueValueOfUserInvite{ + UserInvite: &aclpb.ACLChangeUserInvite{ + AcceptPublicKey: rawAcceptKey, + EncryptPublicKey: rawEncKey, + EncryptedReadKeys: t.encryptReadKeys(invite.EncryptedReadKeys, encKey), + Permissions: t.convertPermission(invite.Permissions), + InviteId: invite.InviteId, + }, + }, + } + case ch.UserConfirm != nil: + confirm := ch.UserConfirm + + convCh = &aclpb.ACLChangeACLContentValue{ + Value: &aclpb.ACLChangeACLContentValueValueOfUserConfirm{ + UserConfirm: &aclpb.ACLChangeUserConfirm{ + Identity: t.keychain.GetIdentity(confirm.Identity), + UserAddId: confirm.UserAddId, + }, + }, + } + case ch.UserPermissionChange != nil: + permissionChange := ch.UserPermissionChange + + convCh = &aclpb.ACLChangeACLContentValue{ + Value: &aclpb.ACLChangeACLContentValueValueOfUserPermissionChange{ + UserPermissionChange: &aclpb.ACLChangeUserPermissionChange{ + Identity: t.keychain.GetIdentity(permissionChange.Identity), + Permissions: t.convertPermission(permissionChange.Permission), + }, + }, + } + case ch.UserRemove != nil: + remove := ch.UserRemove + + newReadKey := t.keychain.GetKey(remove.NewReadKey).(*SymKey) + + var replaces []*aclpb.ACLChangeReadKeyReplace + for _, id := range remove.IdentitiesLeft { + identity := t.keychain.GetIdentity(id) + encKey := t.keychain.EncryptionKeys[id] + rawEncKey, _ := encKey.GetPublic().Raw() + encReadKey, err := encKey.GetPublic().Encrypt(newReadKey.Key.Bytes()) + if err != nil { + panic(err) + } + replaces = append(replaces, &aclpb.ACLChangeReadKeyReplace{ + Identity: identity, + EncryptionKey: rawEncKey, + EncryptedReadKey: encReadKey, + }) + } + + convCh = &aclpb.ACLChangeACLContentValue{ + Value: &aclpb.ACLChangeACLContentValueValueOfUserRemove{ + UserRemove: &aclpb.ACLChangeUserRemove{ + Identity: t.keychain.GetIdentity(remove.RemovedIdentity), + ReadKeyReplaces: replaces, + }, + }, + } + } + if convCh == nil { + panic("cannot have empty acl change") + } + + return convCh +} + +func (t *ACLListStorageBuilder) encryptReadKeys(keys []string, encKey encryptionkey.PrivKey) (enc [][]byte) { + for _, k := range keys { + realKey := t.keychain.GetKey(k).(*SymKey).Key.Bytes() + res, err := encKey.GetPublic().Encrypt(realKey) + if err != nil { + panic(err) + } + + enc = append(enc, res) + } + return +} + +func (t *ACLListStorageBuilder) convertPermission(perm string) aclpb.ACLChangeUserPermissions { + switch perm { + case "admin": + return aclpb.ACLChange_Admin + case "writer": + return aclpb.ACLChange_Writer + case "reader": + return aclpb.ACLChange_Reader + default: + panic(fmt.Sprintf("incorrect permission: %s", perm)) + } +} + +func (t *ACLListStorageBuilder) traverseFromHead(f func(rec *aclpb.Record, id string) error) (err error) { + for i := len(t.records) - 1; i >= 0; i-- { + err = f(t.records[i], t.rawRecords[i].Id) + if err != nil { + return err + } + } + return nil +} + +func (t *ACLListStorageBuilder) createHeaderAndId() { + t.header = &aclpb.Header{ + FirstId: t.rawRecords[0].Id, + AclListId: "", + WorkspaceId: "", + DocType: aclpb.Header_ACL, + } + bytes, _ := t.header.Marshal() + id, _ := cid.NewCIDFromBytes(bytes) + t.id = id +} diff --git a/pkg/acl/testutils/treestoragebuilder/treestoragebuildergraph.go b/pkg/acl/testutils/acllistbuilder/treestoragebuildergraph.go similarity index 72% rename from pkg/acl/testutils/treestoragebuilder/treestoragebuildergraph.go rename to pkg/acl/testutils/acllistbuilder/treestoragebuildergraph.go index 11c6609d..ae4f1027 100644 --- a/pkg/acl/testutils/treestoragebuilder/treestoragebuildergraph.go +++ b/pkg/acl/testutils/acllistbuilder/treestoragebuildergraph.go @@ -2,10 +2,10 @@ // +build !linux,!darwin android ios nographviz // +build !amd64 -package treestoragebuilder +package acllistbuilder import "fmt" -func (t *TreeStorageBuilder) Graph() (string, error) { +func (t *ACLListStorageBuilder) Graph() (string, error) { return "", fmt.Errorf("building graphs is not supported") } diff --git a/pkg/acl/testutils/acllistbuilder/treestoragebuildergraph_nix.go b/pkg/acl/testutils/acllistbuilder/treestoragebuildergraph_nix.go new file mode 100644 index 00000000..b637827f --- /dev/null +++ b/pkg/acl/testutils/acllistbuilder/treestoragebuildergraph_nix.go @@ -0,0 +1,121 @@ +//go:build (linux || darwin) && !android && !ios && !nographviz && (amd64 || arm64) +// +build linux darwin +// +build !android +// +build !ios +// +build !nographviz +// +build amd64 arm64 + +package acllistbuilder + +import ( + "fmt" + "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb" + + "github.com/gogo/protobuf/proto" + "strings" + "unicode" + + "github.com/awalterschulze/gographviz" +) + +// To quickly look at visualized string you can use https://dreampuf.github.io/GraphvizOnline + +type EdgeParameters struct { + style string + color string + label string +} + +func (t *ACLListStorageBuilder) Graph() (string, error) { + // TODO: check updates on https://github.com/goccy/go-graphviz/issues/52 or make a fix yourself to use better library here + graph := gographviz.NewGraph() + graph.SetName("G") + graph.SetDir(true) + var nodes = make(map[string]struct{}) + + var addNodes = func(r *aclpb.Record, id string) error { + style := "solid" + + var chSymbs []string + aclData := &aclpb.ACLChangeACLData{} + err := proto.Unmarshal(r.GetData(), aclData) + if err != nil { + return err + } + + for _, chc := range aclData.AclContent { + tp := fmt.Sprintf("%T", chc.Value) + tp = strings.Replace(tp, "ACLChangeACLContentValueValueOf", "", 1) + res := "" + for _, ts := range tp { + if unicode.IsUpper(ts) { + res += string(ts) + } + } + chSymbs = append(chSymbs, res) + } + + shortId := id + label := fmt.Sprintf("Id: %s\nChanges: %s\n", + shortId, + strings.Join(chSymbs, ","), + ) + e := graph.AddNode("G", "\""+id+"\"", map[string]string{ + "label": "\"" + label + "\"", + "style": "\"" + style + "\"", + }) + if e != nil { + return e + } + nodes[id] = struct{}{} + return nil + } + + var createEdge = func(firstId, secondId string, params EdgeParameters) error { + _, exists := nodes[firstId] + if !exists { + return fmt.Errorf("no such node") + } + _, exists = nodes[secondId] + if !exists { + return fmt.Errorf("no previous node") + } + + err := graph.AddEdge("\""+firstId+"\"", "\""+secondId+"\"", true, map[string]string{ + "color": params.color, + "style": params.style, + }) + if err != nil { + return err + } + + return nil + } + + var addLinks = func(r *aclpb.Record, id string) error { + if r.PrevId == "" { + return nil + } + err := createEdge(id, r.PrevId, EdgeParameters{ + style: "dashed", + color: "red", + }) + if err != nil { + return err + } + + return nil + } + + err := t.traverseFromHead(addNodes) + if err != nil { + return "", err + } + + err = t.traverseFromHead(addLinks) + if err != nil { + return "", err + } + + return graph.String(), nil +} diff --git a/pkg/acl/testutils/treestoragebuilder/ymlentities.go b/pkg/acl/testutils/acllistbuilder/ymlentities.go similarity index 50% rename from pkg/acl/testutils/treestoragebuilder/ymlentities.go rename to pkg/acl/testutils/acllistbuilder/ymlentities.go index 0a873d99..471155c1 100644 --- a/pkg/acl/testutils/treestoragebuilder/ymlentities.go +++ b/pkg/acl/testutils/acllistbuilder/ymlentities.go @@ -1,8 +1,4 @@ -package treestoragebuilder - -type TreeDescription struct { - Author string `yaml:"author"` -} +package acllistbuilder type Keys struct { Enc []string `yaml:"Enc"` @@ -10,20 +6,6 @@ type Keys struct { Read []string `yaml:"Read"` } -type ACLSnapshot struct { - UserStates []struct { - Identity string `yaml:"identity"` - EncryptionKey string `yaml:"encryptionKey"` - EncryptedReadKeys []string `yaml:"encryptedReadKeys"` - Permissions string `yaml:"permission"` - IsConfirmed bool `yaml:"isConfirmed"` - } `yaml:"userStates"` -} - -type PlainTextSnapshot struct { - Text string `yaml:"text"` -} - type ACLChange struct { UserAdd *struct { Identity string `yaml:"identity"` @@ -65,28 +47,9 @@ type ACLChange struct { } } -type PlainTextChange struct { - TextAppend *struct { - Text string `yaml:"text"` - } `yaml:"textAppend"` -} - -type GraphNode struct { - Id string `yaml:"id"` - BaseSnapshot string `yaml:"baseSnapshot"` - AclSnapshot string `yaml:"aclSnapshot"` - ACLHeads []string `yaml:"aclHeads"` - TreeHeads []string `yaml:"treeHeads"` -} - -type Change struct { - Id string `yaml:"id"` - Identity string `yaml:"identity"` - - AclSnapshot *ACLSnapshot `yaml:"aclSnapshot"` - Snapshot *PlainTextSnapshot `yaml:"snapshot"` - AclChanges []*ACLChange `yaml:"aclChanges"` - Changes []*PlainTextChange `yaml:"changes"` +type Record struct { + Identity string `yaml:"identity"` + AclChanges []*ACLChange `yaml:"aclChanges"` ReadKey string `yaml:"readKey"` } @@ -96,22 +59,8 @@ type Header struct { IsWorkspace bool `yaml:"isWorkspace"` } -type Update struct { - UseCase string `yaml:"useCase"` - Changes []*Change `yaml:"changes"` - Graph []*GraphNode `yaml:"graph"` -} - -type YMLTree struct { - Description *TreeDescription `yaml:"tree"` - Changes []*Change `yaml:"changes"` - Updates []*Update `yaml:"updates"` +type YMLList struct { + Records []*Record `yaml:"records"` Keys Keys `yaml:"keys"` - - Graph []*GraphNode `yaml:"graph"` - - Heads []string `yaml:"heads"` - Orphans []string `yaml:"orphans"` - Header *Header `yaml:"header"` } diff --git a/pkg/acl/testutils/treestoragebuilder/ymlentities_test.go b/pkg/acl/testutils/acllistbuilder/ymlentities_test.go similarity index 51% rename from pkg/acl/testutils/treestoragebuilder/ymlentities_test.go rename to pkg/acl/testutils/acllistbuilder/ymlentities_test.go index 2ae2e716..28780446 100644 --- a/pkg/acl/testutils/treestoragebuilder/ymlentities_test.go +++ b/pkg/acl/testutils/acllistbuilder/ymlentities_test.go @@ -1,4 +1,4 @@ -package treestoragebuilder +package acllistbuilder import ( "fmt" @@ -6,7 +6,7 @@ import ( ) func Test_YamlParse(t *testing.T) { - tb, _ := NewTreeStorageBuilderWithTestName("userjoinexampleupdate.yml") + tb, _ := NewACLListStorageBuilderWithTestName("userjoinexampleupdate.yml") gr, _ := tb.Graph() fmt.Println(gr) } diff --git a/pkg/acl/testutils/testchanges/testchangepb/protos/testdocumentchanges.proto b/pkg/acl/testutils/testchanges/testchangepb/protos/testdocumentchanges.proto deleted file mode 100644 index 59a97c38..00000000 --- a/pkg/acl/testutils/testchanges/testchangepb/protos/testdocumentchanges.proto +++ /dev/null @@ -1,26 +0,0 @@ -syntax = "proto3"; -package anytype; -option go_package = "testchangepb"; - -// TODO: move to separate package - -message PlainTextChange { - message Content { - oneof value { - TextAppend textAppend = 1; - } - } - - message TextAppend { - string text = 1; - } - - message Snapshot { - string text = 1; - } - - message Data { - repeated Content content = 1; - Snapshot snapshot = 2; - } -} diff --git a/pkg/acl/testutils/testchanges/testchangepb/testdocumentchanges.pb.go b/pkg/acl/testutils/testchanges/testchangepb/testdocumentchanges.pb.go deleted file mode 100644 index 874061c6..00000000 --- a/pkg/acl/testutils/testchanges/testchangepb/testdocumentchanges.pb.go +++ /dev/null @@ -1,1089 +0,0 @@ -// Code generated by protoc-gen-gogo. DO NOT EDIT. -// source: pkg/acl/testutils/testchanges/testchangepb/protos/testdocumentchanges.proto - -package testchangepb - -import ( - fmt "fmt" - proto "github.com/gogo/protobuf/proto" - io "io" - math "math" - math_bits "math/bits" -) - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package - -type PlainTextChange struct { -} - -func (m *PlainTextChange) Reset() { *m = PlainTextChange{} } -func (m *PlainTextChange) String() string { return proto.CompactTextString(m) } -func (*PlainTextChange) ProtoMessage() {} -func (*PlainTextChange) Descriptor() ([]byte, []int) { - return fileDescriptor_c07268f9f08f2beb, []int{0} -} -func (m *PlainTextChange) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *PlainTextChange) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_PlainTextChange.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *PlainTextChange) XXX_Merge(src proto.Message) { - xxx_messageInfo_PlainTextChange.Merge(m, src) -} -func (m *PlainTextChange) XXX_Size() int { - return m.Size() -} -func (m *PlainTextChange) XXX_DiscardUnknown() { - xxx_messageInfo_PlainTextChange.DiscardUnknown(m) -} - -var xxx_messageInfo_PlainTextChange proto.InternalMessageInfo - -type PlainTextChangeContent struct { - // Types that are valid to be assigned to Value: - // *PlainTextChangeContentValueOfTextAppend - Value IsPlainTextChangeContentValue `protobuf_oneof:"value"` -} - -func (m *PlainTextChangeContent) Reset() { *m = PlainTextChangeContent{} } -func (m *PlainTextChangeContent) String() string { return proto.CompactTextString(m) } -func (*PlainTextChangeContent) ProtoMessage() {} -func (*PlainTextChangeContent) Descriptor() ([]byte, []int) { - return fileDescriptor_c07268f9f08f2beb, []int{0, 0} -} -func (m *PlainTextChangeContent) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *PlainTextChangeContent) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_PlainTextChangeContent.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *PlainTextChangeContent) XXX_Merge(src proto.Message) { - xxx_messageInfo_PlainTextChangeContent.Merge(m, src) -} -func (m *PlainTextChangeContent) XXX_Size() int { - return m.Size() -} -func (m *PlainTextChangeContent) XXX_DiscardUnknown() { - xxx_messageInfo_PlainTextChangeContent.DiscardUnknown(m) -} - -var xxx_messageInfo_PlainTextChangeContent proto.InternalMessageInfo - -type IsPlainTextChangeContentValue interface { - IsPlainTextChangeContentValue() - MarshalTo([]byte) (int, error) - Size() int -} - -type PlainTextChangeContentValueOfTextAppend struct { - TextAppend *PlainTextChangeTextAppend `protobuf:"bytes,1,opt,name=textAppend,proto3,oneof" json:"textAppend,omitempty"` -} - -func (*PlainTextChangeContentValueOfTextAppend) IsPlainTextChangeContentValue() {} - -func (m *PlainTextChangeContent) GetValue() IsPlainTextChangeContentValue { - if m != nil { - return m.Value - } - return nil -} - -func (m *PlainTextChangeContent) GetTextAppend() *PlainTextChangeTextAppend { - if x, ok := m.GetValue().(*PlainTextChangeContentValueOfTextAppend); ok { - return x.TextAppend - } - return nil -} - -// XXX_OneofWrappers is for the internal use of the proto package. -func (*PlainTextChangeContent) XXX_OneofWrappers() []interface{} { - return []interface{}{ - (*PlainTextChangeContentValueOfTextAppend)(nil), - } -} - -type PlainTextChangeTextAppend struct { - Text string `protobuf:"bytes,1,opt,name=text,proto3" json:"text,omitempty"` -} - -func (m *PlainTextChangeTextAppend) Reset() { *m = PlainTextChangeTextAppend{} } -func (m *PlainTextChangeTextAppend) String() string { return proto.CompactTextString(m) } -func (*PlainTextChangeTextAppend) ProtoMessage() {} -func (*PlainTextChangeTextAppend) Descriptor() ([]byte, []int) { - return fileDescriptor_c07268f9f08f2beb, []int{0, 1} -} -func (m *PlainTextChangeTextAppend) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *PlainTextChangeTextAppend) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_PlainTextChangeTextAppend.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *PlainTextChangeTextAppend) XXX_Merge(src proto.Message) { - xxx_messageInfo_PlainTextChangeTextAppend.Merge(m, src) -} -func (m *PlainTextChangeTextAppend) XXX_Size() int { - return m.Size() -} -func (m *PlainTextChangeTextAppend) XXX_DiscardUnknown() { - xxx_messageInfo_PlainTextChangeTextAppend.DiscardUnknown(m) -} - -var xxx_messageInfo_PlainTextChangeTextAppend proto.InternalMessageInfo - -func (m *PlainTextChangeTextAppend) GetText() string { - if m != nil { - return m.Text - } - return "" -} - -type PlainTextChangeSnapshot struct { - Text string `protobuf:"bytes,1,opt,name=text,proto3" json:"text,omitempty"` -} - -func (m *PlainTextChangeSnapshot) Reset() { *m = PlainTextChangeSnapshot{} } -func (m *PlainTextChangeSnapshot) String() string { return proto.CompactTextString(m) } -func (*PlainTextChangeSnapshot) ProtoMessage() {} -func (*PlainTextChangeSnapshot) Descriptor() ([]byte, []int) { - return fileDescriptor_c07268f9f08f2beb, []int{0, 2} -} -func (m *PlainTextChangeSnapshot) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *PlainTextChangeSnapshot) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_PlainTextChangeSnapshot.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *PlainTextChangeSnapshot) XXX_Merge(src proto.Message) { - xxx_messageInfo_PlainTextChangeSnapshot.Merge(m, src) -} -func (m *PlainTextChangeSnapshot) XXX_Size() int { - return m.Size() -} -func (m *PlainTextChangeSnapshot) XXX_DiscardUnknown() { - xxx_messageInfo_PlainTextChangeSnapshot.DiscardUnknown(m) -} - -var xxx_messageInfo_PlainTextChangeSnapshot proto.InternalMessageInfo - -func (m *PlainTextChangeSnapshot) GetText() string { - if m != nil { - return m.Text - } - return "" -} - -type PlainTextChangeData struct { - Content []*PlainTextChangeContent `protobuf:"bytes,1,rep,name=content,proto3" json:"content,omitempty"` - Snapshot *PlainTextChangeSnapshot `protobuf:"bytes,2,opt,name=snapshot,proto3" json:"snapshot,omitempty"` -} - -func (m *PlainTextChangeData) Reset() { *m = PlainTextChangeData{} } -func (m *PlainTextChangeData) String() string { return proto.CompactTextString(m) } -func (*PlainTextChangeData) ProtoMessage() {} -func (*PlainTextChangeData) Descriptor() ([]byte, []int) { - return fileDescriptor_c07268f9f08f2beb, []int{0, 3} -} -func (m *PlainTextChangeData) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *PlainTextChangeData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_PlainTextChangeData.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *PlainTextChangeData) XXX_Merge(src proto.Message) { - xxx_messageInfo_PlainTextChangeData.Merge(m, src) -} -func (m *PlainTextChangeData) XXX_Size() int { - return m.Size() -} -func (m *PlainTextChangeData) XXX_DiscardUnknown() { - xxx_messageInfo_PlainTextChangeData.DiscardUnknown(m) -} - -var xxx_messageInfo_PlainTextChangeData proto.InternalMessageInfo - -func (m *PlainTextChangeData) GetContent() []*PlainTextChangeContent { - if m != nil { - return m.Content - } - return nil -} - -func (m *PlainTextChangeData) GetSnapshot() *PlainTextChangeSnapshot { - if m != nil { - return m.Snapshot - } - return nil -} - -func init() { - proto.RegisterType((*PlainTextChange)(nil), "anytype.PlainTextChange") - proto.RegisterType((*PlainTextChangeContent)(nil), "anytype.PlainTextChange.Content") - proto.RegisterType((*PlainTextChangeTextAppend)(nil), "anytype.PlainTextChange.TextAppend") - proto.RegisterType((*PlainTextChangeSnapshot)(nil), "anytype.PlainTextChange.Snapshot") - proto.RegisterType((*PlainTextChangeData)(nil), "anytype.PlainTextChange.Data") -} - -func init() { - proto.RegisterFile("pkg/acl/testutils/testchanges/testchangepb/protos/testdocumentchanges.proto", fileDescriptor_c07268f9f08f2beb) -} - -var fileDescriptor_c07268f9f08f2beb = []byte{ - // 278 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xf2, 0x2e, 0xc8, 0x4e, 0xd7, - 0x4f, 0x4c, 0xce, 0xd1, 0x2f, 0x49, 0x2d, 0x2e, 0x29, 0x2d, 0xc9, 0xcc, 0x29, 0x06, 0xb3, 0x92, - 0x33, 0x12, 0xf3, 0xd2, 0x53, 0x91, 0xd9, 0x05, 0x49, 0xfa, 0x05, 0x45, 0xf9, 0x25, 0xf9, 0x10, - 0xb1, 0x94, 0xfc, 0xe4, 0xd2, 0xdc, 0xd4, 0x3c, 0x98, 0x3a, 0x3d, 0xb0, 0x94, 0x10, 0x7b, 0x62, - 0x5e, 0x65, 0x49, 0x65, 0x41, 0xaa, 0xd2, 0x26, 0x26, 0x2e, 0xfe, 0x80, 0x9c, 0xc4, 0xcc, 0xbc, - 0x90, 0xd4, 0x8a, 0x12, 0x67, 0xb0, 0x1a, 0xa9, 0x48, 0x2e, 0x76, 0xe7, 0xfc, 0xbc, 0x92, 0xd4, - 0xbc, 0x12, 0x21, 0x57, 0x2e, 0xae, 0x92, 0xd4, 0x8a, 0x12, 0xc7, 0x82, 0x82, 0xd4, 0xbc, 0x14, - 0x09, 0x46, 0x05, 0x46, 0x0d, 0x6e, 0x23, 0x65, 0x3d, 0xa8, 0x66, 0x3d, 0x34, 0x8d, 0x7a, 0x21, - 0x70, 0xa5, 0x1e, 0x0c, 0x41, 0x48, 0x1a, 0x9d, 0xd8, 0xb9, 0x58, 0xcb, 0x12, 0x73, 0x4a, 0x53, - 0xa5, 0x14, 0xb8, 0xb8, 0x10, 0x8a, 0x84, 0x84, 0xb8, 0x58, 0x40, 0x8a, 0xc0, 0xe6, 0x72, 0x06, - 0x81, 0xd9, 0x52, 0x72, 0x5c, 0x1c, 0xc1, 0x79, 0x89, 0x05, 0xc5, 0x19, 0xf9, 0x25, 0x58, 0xe5, - 0x1b, 0x19, 0xb9, 0x58, 0x5c, 0x12, 0x4b, 0x12, 0x85, 0xac, 0xb8, 0xd8, 0x93, 0x21, 0xae, 0x94, - 0x60, 0x54, 0x60, 0xd6, 0xe0, 0x36, 0x52, 0xc0, 0xe9, 0x2e, 0xa8, 0x6f, 0x82, 0x60, 0x1a, 0x84, - 0x6c, 0xb9, 0x38, 0x8a, 0xa1, 0x96, 0x48, 0x30, 0x81, 0x3d, 0xa5, 0x88, 0x53, 0x33, 0xcc, 0x35, - 0x41, 0x70, 0x2d, 0x4e, 0x6a, 0x27, 0x1e, 0xc9, 0x31, 0x5e, 0x78, 0x24, 0xc7, 0xf8, 0xe0, 0x91, - 0x1c, 0xe3, 0x84, 0xc7, 0x72, 0x0c, 0x17, 0x1e, 0xcb, 0x31, 0xdc, 0x78, 0x2c, 0xc7, 0x10, 0xc5, - 0x83, 0x1c, 0x0d, 0x49, 0x6c, 0xe0, 0xc0, 0x36, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0xfd, 0x73, - 0xe1, 0xf2, 0xbb, 0x01, 0x00, 0x00, -} - -func (m *PlainTextChange) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *PlainTextChange) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *PlainTextChange) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - return len(dAtA) - i, nil -} - -func (m *PlainTextChangeContent) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *PlainTextChangeContent) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *PlainTextChangeContent) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.Value != nil { - { - size := m.Value.Size() - i -= size - if _, err := m.Value.MarshalTo(dAtA[i:]); err != nil { - return 0, err - } - } - } - return len(dAtA) - i, nil -} - -func (m *PlainTextChangeContentValueOfTextAppend) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *PlainTextChangeContentValueOfTextAppend) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - if m.TextAppend != nil { - { - size, err := m.TextAppend.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintTestdocumentchanges(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} -func (m *PlainTextChangeTextAppend) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *PlainTextChangeTextAppend) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *PlainTextChangeTextAppend) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if len(m.Text) > 0 { - i -= len(m.Text) - copy(dAtA[i:], m.Text) - i = encodeVarintTestdocumentchanges(dAtA, i, uint64(len(m.Text))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func (m *PlainTextChangeSnapshot) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *PlainTextChangeSnapshot) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *PlainTextChangeSnapshot) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if len(m.Text) > 0 { - i -= len(m.Text) - copy(dAtA[i:], m.Text) - i = encodeVarintTestdocumentchanges(dAtA, i, uint64(len(m.Text))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func (m *PlainTextChangeData) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *PlainTextChangeData) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *PlainTextChangeData) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.Snapshot != nil { - { - size, err := m.Snapshot.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintTestdocumentchanges(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x12 - } - if len(m.Content) > 0 { - for iNdEx := len(m.Content) - 1; iNdEx >= 0; iNdEx-- { - { - size, err := m.Content[iNdEx].MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintTestdocumentchanges(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0xa - } - } - return len(dAtA) - i, nil -} - -func encodeVarintTestdocumentchanges(dAtA []byte, offset int, v uint64) int { - offset -= sovTestdocumentchanges(v) - base := offset - for v >= 1<<7 { - dAtA[offset] = uint8(v&0x7f | 0x80) - v >>= 7 - offset++ - } - dAtA[offset] = uint8(v) - return base -} -func (m *PlainTextChange) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - return n -} - -func (m *PlainTextChangeContent) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.Value != nil { - n += m.Value.Size() - } - return n -} - -func (m *PlainTextChangeContentValueOfTextAppend) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.TextAppend != nil { - l = m.TextAppend.Size() - n += 1 + l + sovTestdocumentchanges(uint64(l)) - } - return n -} -func (m *PlainTextChangeTextAppend) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Text) - if l > 0 { - n += 1 + l + sovTestdocumentchanges(uint64(l)) - } - return n -} - -func (m *PlainTextChangeSnapshot) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Text) - if l > 0 { - n += 1 + l + sovTestdocumentchanges(uint64(l)) - } - return n -} - -func (m *PlainTextChangeData) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if len(m.Content) > 0 { - for _, e := range m.Content { - l = e.Size() - n += 1 + l + sovTestdocumentchanges(uint64(l)) - } - } - if m.Snapshot != nil { - l = m.Snapshot.Size() - n += 1 + l + sovTestdocumentchanges(uint64(l)) - } - return n -} - -func sovTestdocumentchanges(x uint64) (n int) { - return (math_bits.Len64(x|1) + 6) / 7 -} -func sozTestdocumentchanges(x uint64) (n int) { - return sovTestdocumentchanges(uint64((x << 1) ^ uint64((int64(x) >> 63)))) -} -func (m *PlainTextChange) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTestdocumentchanges - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: PlainTextChange: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: PlainTextChange: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - default: - iNdEx = preIndex - skippy, err := skipTestdocumentchanges(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthTestdocumentchanges - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *PlainTextChangeContent) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTestdocumentchanges - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: Content: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: Content: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field TextAppend", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTestdocumentchanges - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthTestdocumentchanges - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthTestdocumentchanges - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - v := &PlainTextChangeTextAppend{} - if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - m.Value = &PlainTextChangeContentValueOfTextAppend{v} - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipTestdocumentchanges(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthTestdocumentchanges - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *PlainTextChangeTextAppend) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTestdocumentchanges - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: TextAppend: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: TextAppend: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Text", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTestdocumentchanges - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthTestdocumentchanges - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthTestdocumentchanges - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Text = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipTestdocumentchanges(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthTestdocumentchanges - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *PlainTextChangeSnapshot) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTestdocumentchanges - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: Snapshot: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: Snapshot: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Text", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTestdocumentchanges - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthTestdocumentchanges - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthTestdocumentchanges - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Text = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipTestdocumentchanges(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthTestdocumentchanges - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *PlainTextChangeData) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTestdocumentchanges - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: Data: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: Data: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Content", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTestdocumentchanges - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthTestdocumentchanges - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthTestdocumentchanges - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Content = append(m.Content, &PlainTextChangeContent{}) - if err := m.Content[len(m.Content)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Snapshot", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTestdocumentchanges - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthTestdocumentchanges - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthTestdocumentchanges - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.Snapshot == nil { - m.Snapshot = &PlainTextChangeSnapshot{} - } - if err := m.Snapshot.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipTestdocumentchanges(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthTestdocumentchanges - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func skipTestdocumentchanges(dAtA []byte) (n int, err error) { - l := len(dAtA) - iNdEx := 0 - depth := 0 - for iNdEx < l { - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowTestdocumentchanges - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - wireType := int(wire & 0x7) - switch wireType { - case 0: - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowTestdocumentchanges - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - iNdEx++ - if dAtA[iNdEx-1] < 0x80 { - break - } - } - case 1: - iNdEx += 8 - case 2: - var length int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowTestdocumentchanges - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - length |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if length < 0 { - return 0, ErrInvalidLengthTestdocumentchanges - } - iNdEx += length - case 3: - depth++ - case 4: - if depth == 0 { - return 0, ErrUnexpectedEndOfGroupTestdocumentchanges - } - depth-- - case 5: - iNdEx += 4 - default: - return 0, fmt.Errorf("proto: illegal wireType %d", wireType) - } - if iNdEx < 0 { - return 0, ErrInvalidLengthTestdocumentchanges - } - if depth == 0 { - return iNdEx, nil - } - } - return 0, io.ErrUnexpectedEOF -} - -var ( - ErrInvalidLengthTestdocumentchanges = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowTestdocumentchanges = fmt.Errorf("proto: integer overflow") - ErrUnexpectedEndOfGroupTestdocumentchanges = fmt.Errorf("proto: unexpected end of group") -) diff --git a/pkg/acl/testutils/treestoragebuilder/treestoragebuilder.go b/pkg/acl/testutils/treestoragebuilder/treestoragebuilder.go deleted file mode 100644 index f7bb65bd..00000000 --- a/pkg/acl/testutils/treestoragebuilder/treestoragebuilder.go +++ /dev/null @@ -1,539 +0,0 @@ -package treestoragebuilder - -import ( - "context" - "fmt" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb" - testpb "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/testutils/testchanges/testchangepb" - "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/testutils/yamltests" - storagepb "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/treestorage/treepb" - "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/encryptionkey" - "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/signingkey" - "github.com/anytypeio/go-anytype-infrastructure-experiments/util/slice" - "io/ioutil" - "path" - - "github.com/gogo/protobuf/proto" - "gopkg.in/yaml.v3" -) - -const plainTextDocType uint16 = 1 - -type treeChange struct { - *aclpb.ACLChange - id string - readKey *SymKey - signKey signingkey.PrivKey - - changesDataDecrypted []byte -} - -type updateUseCase struct { - changes map[string]*treeChange -} - -type TreeStorageBuilder struct { - treeId string - allChanges map[string]*treeChange - updates map[string]*updateUseCase - heads []string - orphans []string - keychain *Keychain - header *storagepb.TreeHeader -} - -func NewTreeStorageBuilder(keychain *Keychain) *TreeStorageBuilder { - return &TreeStorageBuilder{ - allChanges: make(map[string]*treeChange), - updates: make(map[string]*updateUseCase), - keychain: keychain, - } -} - -func NewTreeStorageBuilderWithTestName(name string) (*TreeStorageBuilder, error) { - filePath := path.Join(yamltests.Path(), name) - return NewTreeStorageBuilderFromFile(filePath) -} - -func NewTreeStorageBuilderFromFile(file string) (*TreeStorageBuilder, error) { - content, err := ioutil.ReadFile(file) - if err != nil { - return nil, err - } - - ymlTree := YMLTree{} - err = yaml.Unmarshal(content, &ymlTree) - if err != nil { - return nil, err - } - - tb := NewTreeStorageBuilder(NewKeychain()) - tb.Parse(&ymlTree) - - return tb, nil -} - -func (t *TreeStorageBuilder) TreeID() (string, error) { - return t.treeId, nil -} - -func (t *TreeStorageBuilder) GetKeychain() *Keychain { - return t.keychain -} - -func (t *TreeStorageBuilder) Heads() ([]string, error) { - return t.heads, nil -} - -func (t *TreeStorageBuilder) AddRawChange(change *aclpb.RawChange) error { - aclChange := new(aclpb.ACLChange) - var err error - - if err = proto.Unmarshal(change.Payload, aclChange); err != nil { - return fmt.Errorf("could not unmarshall changes") - } - var changesData []byte - - // get correct readkey - readKey := t.keychain.ReadKeysByHash[aclChange.CurrentReadKeyHash] - if aclChange.ChangesData != nil { - changesData, err = readKey.Key.Decrypt(aclChange.ChangesData) - if err != nil { - return fmt.Errorf("failed to decrypt changes data: %w", err) - } - } - - // get correct signing key - signKey := t.keychain.SigningKeysByIdentity[aclChange.Identity] - - t.allChanges[change.Id] = &treeChange{ - ACLChange: aclChange, - id: change.Id, - readKey: readKey, - signKey: signKey, - changesDataDecrypted: changesData, - } - return nil -} - -func (t *TreeStorageBuilder) AddOrphans(orphans ...string) error { - t.orphans = append(t.orphans, orphans...) - return nil -} - -func (t *TreeStorageBuilder) AddChange(change aclchanges.Change) error { - aclChange := change.ProtoChange() - var err error - var changesData []byte - - // get correct readkey - readKey := t.keychain.ReadKeysByHash[aclChange.CurrentReadKeyHash] - if aclChange.ChangesData != nil { - changesData, err = readKey.Key.Decrypt(aclChange.ChangesData) - if err != nil { - return fmt.Errorf("failed to decrypt changes data: %w", err) - } - } - - // get correct signing key - signKey := t.keychain.SigningKeysByIdentity[aclChange.Identity] - - t.allChanges[change.CID()] = &treeChange{ - ACLChange: aclChange, - id: change.CID(), - readKey: readKey, - signKey: signKey, - changesDataDecrypted: changesData, - } - return nil -} - -func (t *TreeStorageBuilder) Orphans() ([]string, error) { - return t.orphans, nil -} - -func (t *TreeStorageBuilder) SetHeads(heads []string) error { - // we should copy here instead of just setting the value - t.heads = heads - return nil -} - -func (t *TreeStorageBuilder) RemoveOrphans(orphans ...string) error { - t.orphans = slice.Difference(t.orphans, orphans) - return nil -} - -func (t *TreeStorageBuilder) GetChange(ctx context.Context, recordID string) (*aclpb.RawChange, error) { - return t.getChange(recordID, t.allChanges), nil -} - -func (t *TreeStorageBuilder) GetUpdates(useCase string) []*aclpb.RawChange { - var res []*aclpb.RawChange - update := t.updates[useCase] - for _, ch := range update.changes { - rawCh := t.getChange(ch.id, update.changes) - res = append(res, rawCh) - } - return res -} - -func (t *TreeStorageBuilder) Header() (*storagepb.TreeHeader, error) { - return t.header, nil -} - -func (t *TreeStorageBuilder) getChange(changeId string, m map[string]*treeChange) *aclpb.RawChange { - rec := m[changeId] - - if rec.changesDataDecrypted != nil { - encrypted, err := rec.readKey.Key.Encrypt(rec.changesDataDecrypted) - if err != nil { - panic("should be able to encrypt data with read key!") - } - - rec.ChangesData = encrypted - } - - aclMarshaled, err := proto.Marshal(rec.ACLChange) - if err != nil { - panic("should be able to marshal final acl message!") - } - - signature, err := rec.signKey.Sign(aclMarshaled) - if err != nil { - panic("should be able to sign final acl message!") - } - - transformedRec := &aclpb.RawChange{ - Payload: aclMarshaled, - Signature: signature, - Id: changeId, - } - return transformedRec -} - -func (t *TreeStorageBuilder) Parse(tree *YMLTree) { - // Just to clarify - we are generating new identities for the ones that - // are specified in the yml file, because our identities should be Ed25519 - // the same thing is happening for the encryption keys - t.keychain.ParseKeys(&tree.Keys) - t.treeId = t.parseTreeId(tree.Description) - for _, ch := range tree.Changes { - newChange := t.parseChange(ch) - t.allChanges[newChange.id] = newChange - } - - t.parseGraph(tree) - t.parseOrphans(tree) - t.parseHeader(tree) - t.parseUpdates(tree.Updates) -} - -func (t *TreeStorageBuilder) parseChange(ch *Change) *treeChange { - newChange := &treeChange{ - id: ch.Id, - } - k := t.keychain.GetKey(ch.ReadKey).(*SymKey) - newChange.readKey = k - newChange.signKey = t.keychain.SigningKeys[ch.Identity] - aclChange := &aclpb.ACLChange{} - aclChange.Identity = t.keychain.GetIdentity(ch.Identity) - if len(ch.AclChanges) > 0 || ch.AclSnapshot != nil { - aclChange.AclData = &aclpb.ACLChangeACLData{} - if ch.AclSnapshot != nil { - aclChange.AclData.AclSnapshot = t.parseACLSnapshot(ch.AclSnapshot) - } - if ch.AclChanges != nil { - var aclChangeContents []*aclpb.ACLChangeACLContentValue - for _, ch := range ch.AclChanges { - aclChangeContent := t.parseACLChange(ch) - aclChangeContents = append(aclChangeContents, aclChangeContent) - } - aclChange.AclData.AclContent = aclChangeContents - } - } - if len(ch.Changes) > 0 || ch.Snapshot != nil { - changesData := &testpb.PlainTextChangeData{} - if ch.Snapshot != nil { - changesData.Snapshot = t.parseChangeSnapshot(ch.Snapshot) - } - if len(ch.Changes) > 0 { - var changeContents []*testpb.PlainTextChangeContent - for _, ch := range ch.Changes { - aclChangeContent := t.parseDocumentChange(ch) - changeContents = append(changeContents, aclChangeContent) - } - changesData.Content = changeContents - } - m, err := proto.Marshal(changesData) - if err != nil { - return nil - } - newChange.changesDataDecrypted = m - } - aclChange.CurrentReadKeyHash = k.Hash - newChange.ACLChange = aclChange - return newChange -} - -func (t *TreeStorageBuilder) parseTreeId(description *TreeDescription) string { - if description == nil { - panic("no author in tree") - } - return description.Author + ".tree.id" -} - -func (t *TreeStorageBuilder) parseChangeSnapshot(s *PlainTextSnapshot) *testpb.PlainTextChangeSnapshot { - return &testpb.PlainTextChangeSnapshot{ - Text: s.Text, - } -} - -func (t *TreeStorageBuilder) parseACLSnapshot(s *ACLSnapshot) *aclpb.ACLChangeACLSnapshot { - newState := &aclpb.ACLChangeACLState{} - for _, state := range s.UserStates { - aclUserState := &aclpb.ACLChangeUserState{} - aclUserState.Identity = t.keychain.GetIdentity(state.Identity) - - encKey := t.keychain. - GetKey(state.EncryptionKey).(encryptionkey.PrivKey) - rawKey, _ := encKey.GetPublic().Raw() - aclUserState.EncryptionKey = rawKey - - aclUserState.EncryptedReadKeys = t.encryptReadKeys(state.EncryptedReadKeys, encKey) - aclUserState.Permissions = t.convertPermission(state.Permissions) - newState.UserStates = append(newState.UserStates, aclUserState) - } - return &aclpb.ACLChangeACLSnapshot{ - AclState: newState, - } -} - -func (t *TreeStorageBuilder) parseDocumentChange(ch *PlainTextChange) (convCh *testpb.PlainTextChangeContent) { - switch { - case ch.TextAppend != nil: - convCh = &testpb.PlainTextChangeContent{ - Value: &testpb.PlainTextChangeContentValueOfTextAppend{ - TextAppend: &testpb.PlainTextChangeTextAppend{ - Text: ch.TextAppend.Text, - }, - }, - } - } - if convCh == nil { - panic("cannot have empty document change") - } - - return convCh -} - -func (t *TreeStorageBuilder) parseACLChange(ch *ACLChange) (convCh *aclpb.ACLChangeACLContentValue) { - switch { - case ch.UserAdd != nil: - add := ch.UserAdd - - encKey := t.keychain. - GetKey(add.EncryptionKey).(encryptionkey.PrivKey) - rawKey, _ := encKey.GetPublic().Raw() - - convCh = &aclpb.ACLChangeACLContentValue{ - Value: &aclpb.ACLChangeACLContentValueValueOfUserAdd{ - UserAdd: &aclpb.ACLChangeUserAdd{ - Identity: t.keychain.GetIdentity(add.Identity), - EncryptionKey: rawKey, - EncryptedReadKeys: t.encryptReadKeys(add.EncryptedReadKeys, encKey), - Permissions: t.convertPermission(add.Permission), - }, - }, - } - case ch.UserJoin != nil: - join := ch.UserJoin - - encKey := t.keychain. - GetKey(join.EncryptionKey).(encryptionkey.PrivKey) - rawKey, _ := encKey.GetPublic().Raw() - - idKey, _ := t.keychain.SigningKeys[join.Identity].GetPublic().Raw() - signKey := t.keychain.GetKey(join.AcceptSignature).(signingkey.PrivKey) - signature, err := signKey.Sign(idKey) - if err != nil { - panic(err) - } - - convCh = &aclpb.ACLChangeACLContentValue{ - Value: &aclpb.ACLChangeACLContentValueValueOfUserJoin{ - UserJoin: &aclpb.ACLChangeUserJoin{ - Identity: t.keychain.GetIdentity(join.Identity), - EncryptionKey: rawKey, - AcceptSignature: signature, - UserInviteId: join.InviteId, - EncryptedReadKeys: t.encryptReadKeys(join.EncryptedReadKeys, encKey), - }, - }, - } - case ch.UserInvite != nil: - invite := ch.UserInvite - rawAcceptKey, _ := t.keychain.GetKey(invite.AcceptKey).(signingkey.PrivKey).GetPublic().Raw() - encKey := t.keychain. - GetKey(invite.EncryptionKey).(encryptionkey.PrivKey) - rawEncKey, _ := encKey.GetPublic().Raw() - - convCh = &aclpb.ACLChangeACLContentValue{ - Value: &aclpb.ACLChangeACLContentValueValueOfUserInvite{ - UserInvite: &aclpb.ACLChangeUserInvite{ - AcceptPublicKey: rawAcceptKey, - EncryptPublicKey: rawEncKey, - EncryptedReadKeys: t.encryptReadKeys(invite.EncryptedReadKeys, encKey), - Permissions: t.convertPermission(invite.Permissions), - InviteId: invite.InviteId, - }, - }, - } - case ch.UserConfirm != nil: - confirm := ch.UserConfirm - - convCh = &aclpb.ACLChangeACLContentValue{ - Value: &aclpb.ACLChangeACLContentValueValueOfUserConfirm{ - UserConfirm: &aclpb.ACLChangeUserConfirm{ - Identity: t.keychain.GetIdentity(confirm.Identity), - UserAddId: confirm.UserAddId, - }, - }, - } - case ch.UserPermissionChange != nil: - permissionChange := ch.UserPermissionChange - - convCh = &aclpb.ACLChangeACLContentValue{ - Value: &aclpb.ACLChangeACLContentValueValueOfUserPermissionChange{ - UserPermissionChange: &aclpb.ACLChangeUserPermissionChange{ - Identity: t.keychain.GetIdentity(permissionChange.Identity), - Permissions: t.convertPermission(permissionChange.Permission), - }, - }, - } - case ch.UserRemove != nil: - remove := ch.UserRemove - - newReadKey := t.keychain.GetKey(remove.NewReadKey).(*SymKey) - - var replaces []*aclpb.ACLChangeReadKeyReplace - for _, id := range remove.IdentitiesLeft { - identity := t.keychain.GetIdentity(id) - encKey := t.keychain.EncryptionKeys[id] - rawEncKey, _ := encKey.GetPublic().Raw() - encReadKey, err := encKey.GetPublic().Encrypt(newReadKey.Key.Bytes()) - if err != nil { - panic(err) - } - replaces = append(replaces, &aclpb.ACLChangeReadKeyReplace{ - Identity: identity, - EncryptionKey: rawEncKey, - EncryptedReadKey: encReadKey, - }) - } - - convCh = &aclpb.ACLChangeACLContentValue{ - Value: &aclpb.ACLChangeACLContentValueValueOfUserRemove{ - UserRemove: &aclpb.ACLChangeUserRemove{ - Identity: t.keychain.GetIdentity(remove.RemovedIdentity), - ReadKeyReplaces: replaces, - }, - }, - } - } - if convCh == nil { - panic("cannot have empty acl change") - } - - return convCh -} - -func (t *TreeStorageBuilder) encryptReadKeys(keys []string, encKey encryptionkey.PrivKey) (enc [][]byte) { - for _, k := range keys { - realKey := t.keychain.GetKey(k).(*SymKey).Key.Bytes() - res, err := encKey.GetPublic().Encrypt(realKey) - if err != nil { - panic(err) - } - - enc = append(enc, res) - } - return -} - -func (t *TreeStorageBuilder) convertPermission(perm string) aclpb.ACLChangeUserPermissions { - switch perm { - case "admin": - return aclpb.ACLChange_Admin - case "writer": - return aclpb.ACLChange_Writer - case "reader": - return aclpb.ACLChange_Reader - default: - panic(fmt.Sprintf("incorrect permission: %s", perm)) - } -} - -func (t *TreeStorageBuilder) traverseFromHeads(f func(t *treeChange) error) error { - uniqMap := map[string]struct{}{} - stack := make([]string, len(t.orphans), 10) - copy(stack, t.orphans) - for len(stack) > 0 { - id := stack[len(stack)-1] - stack = stack[:len(stack)-1] - if _, exists := uniqMap[id]; exists { - continue - } - - ch := t.allChanges[id] - uniqMap[id] = struct{}{} - if err := f(ch); err != nil { - return err - } - - for _, prev := range ch.ACLChange.TreeHeadIds { - stack = append(stack, prev) - } - } - return nil -} - -func (t *TreeStorageBuilder) parseUpdates(updates []*Update) { - for _, update := range updates { - useCase := &updateUseCase{ - changes: map[string]*treeChange{}, - } - for _, ch := range update.Changes { - newChange := t.parseChange(ch) - useCase.changes[newChange.id] = newChange - } - for _, node := range update.Graph { - rec := useCase.changes[node.Id] - rec.AclHeadIds = node.ACLHeads - rec.TreeHeadIds = node.TreeHeads - rec.SnapshotBaseId = node.BaseSnapshot - } - - t.updates[update.UseCase] = useCase - } -} - -func (t *TreeStorageBuilder) parseGraph(tree *YMLTree) { - for _, node := range tree.Graph { - rec := t.allChanges[node.Id] - rec.AclHeadIds = node.ACLHeads - rec.TreeHeadIds = node.TreeHeads - rec.SnapshotBaseId = node.BaseSnapshot - } -} - -func (t *TreeStorageBuilder) parseOrphans(tree *YMLTree) { - t.orphans = tree.Orphans -} - -func (t *TreeStorageBuilder) parseHeader(tree *YMLTree) { - t.header = &storagepb.TreeHeader{ - FirstChangeId: tree.Header.FirstChangeId, - IsWorkspace: tree.Header.IsWorkspace, - } -} diff --git a/pkg/acl/testutils/treestoragebuilder/treestoragebuildergraph_nix.go b/pkg/acl/testutils/treestoragebuilder/treestoragebuildergraph_nix.go deleted file mode 100644 index 935fd711..00000000 --- a/pkg/acl/testutils/treestoragebuilder/treestoragebuildergraph_nix.go +++ /dev/null @@ -1,162 +0,0 @@ -//go:build (linux || darwin) && !android && !ios && !nographviz && (amd64 || arm64) -// +build linux darwin -// +build !android -// +build !ios -// +build !nographviz -// +build amd64 arm64 - -package treestoragebuilder - -import ( - "fmt" - testpb "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/testutils/testchanges/testchangepb" - - "github.com/gogo/protobuf/proto" - "strings" - "unicode" - - "github.com/awalterschulze/gographviz" -) - -// To quickly look at visualized string you can use https://dreampuf.github.io/GraphvizOnline - -type EdgeParameters struct { - style string - color string - label string -} - -func (t *TreeStorageBuilder) Graph() (string, error) { - // TODO: check updates on https://github.com/goccy/go-graphviz/issues/52 or make a fix yourself to use better library here - graph := gographviz.NewGraph() - graph.SetName("G") - graph.SetDir(true) - var nodes = make(map[string]struct{}) - - var addNodes = func(r *treeChange) error { - // TODO: revisit function after checking - - style := "solid" - if r.GetAclData() != nil { - style = "filled" - } else if r.changesDataDecrypted != nil { - style = "dashed" - } - - var chSymbs []string - if r.changesDataDecrypted != nil { - res := &testpb.PlainTextChangeData{} - err := proto.Unmarshal(r.changesDataDecrypted, res) - if err != nil { - return err - } - - for _, chc := range res.Content { - tp := fmt.Sprintf("%T", chc.Value) - tp = strings.Replace(tp, "ChangeContentValueOf", "", 1) - res := "" - for _, ts := range tp { - if unicode.IsUpper(ts) { - res += string(ts) - } - } - chSymbs = append(chSymbs, res) - } - } - if r.GetAclData() != nil { - for _, chc := range r.GetAclData().AclContent { - tp := fmt.Sprintf("%T", chc.Value) - tp = strings.Replace(tp, "ACLChangeACLContentValueValueOf", "", 1) - res := "" - for _, ts := range tp { - if unicode.IsUpper(ts) { - res += string(ts) - } - } - chSymbs = append(chSymbs, res) - } - } - - shortId := r.id - label := fmt.Sprintf("Id: %s\nChanges: %s\n", - shortId, - strings.Join(chSymbs, ","), - ) - e := graph.AddNode("G", "\""+r.id+"\"", map[string]string{ - "label": "\"" + label + "\"", - "style": "\"" + style + "\"", - }) - if e != nil { - return e - } - nodes[r.id] = struct{}{} - return nil - } - - var createEdge = func(firstId, secondId string, params EdgeParameters) error { - _, exists := nodes[firstId] - if !exists { - return fmt.Errorf("no such node") - } - _, exists = nodes[secondId] - if !exists { - return fmt.Errorf("no previous node") - } - - err := graph.AddEdge("\""+firstId+"\"", "\""+secondId+"\"", true, map[string]string{ - "color": params.color, - "style": params.style, - }) - if err != nil { - return err - } - - return nil - } - - var addLinks = func(t *treeChange) error { - for _, prevId := range t.AclHeadIds { - err := createEdge(t.id, prevId, EdgeParameters{ - style: "dashed", - color: "red", - }) - if err != nil { - return err - } - } - - for _, prevId := range t.TreeHeadIds { - err := createEdge(t.id, prevId, EdgeParameters{ - style: "dashed", - color: "blue", - }) - if err != nil { - return err - } - } - - if t.SnapshotBaseId != "" { - err := createEdge(t.id, t.SnapshotBaseId, EdgeParameters{ - style: "bold", - color: "blue", - }) - if err != nil { - return err - } - } - - return nil - } - - err := t.traverseFromHeads(addNodes) - if err != nil { - return "", err - } - - err = t.traverseFromHeads(addLinks) - if err != nil { - return "", err - } - - return graph.String(), nil -} diff --git a/pkg/acl/testutils/yamltests/invalidsnapshotexample.yml b/pkg/acl/testutils/yamltests/invalidsnapshotexample.yml deleted file mode 100644 index f3eaf00c..00000000 --- a/pkg/acl/testutils/yamltests/invalidsnapshotexample.yml +++ /dev/null @@ -1,126 +0,0 @@ -tree: - author: A -changes: - - id: A.1.1 - identity: A - aclSnapshot: - userStates: - - identity: A - encryptionKey: key.Enc.A - encryptedReadKeys: [key.Read.1] - permission: admin - - identity: B - encryptionKey: key.Enc.B - encryptedReadKeys: [key.Read.1] - permission: admin - snapshot: - text: "some text" - aclChanges: - - userAdd: - identity: A - permission: admin - encryptionKey: key.Enc.A - encryptedReadKeys: [key.Read.1] - - userAdd: - identity: B - permission: admin - encryptionKey: key.Enc.B - encryptedReadKeys: [key.Read.1] - readKey: key.Read.1 - - id: A.1.2 - identity: A - aclSnapshot: - userStates: - - identity: A - encryptionKey: key.Enc.A - encryptedReadKeys: [key.Read.1] - permission: admin - - identity: B - encryptionKey: key.Enc.B - encryptedReadKeys: [key.Read.1] - permission: admin - - identity: D - encryptionKey: key.Enc.D - encryptedReadKeys: [ key.Read.1 ] - permission: admin - snapshot: - text: "some text" - aclChanges: - - userAdd: - identity: D - permission: admin - encryptionKey: key.Enc.D - encryptedReadKeys: [key.Read.1] - readKey: key.Read.1 - - id: A.1.3 - identity: A - aclChanges: - - userAdd: - identity: E - permission: admin - encryptionKey: key.Enc.E - encryptedReadKeys: [key.Read.1] - readKey: key.Read.1 - - id: B.1.1 - identity: B - aclChanges: - - userAdd: - identity: C - permission: admin - encryptionKey: key.Enc.C - encryptedReadKeys: [ key.Read.1 ] - readKey: key.Read.1 - - id: B.1.2 - identity: B - aclChanges: - - userAdd: - identity: F - permission: admin - encryptionKey: key.Enc.F - encryptedReadKeys: [ key.Read.1 ] - readKey: key.Read.1 -keys: - Enc: - - A - - B - - C - - D - - E - - F - Sign: - - A - - B - - C - - D - - E - - F - Read: - - 1 - - 2 -graph: - - id: A.1.1 - baseSnapshot: A.1.1 - aclSnapshot: A.1.1 - - id: A.1.2 - baseSnapshot: A.1.1 - aclHeads: [B.1.1] - treeHeads: [B.1.1] - - id: B.1.1 - baseSnapshot: A.1.1 - aclHeads: [A.1.1] - treeHeads: [A.1.1] - - id: B.1.2 - baseSnapshot: A.1.2 - aclHeads: [A.1.2] - treeHeads: [A.1.2] - - id: A.1.3 - baseSnapshot: A.1.2 - aclHeads: [A.1.2] - treeHeads: [A.1.2] -header: - firstChangeId: A.1.1 - isWorkspace: false -orphans: - - A.1.3 - - B.1.2 - diff --git a/pkg/acl/testutils/yamltests/userjoinexample.yml b/pkg/acl/testutils/yamltests/userjoinexample.yml index ff15f1f6..9b4ac7f1 100644 --- a/pkg/acl/testutils/yamltests/userjoinexample.yml +++ b/pkg/acl/testutils/yamltests/userjoinexample.yml @@ -1,49 +1,29 @@ -tree: +list: author: A -changes: - - id: A.1.1 - identity: A - aclSnapshot: - userStates: - - identity: A - encryptionKey: key.Enc.A - encryptedReadKeys: [key.Read.1] - permission: admin - snapshot: - text: "some text" +records: + - identity: A aclChanges: - userAdd: identity: A permission: admin encryptionKey: key.Enc.A encryptedReadKeys: [key.Read.1] - changes: - - textAppend: - text: "some text" readKey: key.Read.1 - - id: A.1.2 - identity: A + - identity: A aclChanges: - userInvite: acceptKey: key.Sign.Onetime1 encryptionKey: key.Enc.Onetime1 encryptedReadKeys: [key.Read.1] permissions: writer - inviteId: A.1.2 + inviteIdx: A.1.2 - userAdd: identity: C permission: reader encryptionKey: key.Enc.C - encryptedReadKeys: [ key.Read.1 ] + encryptedReadKeys: [key.Read.1] readKey: key.Read.1 - - id: A.1.3 - identity: A - changes: - - textAppend: - text: "second" - readKey: key.Read.1 - - id: B.1.1 - identity: B + - identity: B aclChanges: - userJoin: identity: B @@ -52,56 +32,25 @@ changes: inviteId: A.1.2 encryptedReadKeys: [key.Read.1] readKey: key.Read.1 - - id: B.1.2 - identity: B - changes: - - textAppend: - text: "first" - readKey: key.Read.1 - - id: C.1.1 - identity: C - changes: - - textAppend: - text: "third" - readKey: key.Read.1 keys: Enc: - - A - - B - - C - - Onetime1 + - name: A + value: generated + - name: B + value: generated + - name: C + value: generated + - name: Onetime1 + value: generated Sign: - - A - - B - - C - - Onetime1 + - name: A + value: generated + - name: B + value: generated + - name: C + value: generated + - name: Onetime1 + value: generated Read: - - 1 -graph: - - id: A.1.1 - baseSnapshot: A.1.1 - - id: A.1.2 - baseSnapshot: A.1.1 - aclHeads: [A.1.1] - treeHeads: [A.1.1] - - id: B.1.1 - baseSnapshot: A.1.1 - aclHeads: [A.1.2] - treeHeads: [A.1.2] - - id: B.1.2 - baseSnapshot: A.1.1 - aclHeads: [B.1.1] - treeHeads: [B.1.1] - - id: A.1.3 # this should be invalid, because it is based on one of the invalid changes - baseSnapshot: A.1.1 - aclHeads: [B.1.1] - treeHeads: [B.1.2, C.1.1] - - id: C.1.1 # this should be invalid, because C is a reader - baseSnapshot: A.1.1 - aclHeads: [B.1.1] - treeHeads: [B.1.1] -header: - firstChangeId: A.1.1 - isWorkspace: false -orphans: - - "A.1.3" + - name: 1 + value: generated diff --git a/pkg/acl/tree/doctree.go b/pkg/acl/tree/doctree.go index 8717cc09..45a5bca1 100644 --- a/pkg/acl/tree/doctree.go +++ b/pkg/acl/tree/doctree.go @@ -184,7 +184,7 @@ func (d *docTree) AddContent(ctx context.Context, aclList list.ACLList, content state := aclList.ACLState() change := &aclpb.Change{ TreeHeadIds: d.tree.Heads(), - AclHeadId: aclList.Last().Id, + AclHeadId: aclList.Head().Id, SnapshotBaseId: d.tree.RootId(), CurrentReadKeyHash: state.CurrentReadKeyHash(), Timestamp: int64(time.Now().Nanosecond()), diff --git a/pkg/acl/tree/treestorage.go b/pkg/acl/tree/treestorage.go index 87a3a47d..b5e450db 100644 --- a/pkg/acl/tree/treestorage.go +++ b/pkg/acl/tree/treestorage.go @@ -61,7 +61,7 @@ func CreateNewTreeStorage( state := aclList.ACLState() change := &aclpb.Change{ - AclHeadId: aclList.Last().Id, + AclHeadId: aclList.Head().Id, CurrentReadKeyHash: state.CurrentReadKeyHash(), Timestamp: int64(time.Now().Nanosecond()), Identity: acc.Identity,