node acl service (wip), fixes

This commit is contained in:
Sergey Cherepanov 2022-10-26 17:00:43 +03:00 committed by Mikhail Iudin
parent 348acd6d84
commit 41a3f0502e
No known key found for this signature in database
GPG Key ID: FAAAA8BAABDFF1C0
22 changed files with 1439 additions and 491 deletions

View File

@ -37,7 +37,7 @@ func newListStorage(spaceId string, db *badger.DB, txn *badger.Txn) (ls storage.
ls = &listStorage{
db: db,
keys: keys,
keys: newACLKeys(spaceId),
id: stringId,
root: rootWithId,
}
@ -70,14 +70,14 @@ func createListStorage(spaceId string, db *badger.DB, txn *badger.Txn, root *acl
ls = &listStorage{
db: db,
keys: keys,
keys: newACLKeys(spaceId),
id: root.Id,
root: root,
}
return
}
func (l *listStorage) ID() string {
func (l *listStorage) Id() string {
return l.id
}

View File

@ -3,7 +3,7 @@ package storage
import (
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/spacesyncproto"
spacestorage "github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/storage"
storage2 "github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/storage"
storage "github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/storage"
"github.com/dgraph-io/badger/v3"
"sync"
)
@ -12,7 +12,7 @@ type spaceStorage struct {
spaceId string
objDb *badger.DB
keys spaceKeys
aclStorage storage2.ListStorage
aclStorage storage.ListStorage
header *spacesyncproto.RawSpaceHeaderWithId
mx sync.Mutex
}
@ -77,15 +77,15 @@ func createSpaceStorage(db *badger.DB, payload spacestorage.SpaceStorageCreatePa
return
}
func (s *spaceStorage) ID() string {
func (s *spaceStorage) Id() string {
return s.spaceId
}
func (s *spaceStorage) TreeStorage(id string) (storage2.TreeStorage, error) {
func (s *spaceStorage) TreeStorage(id string) (storage.TreeStorage, error) {
return newTreeStorage(s.objDb, s.spaceId, id)
}
func (s *spaceStorage) CreateTreeStorage(payload storage2.TreeStorageCreatePayload) (ts storage2.TreeStorage, err error) {
func (s *spaceStorage) CreateTreeStorage(payload storage.TreeStorageCreatePayload) (ts storage.TreeStorage, err error) {
// we have mutex here, so we prevent overwriting the heads of a tree on concurrent creation
s.mx.Lock()
defer s.mx.Unlock()
@ -93,7 +93,7 @@ func (s *spaceStorage) CreateTreeStorage(payload storage2.TreeStorageCreatePaylo
return createTreeStorage(s.objDb, s.spaceId, payload)
}
func (s *spaceStorage) ACLStorage() (storage2.ListStorage, error) {
func (s *spaceStorage) ACLStorage() (storage.ListStorage, error) {
return s.aclStorage, nil
}

View File

@ -2,7 +2,7 @@ package storage
import (
"context"
storage "github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/storage"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/storage"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/treechangeproto"
"github.com/dgraph-io/badger/v3"
)
@ -40,14 +40,11 @@ func newTreeStorage(db *badger.DB, spaceId, treeId string) (ts storage.TreeStora
}
return nil
})
if err == badger.ErrKeyNotFound {
err = storage.ErrUnknownTreeId
}
return
}
func createTreeStorage(db *badger.DB, spaceId string, payload storage.TreeStorageCreatePayload) (ts storage.TreeStorage, err error) {
keys := newTreeKeys(spaceId, payload.RootRawChange.Id)
keys := newTreeKeys(spaceId, payload.TreeId)
if hasDB(db, keys.RootIdKey()) {
err = storage.ErrTreeExists
return
@ -88,7 +85,7 @@ func createTreeStorage(db *badger.DB, spaceId string, payload storage.TreeStorag
return
}
func (t *treeStorage) ID() string {
func (t *treeStorage) Id() string {
return t.id
}

View File

@ -6,15 +6,12 @@ import (
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/app"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/app/logger"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/diffservice"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/spacesyncproto"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/storage"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/syncservice"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/treegetter"
config2 "github.com/anytypeio/go-anytype-infrastructure-experiments/common/config"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/net/peer"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/config"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/net/pool"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/nodeconf"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/aclrecordproto"
)
const CName = "common.commonspace"
@ -29,12 +26,11 @@ type Service interface {
DeriveSpace(ctx context.Context, payload SpaceDerivePayload) (string, error)
CreateSpace(ctx context.Context, payload SpaceCreatePayload) (string, error)
GetSpace(ctx context.Context, id string) (sp Space, err error)
AddSpace(ctx context.Context, spaceDescription SpaceDescription) (err error)
app.Component
}
type service struct {
config config2.Space
config config.Space
account account.Service
configurationService nodeconf.Service
storageProvider storage.SpaceStorageProvider
@ -43,7 +39,7 @@ type service struct {
}
func (s *service) Init(a *app.App) (err error) {
s.config = a.MustComponent(config2.CName).(*config2.Config).Space
s.config = a.MustComponent(config.CName).(*config.Config).Space
s.account = a.MustComponent(account.CName).(account.Service)
s.storageProvider = a.MustComponent(storage.CName).(storage.SpaceStorageProvider)
s.configurationService = a.MustComponent(nodeconf.CName).(nodeconf.Service)
@ -56,9 +52,7 @@ func (s *service) Name() (name string) {
return CName
}
func (s *service) CreateSpace(
ctx context.Context,
payload SpaceCreatePayload) (id string, err error) {
func (s *service) CreateSpace(ctx context.Context, payload SpaceCreatePayload) (id string, err error) {
storageCreate, err := storagePayloadForSpaceCreate(payload)
if err != nil {
return
@ -68,12 +62,10 @@ func (s *service) CreateSpace(
return
}
return store.ID(), nil
return store.Id(), nil
}
func (s *service) DeriveSpace(
ctx context.Context,
payload SpaceDerivePayload) (id string, err error) {
func (s *service) DeriveSpace(ctx context.Context, payload SpaceDerivePayload) (id string, err error) {
storageCreate, err := storagePayloadForSpaceDerive(payload)
if err != nil {
return
@ -83,51 +75,13 @@ func (s *service) DeriveSpace(
return
}
return store.ID(), nil
}
func (s *service) AddSpace(ctx context.Context, spaceDescription SpaceDescription) (err error) {
_, err = s.storageProvider.SpaceStorage(spaceDescription.SpaceHeader.Id)
if err == nil {
err = spacesyncproto.ErrSpaceExists
return
}
if err != storage.ErrSpaceStorageMissing {
err = spacesyncproto.ErrUnexpected
return
}
payload := storage.SpaceStorageCreatePayload{
RecWithId: &aclrecordproto.RawACLRecordWithId{
Payload: spaceDescription.AclPayload,
Id: spaceDescription.AclId,
},
SpaceHeaderWithId: spaceDescription.SpaceHeader,
}
st, err := s.storageProvider.CreateSpaceStorage(payload)
if err != nil {
err = spacesyncproto.ErrUnexpected
if err == storage.ErrSpaceStorageExists {
err = spacesyncproto.ErrSpaceExists
}
return
}
err = st.Close()
return
return store.Id(), nil
}
func (s *service) GetSpace(ctx context.Context, id string) (Space, error) {
st, err := s.storageProvider.SpaceStorage(id)
if err != nil {
if err != spacesyncproto.ErrSpaceMissing {
return nil, err
}
st, err = s.getSpaceStorageFromRemote(ctx, id)
if err != nil {
err = storage.ErrSpaceStorageMissing
return nil, err
}
return nil, err
}
lastConfiguration := s.configurationService.GetLast()
@ -148,38 +102,3 @@ func (s *service) GetSpace(ctx context.Context, id string) (Space, error) {
}
return sp, nil
}
func (s *service) getSpaceStorageFromRemote(ctx context.Context, id string) (st storage.SpaceStorage, err error) {
var p peer.Peer
peerId, err := syncservice.GetPeerIdFromStreamContext(ctx)
if err == nil {
p, err = s.pool.Dial(ctx, peerId)
if err != nil {
return
}
} else {
lastConfiguration := s.configurationService.GetLast()
// for nodes we always get remote space only if we have id in the context
if lastConfiguration.IsResponsible(id) {
err = spacesyncproto.ErrSpaceMissing
return
}
p, err = s.pool.DialOneOf(ctx, lastConfiguration.NodeIds(id))
if err != nil {
return
}
}
cl := spacesyncproto.NewDRPCSpaceClient(p)
res, err := cl.PullSpace(ctx, &spacesyncproto.PullSpaceRequest{Id: id})
if err != nil {
return
}
st, err = s.storageProvider.CreateSpaceStorage(storage.SpaceStorageCreatePayload{
RecWithId: &aclrecordproto.RawACLRecordWithId{
Payload: res.AclPayload,
Id: res.AclPayloadId,
},
SpaceHeaderWithId: res.SpaceHeader,
})
return
}

View File

@ -162,18 +162,18 @@ func (mr *MockSpaceStorageMockRecorder) CreateTreeStorage(arg0 interface{}) *gom
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateTreeStorage", reflect.TypeOf((*MockSpaceStorage)(nil).CreateTreeStorage), arg0)
}
// ID mocks base method.
func (m *MockSpaceStorage) ID() string {
// Id mocks base method.
func (m *MockSpaceStorage) Id() string {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "ID")
ret := m.ctrl.Call(m, "Id")
ret0, _ := ret[0].(string)
return ret0
}
// ID indicates an expected call of ID.
func (mr *MockSpaceStorageMockRecorder) ID() *gomock.Call {
// Id indicates an expected call of Id.
func (mr *MockSpaceStorageMockRecorder) Id() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ID", reflect.TypeOf((*MockSpaceStorage)(nil).ID))
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Id", reflect.TypeOf((*MockSpaceStorage)(nil).Id))
}
// SpaceHeader mocks base method.

View File

@ -6,7 +6,7 @@ import (
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/app"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/spacesyncproto"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/aclrecordproto"
storage "github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/storage"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/storage"
)
const CName = "commonspace.storage"
@ -16,7 +16,7 @@ var ErrSpaceStorageMissing = errors.New("space storage missing")
type SpaceStorage interface {
storage.Provider
ID() string
Id() string
ACLStorage() (storage.ListStorage, error)
SpaceHeader() (*spacesyncproto.RawSpaceHeaderWithId, error)
StoredIds() ([]string, error)

File diff suppressed because it is too large Load Diff

View File

@ -3,99 +3,124 @@ package aclrecord;
option go_package = "pkg/acl/aclrecordproto";
message RawACLRecord {
bytes payload = 1;
bytes signature = 2;
bytes payload = 1;
bytes signature = 2;
}
message RawACLRecordWithId {
bytes payload = 1;
string id = 2;
bytes payload = 1;
string id = 2;
bytes acceptorIdentity = 3;
bytes acceptorSignature = 4;
}
message ACLRecord {
string prevId = 1;
bytes identity = 2;
bytes data = 3;
uint64 currentReadKeyHash = 4;
int64 timestamp = 5;
string prevId = 1;
bytes identity = 2;
bytes data = 3;
uint64 currentReadKeyHash = 4;
int64 timestamp = 5;
}
message ACLRoot {
bytes identity = 1;
bytes encryptionKey = 2;
string spaceId = 3;
bytes encryptedReadKey = 4;
string derivationScheme = 5;
uint64 currentReadKeyHash = 6;
int64 timestamp = 7;
bytes identity = 1;
bytes encryptionKey = 2;
string spaceId = 3;
bytes encryptedReadKey = 4;
string derivationScheme = 5;
uint64 currentReadKeyHash = 6;
int64 timestamp = 7;
}
message ACLContentValue {
oneof value {
ACLUserAdd userAdd = 1;
ACLUserRemove userRemove = 2;
ACLUserPermissionChange userPermissionChange = 3;
ACLUserInvite userInvite = 4;
ACLUserJoin userJoin = 5;
}
oneof value {
ACLUserAdd userAdd = 1;
ACLUserRemove userRemove = 2;
ACLUserPermissionChange userPermissionChange = 3;
ACLUserInvite userInvite = 4;
ACLUserJoin userJoin = 5;
}
}
message ACLData {
repeated ACLContentValue aclContent = 1;
repeated ACLContentValue aclContent = 1;
}
message ACLState {
repeated uint64 readKeyHashes = 1;
repeated ACLUserState userStates = 2;
map<string, ACLUserInvite> invites = 3;
repeated uint64 readKeyHashes = 1;
repeated ACLUserState userStates = 2;
map<string, ACLUserInvite> invites = 3;
}
message ACLUserState {
bytes identity = 1;
bytes encryptionKey = 2;
ACLUserPermissions permissions = 3;
bytes identity = 1;
bytes encryptionKey = 2;
ACLUserPermissions permissions = 3;
}
message ACLUserAdd {
bytes identity = 1;
bytes encryptionKey = 2;
repeated bytes encryptedReadKeys = 3;
ACLUserPermissions permissions = 4;
bytes identity = 1;
bytes encryptionKey = 2;
repeated bytes encryptedReadKeys = 3;
ACLUserPermissions permissions = 4;
}
// accept key, encrypt key, invite id
// GetSpace(id) -> ... (space header + acl root) -> diff
// Join(ACLJoinRecord) -> Ok
message ACLUserInvite {
bytes acceptPublicKey = 1;
uint64 encryptSymKeyHash = 2;
repeated bytes encryptedReadKeys = 3;
ACLUserPermissions permissions = 4;
bytes acceptPublicKey = 1;
// TODO: change to read key
bytes encryptPublicKey = 2;
repeated bytes encryptedReadKeys = 3;
ACLUserPermissions permissions = 4;
// TODO: either derive inviteId from pub keys or think if it is possible to just use ACL record id
string inviteId = 5;
}
message ACLUserJoin {
bytes identity = 1;
bytes encryptionKey = 2;
bytes acceptSignature = 3;
bytes acceptPubKey = 4;
repeated bytes encryptedReadKeys = 5;
bytes identity = 1;
bytes encryptionKey = 2;
bytes acceptSignature = 3;
string inviteId = 4;
repeated bytes encryptedReadKeys = 5;
}
message ACLUserRemove {
bytes identity = 1;
repeated ACLReadKeyReplace readKeyReplaces = 2;
bytes identity = 1;
repeated ACLReadKeyReplace readKeyReplaces = 3;
}
message ACLReadKeyReplace {
bytes identity = 1;
bytes encryptionKey = 2;
bytes encryptedReadKey = 3;
bytes identity = 1;
bytes encryptionKey = 2;
bytes encryptedReadKey = 3;
}
message ACLUserPermissionChange {
bytes identity = 1;
ACLUserPermissions permissions = 2;
bytes identity = 1;
ACLUserPermissions permissions = 2;
}
enum ACLUserPermissions {
Admin = 0;
Writer = 1;
Reader = 2;
Admin = 0;
Writer = 1;
Reader = 2;
}
message ACLSyncMessage {
ACLSyncContentValue content = 2;
}
// ACLSyncContentValue provides different types for acl sync
message ACLSyncContentValue {
oneof value {
ACLAddRecords addRecords = 1;
}
}
message ACLAddRecords {
repeated RawACLRecordWithId records = 1;
}

View File

@ -25,11 +25,10 @@ type RWLocker interface {
type ACLList interface {
RWLocker
ID() string
Root() *aclrecordproto.RawACLRecordWithId
Root() *aclrecordproto.ACLRoot
Records() []*ACLRecord
ACLState() *ACLState
IsAfter(first string, second string) (bool, error)
AddRawRecords(ctx context.Context, rec []*aclrecordproto.RawACLRecordWithId) (err error)
Head() *ACLRecord
Get(id string) (*ACLRecord, error)
Iterate(iterFunc IterFunc)
@ -38,32 +37,38 @@ type ACLList interface {
}
type aclList struct {
root *aclrecordproto.RawACLRecordWithId
root *aclrecordproto.ACLRoot
records []*ACLRecord
indexes map[string]int
id string
stateBuilder *aclStateBuilder
recordBuilder ACLRecordBuilder
aclState *ACLState
keychain *common.Keychain
storage storage.ListStorage
builder *aclStateBuilder
aclState *ACLState
keychain *common.Keychain
sync.RWMutex
}
func BuildACLListWithIdentity(acc *account.AccountData, storage storage.ListStorage) (ACLList, error) {
id := storage.ID()
builder := newACLStateBuilderWithIdentity(acc)
return build(id, builder, newACLRecordBuilder(id, common.NewKeychain()), storage)
return build(storage.Id(), builder, newACLRecordBuilder(storage.Id(), common.NewKeychain()), storage)
}
func BuildACLList(storage storage.ListStorage) (ACLList, error) {
id := storage.ID()
return build(id, newACLStateBuilder(), newACLRecordBuilder(id, common.NewKeychain()), storage)
return build(storage.Id(), newACLStateBuilder(), newACLRecordBuilder(storage.Id(), common.NewKeychain()), storage)
}
func build(id string, stateBuilder *aclStateBuilder, recBuilder ACLRecordBuilder, storage storage.ListStorage) (list ACLList, err error) {
// TODO: need to add context here
rootWithId, err := storage.Root()
if err != nil {
return
}
aclRecRoot, err := recBuilder.ConvertFromRaw(rootWithId)
if err != nil {
return
}
head, err := storage.Head()
if err != nil {
return
@ -80,7 +85,7 @@ func build(id string, stateBuilder *aclStateBuilder, recBuilder ACLRecordBuilder
}
records := []*ACLRecord{record}
for record.PrevId != "" {
for record.PrevId != "" && record.PrevId != id {
rawRecordWithId, err = storage.GetRawRecord(context.Background(), record.PrevId)
if err != nil {
return
@ -92,6 +97,8 @@ func build(id string, stateBuilder *aclStateBuilder, recBuilder ACLRecordBuilder
}
records = append(records, record)
}
// adding root in the end, because we already parsed it
records = append(records, aclRecRoot)
indexes := make(map[string]int)
for i, j := 0, len(records)-1; i < j; i, j = i+1, j-1 {
@ -110,21 +117,14 @@ func build(id string, stateBuilder *aclStateBuilder, recBuilder ACLRecordBuilder
return
}
rootWithId, err := storage.Root()
if err != nil {
return
}
list = &aclList{
root: rootWithId,
records: records,
indexes: indexes,
stateBuilder: stateBuilder,
recordBuilder: recBuilder,
aclState: state,
storage: storage,
id: id,
RWMutex: sync.RWMutex{},
root: aclRecRoot.Model.(*aclrecordproto.ACLRoot),
records: records,
indexes: indexes,
builder: stateBuilder,
aclState: state,
id: id,
RWMutex: sync.RWMutex{},
}
return
}
@ -137,44 +137,10 @@ func (a *aclList) ID() string {
return a.id
}
func (a *aclList) Root() *aclrecordproto.RawACLRecordWithId {
func (a *aclList) Root() *aclrecordproto.ACLRoot {
return a.root
}
func (a *aclList) AddRawRecords(ctx context.Context, records []*aclrecordproto.RawACLRecordWithId) (err error) {
if len(records) == 0 {
return
}
// converting and verifying
var aclRecords []*ACLRecord
for _, rec := range records {
var record *ACLRecord
record, err = a.recordBuilder.ConvertFromRaw(rec)
if err != nil {
return
}
aclRecords = append(aclRecords, record)
}
// trying to append them to state
err = a.stateBuilder.Append(a.aclState, aclRecords)
if err != nil {
return
}
// saving to storage
for _, rec := range records {
err = a.storage.AddRawRecord(ctx, rec)
if err != nil {
return
}
}
// setting new head
err = a.storage.SetHead(records[len(records)-1].Id)
return
}
func (a *aclList) ACLState() *ACLState {
return a.aclState
}

View File

@ -9,10 +9,8 @@ import (
)
type inMemoryACLListStorage struct {
records []*aclrecordproto.RawACLRecordWithId
id string
root *aclrecordproto.RawACLRecordWithId
head string
records map[string]*aclrecordproto.RawACLRecordWithId
sync.RWMutex
}
@ -20,63 +18,48 @@ type inMemoryACLListStorage struct {
func NewInMemoryACLListStorage(
id string,
records []*aclrecordproto.RawACLRecordWithId) (ListStorage, error) {
allRecords := make(map[string]*aclrecordproto.RawACLRecordWithId)
for _, ch := range records {
allRecords[ch.Id] = ch
}
root := records[0]
head := records[len(records)-1]
return &inMemoryACLListStorage{
id: root.Id,
root: root,
head: head.Id,
records: allRecords,
id: id,
records: records,
RWMutex: sync.RWMutex{},
}, nil
}
func (t *inMemoryACLListStorage) ID() string {
t.RLock()
defer t.RUnlock()
return t.id
func (i *inMemoryACLListStorage) Root() (*aclrecordproto.RawACLRecordWithId, error) {
i.RLock()
defer i.RUnlock()
return i.records[0], nil
}
func (t *inMemoryACLListStorage) Root() (*aclrecordproto.RawACLRecordWithId, error) {
t.RLock()
defer t.RUnlock()
return t.root, nil
func (i *inMemoryACLListStorage) SetHead(headId string) error {
panic("implement me")
}
func (t *inMemoryACLListStorage) Head() (string, error) {
t.RLock()
defer t.RUnlock()
return t.head, nil
func (i *inMemoryACLListStorage) Head() (string, error) {
i.RLock()
defer i.RUnlock()
return i.records[len(i.records)-1].Id, nil
}
func (t *inMemoryACLListStorage) SetHead(head string) error {
t.Lock()
defer t.Unlock()
t.head = head
return nil
}
func (t *inMemoryACLListStorage) AddRawRecord(ctx context.Context, record *aclrecordproto.RawACLRecordWithId) error {
t.Lock()
defer t.Unlock()
// TODO: better to do deep copy
t.records[record.Id] = record
return nil
}
func (t *inMemoryACLListStorage) GetRawRecord(ctx context.Context, recordId string) (*aclrecordproto.RawACLRecordWithId, error) {
t.RLock()
defer t.RUnlock()
if res, exists := t.records[recordId]; exists {
return res, nil
func (i *inMemoryACLListStorage) GetRawRecord(ctx context.Context, id string) (*aclrecordproto.RawACLRecordWithId, error) {
i.RLock()
defer i.RUnlock()
for _, rec := range i.records {
if rec.Id == id {
return rec, nil
}
}
return nil, fmt.Errorf("could not get record with id: %s", recordId)
return nil, fmt.Errorf("no such record")
}
func (i *inMemoryACLListStorage) AddRawRecord(ctx context.Context, rec *aclrecordproto.RawACLRecordWithId) error {
panic("implement me")
}
func (i *inMemoryACLListStorage) Id() string {
i.RLock()
defer i.RUnlock()
return i.id
}
type inMemoryTreeStorage struct {
@ -89,6 +72,7 @@ type inMemoryTreeStorage struct {
}
func NewInMemoryTreeStorage(
treeId string,
root *treechangeproto.RawTreeChangeWithId,
heads []string,
changes []*treechangeproto.RawTreeChangeWithId) (TreeStorage, error) {
@ -96,10 +80,10 @@ func NewInMemoryTreeStorage(
for _, ch := range changes {
allChanges[ch.Id] = ch
}
allChanges[root.Id] = root
allChanges[treeId] = root
return &inMemoryTreeStorage{
id: root.Id,
id: treeId,
root: root,
heads: heads,
changes: allChanges,
@ -112,7 +96,7 @@ func (t *inMemoryTreeStorage) HasChange(ctx context.Context, id string) (bool, e
return exists, nil
}
func (t *inMemoryTreeStorage) ID() string {
func (t *inMemoryTreeStorage) Id() string {
t.RLock()
defer t.RUnlock()
return t.id
@ -175,12 +159,12 @@ func (i *inMemoryStorageProvider) TreeStorage(id string) (TreeStorage, error) {
func (i *inMemoryStorageProvider) CreateTreeStorage(payload TreeStorageCreatePayload) (TreeStorage, error) {
i.Lock()
defer i.Unlock()
res, err := NewInMemoryTreeStorage(payload.RootRawChange, payload.Heads, payload.Changes)
res, err := NewInMemoryTreeStorage(payload.TreeId, payload.RootRawChange, payload.Heads, payload.Changes)
if err != nil {
return nil, err
}
i.objects[payload.RootRawChange.Id] = res
i.objects[payload.TreeId] = res
return res, nil
}

View File

@ -12,7 +12,7 @@ var ErrACLExists = errors.New("acl already exists")
var ErrUnknownRecord = errors.New("record doesn't exist")
type ListStorage interface {
ID() string
Id() string
Root() (*aclrecordproto.RawACLRecordWithId, error)
Head() (string, error)
SetHead(headId string) error

View File

@ -80,18 +80,18 @@ func (mr *MockListStorageMockRecorder) Head() *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Head", reflect.TypeOf((*MockListStorage)(nil).Head))
}
// ID mocks base method.
func (m *MockListStorage) ID() string {
// Id mocks base method.
func (m *MockListStorage) Id() string {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "ID")
ret := m.ctrl.Call(m, "Id")
ret0, _ := ret[0].(string)
return ret0
}
// ID indicates an expected call of ID.
func (mr *MockListStorageMockRecorder) ID() *gomock.Call {
// Id indicates an expected call of Id.
func (mr *MockListStorageMockRecorder) Id() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ID", reflect.TypeOf((*MockListStorage)(nil).ID))
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Id", reflect.TypeOf((*MockListStorage)(nil).Id))
}
// Root mocks base method.
@ -205,18 +205,18 @@ func (mr *MockTreeStorageMockRecorder) Heads() *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Heads", reflect.TypeOf((*MockTreeStorage)(nil).Heads))
}
// ID mocks base method.
func (m *MockTreeStorage) ID() string {
// Id mocks base method.
func (m *MockTreeStorage) Id() string {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "ID")
ret := m.ctrl.Call(m, "Id")
ret0, _ := ret[0].(string)
return ret0
}
// ID indicates an expected call of ID.
func (mr *MockTreeStorageMockRecorder) ID() *gomock.Call {
// Id indicates an expected call of Id.
func (mr *MockTreeStorageMockRecorder) Id() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ID", reflect.TypeOf((*MockTreeStorage)(nil).ID))
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Id", reflect.TypeOf((*MockTreeStorage)(nil).Id))
}
// Root mocks base method.

View File

@ -6,7 +6,7 @@ import (
)
type TreeStorage interface {
ID() string
Id() string
Root() (*treechangeproto.RawTreeChangeWithId, error)
Heads() ([]string, error)
SetHeads(heads []string) error

View File

@ -3,13 +3,13 @@ package acllistbuilder
import (
"context"
"fmt"
aclrecordproto "github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/aclrecordproto"
aclrecordproto2 "github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/aclrecordproto"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/storage"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/testutils/yamltests"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/util/cid"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/util/keys/asymmetric/encryptionkey"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/util/keys/asymmetric/signingkey"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/util/keys/symmetric"
"hash/fnv"
"io/ioutil"
"path"
"time"
@ -19,12 +19,20 @@ import (
)
type ACLListStorageBuilder struct {
storage.ListStorage
keychain *YAMLKeychain
aclList string
records []*aclrecordproto2.ACLRecord
rawRecords []*aclrecordproto2.RawACLRecordWithId
indexes map[string]int
keychain *YAMLKeychain
rawRoot *aclrecordproto2.RawACLRecordWithId
root *aclrecordproto2.ACLRoot
id string
}
func NewACLListStorageBuilder(keychain *YAMLKeychain) *ACLListStorageBuilder {
return &ACLListStorageBuilder{
records: make([]*aclrecordproto2.ACLRecord, 0),
indexes: make(map[string]int),
keychain: keychain,
}
}
@ -52,7 +60,7 @@ func NewACLListStorageBuilderFromFile(file string) (*ACLListStorageBuilder, erro
return tb, nil
}
func (t *ACLListStorageBuilder) createRaw(rec proto.Marshaler, identity []byte) *aclrecordproto.RawACLRecordWithId {
func (t *ACLListStorageBuilder) createRaw(rec proto.Marshaler, identity []byte) *aclrecordproto2.RawACLRecordWithId {
protoMarshalled, err := rec.Marshal()
if err != nil {
panic("should be able to marshal final acl message!")
@ -63,7 +71,7 @@ func (t *ACLListStorageBuilder) createRaw(rec proto.Marshaler, identity []byte)
panic("should be able to sign final acl message!")
}
rawRec := &aclrecordproto.RawACLRecord{
rawRec := &aclrecordproto2.RawACLRecord{
Payload: protoMarshalled,
Signature: signature,
}
@ -75,62 +83,94 @@ func (t *ACLListStorageBuilder) createRaw(rec proto.Marshaler, identity []byte)
id, _ := cid.NewCIDFromBytes(rawMarshalled)
return &aclrecordproto.RawACLRecordWithId{
return &aclrecordproto2.RawACLRecordWithId{
Payload: rawMarshalled,
Id: id,
}
}
func (t *ACLListStorageBuilder) Head() (string, error) {
l := len(t.records)
if l > 0 {
return t.rawRecords[l-1].Id, nil
}
return t.rawRoot.Id, nil
}
func (t *ACLListStorageBuilder) SetHead(headId string) error {
panic("SetHead is not implemented")
}
func (t *ACLListStorageBuilder) Root() (*aclrecordproto2.RawACLRecordWithId, error) {
return t.rawRoot, nil
}
func (t *ACLListStorageBuilder) GetRawRecord(ctx context.Context, id string) (*aclrecordproto2.RawACLRecordWithId, error) {
recIdx, ok := t.indexes[id]
if !ok {
if id == t.rawRoot.Id {
return t.rawRoot, nil
}
return nil, fmt.Errorf("no such record")
}
return t.rawRecords[recIdx], nil
}
func (t *ACLListStorageBuilder) AddRawRecord(ctx context.Context, rec *aclrecordproto2.RawACLRecordWithId) error {
panic("implement me")
}
func (t *ACLListStorageBuilder) Id() string {
return t.id
}
func (t *ACLListStorageBuilder) GetRawRecords() []*aclrecordproto2.RawACLRecordWithId {
return t.rawRecords
}
func (t *ACLListStorageBuilder) GetKeychain() *YAMLKeychain {
return t.keychain
}
func (t *ACLListStorageBuilder) Parse(l *YMLList) {
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(&l.Keys)
rawRoot := t.parseRoot(l.Root)
var err error
t.ListStorage, err = storage.NewInMemoryACLListStorage(rawRoot.Id, []*aclrecordproto.RawACLRecordWithId{rawRoot})
if err != nil {
panic(err)
}
prevId := rawRoot.Id
for _, rec := range l.Records {
t.keychain.ParseKeys(&tree.Keys)
t.parseRoot(tree.Root)
prevId := t.id
for idx, rec := range tree.Records {
newRecord := t.parseRecord(rec, prevId)
rawRecord := t.createRaw(newRecord, newRecord.Identity)
err = t.AddRawRecord(context.Background(), rawRecord)
if err != nil {
panic(err)
}
t.records = append(t.records, newRecord)
t.rawRecords = append(t.rawRecords, rawRecord)
t.indexes[rawRecord.Id] = idx
prevId = rawRecord.Id
}
t.SetHead(prevId)
}
func (t *ACLListStorageBuilder) parseRecord(rec *Record, prevId string) *aclrecordproto.ACLRecord {
func (t *ACLListStorageBuilder) parseRecord(rec *Record, prevId string) *aclrecordproto2.ACLRecord {
k := t.keychain.GetKey(rec.ReadKey).(*SymKey)
var aclChangeContents []*aclrecordproto.ACLContentValue
var aclChangeContents []*aclrecordproto2.ACLContentValue
for _, ch := range rec.AclChanges {
aclChangeContent := t.parseACLChange(ch)
aclChangeContents = append(aclChangeContents, aclChangeContent)
}
data := &aclrecordproto.ACLData{
data := &aclrecordproto2.ACLData{
AclContent: aclChangeContents,
}
bytes, _ := data.Marshal()
return &aclrecordproto.ACLRecord{
return &aclrecordproto2.ACLRecord{
PrevId: prevId,
Identity: []byte(t.keychain.GetIdentity(rec.Identity)),
Data: bytes,
CurrentReadKeyHash: k.Hash,
Timestamp: time.Now().UnixNano(),
Timestamp: time.Now().Unix(),
}
}
func (t *ACLListStorageBuilder) parseACLChange(ch *ACLChange) (convCh *aclrecordproto.ACLContentValue) {
func (t *ACLListStorageBuilder) parseACLChange(ch *ACLChange) (convCh *aclrecordproto2.ACLContentValue) {
switch {
case ch.UserAdd != nil:
add := ch.UserAdd
@ -138,12 +178,12 @@ func (t *ACLListStorageBuilder) parseACLChange(ch *ACLChange) (convCh *aclrecord
encKey := t.keychain.GetKey(add.EncryptionKey).(encryptionkey.PrivKey)
rawKey, _ := encKey.GetPublic().Raw()
convCh = &aclrecordproto.ACLContentValue{
Value: &aclrecordproto.ACLContentValue_UserAdd{
UserAdd: &aclrecordproto.ACLUserAdd{
convCh = &aclrecordproto2.ACLContentValue{
Value: &aclrecordproto2.ACLContentValue_UserAdd{
UserAdd: &aclrecordproto2.ACLUserAdd{
Identity: []byte(t.keychain.GetIdentity(add.Identity)),
EncryptionKey: rawKey,
EncryptedReadKeys: t.encryptReadKeysWithPubKey(add.EncryptedReadKeys, encKey),
EncryptedReadKeys: t.encryptReadKeys(add.EncryptedReadKeys, encKey),
Permissions: t.convertPermission(add.Permission),
},
},
@ -151,50 +191,52 @@ func (t *ACLListStorageBuilder) parseACLChange(ch *ACLChange) (convCh *aclrecord
case ch.UserJoin != nil:
join := ch.UserJoin
encKey := t.keychain.GetKey(join.EncryptionKey).(encryptionkey.PrivKey)
encKey := t.keychain.
GetKey(join.EncryptionKey).(encryptionkey.PrivKey)
rawKey, _ := encKey.GetPublic().Raw()
idKey, _ := t.keychain.SigningKeysByYAMLName[join.Identity].GetPublic().Raw()
signKey := t.keychain.GetKey(join.AcceptKey).(signingkey.PrivKey)
idKey, _ := t.keychain.SigningKeysByYAMLIdentity[join.Identity].GetPublic().Raw()
signKey := t.keychain.GetKey(join.AcceptSignature).(signingkey.PrivKey)
signature, err := signKey.Sign(idKey)
if err != nil {
panic(err)
}
acceptPubKey, _ := signKey.GetPublic().Raw()
convCh = &aclrecordproto.ACLContentValue{
Value: &aclrecordproto.ACLContentValue_UserJoin{
UserJoin: &aclrecordproto.ACLUserJoin{
convCh = &aclrecordproto2.ACLContentValue{
Value: &aclrecordproto2.ACLContentValue_UserJoin{
UserJoin: &aclrecordproto2.ACLUserJoin{
Identity: []byte(t.keychain.GetIdentity(join.Identity)),
EncryptionKey: rawKey,
AcceptSignature: signature,
AcceptPubKey: acceptPubKey,
EncryptedReadKeys: t.encryptReadKeysWithPubKey(join.EncryptedReadKeys, encKey),
InviteId: 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()
hash := t.keychain.GetKey(invite.EncryptionKey).(*SymKey).Hash
encKey := t.keychain.ReadKeysByHash[hash]
encKey := t.keychain.
GetKey(invite.EncryptionKey).(encryptionkey.PrivKey)
rawEncKey, _ := encKey.GetPublic().Raw()
convCh = &aclrecordproto.ACLContentValue{
Value: &aclrecordproto.ACLContentValue_UserInvite{
UserInvite: &aclrecordproto.ACLUserInvite{
convCh = &aclrecordproto2.ACLContentValue{
Value: &aclrecordproto2.ACLContentValue_UserInvite{
UserInvite: &aclrecordproto2.ACLUserInvite{
AcceptPublicKey: rawAcceptKey,
EncryptSymKeyHash: hash,
EncryptedReadKeys: t.encryptReadKeysWithSymKey(invite.EncryptedReadKeys, encKey.Key),
EncryptPublicKey: rawEncKey,
EncryptedReadKeys: t.encryptReadKeys(invite.EncryptedReadKeys, encKey),
Permissions: t.convertPermission(invite.Permissions),
InviteId: invite.InviteId,
},
},
}
case ch.UserPermissionChange != nil:
permissionChange := ch.UserPermissionChange
convCh = &aclrecordproto.ACLContentValue{
Value: &aclrecordproto.ACLContentValue_UserPermissionChange{
UserPermissionChange: &aclrecordproto.ACLUserPermissionChange{
convCh = &aclrecordproto2.ACLContentValue{
Value: &aclrecordproto2.ACLContentValue_UserPermissionChange{
UserPermissionChange: &aclrecordproto2.ACLUserPermissionChange{
Identity: []byte(t.keychain.GetIdentity(permissionChange.Identity)),
Permissions: t.convertPermission(permissionChange.Permission),
},
@ -205,24 +247,24 @@ func (t *ACLListStorageBuilder) parseACLChange(ch *ACLChange) (convCh *aclrecord
newReadKey := t.keychain.GetKey(remove.NewReadKey).(*SymKey)
var replaces []*aclrecordproto.ACLReadKeyReplace
var replaces []*aclrecordproto2.ACLReadKeyReplace
for _, id := range remove.IdentitiesLeft {
encKey := t.keychain.EncryptionKeysByYAMLName[id]
encKey := t.keychain.EncryptionKeysByYAMLIdentity[id]
rawEncKey, _ := encKey.GetPublic().Raw()
encReadKey, err := encKey.GetPublic().Encrypt(newReadKey.Key.Bytes())
if err != nil {
panic(err)
}
replaces = append(replaces, &aclrecordproto.ACLReadKeyReplace{
replaces = append(replaces, &aclrecordproto2.ACLReadKeyReplace{
Identity: []byte(t.keychain.GetIdentity(id)),
EncryptionKey: rawEncKey,
EncryptedReadKey: encReadKey,
})
}
convCh = &aclrecordproto.ACLContentValue{
Value: &aclrecordproto.ACLContentValue_UserRemove{
UserRemove: &aclrecordproto.ACLUserRemove{
convCh = &aclrecordproto2.ACLContentValue{
Value: &aclrecordproto2.ACLContentValue_UserRemove{
UserRemove: &aclrecordproto2.ACLUserRemove{
Identity: []byte(t.keychain.GetIdentity(remove.RemovedIdentity)),
ReadKeyReplaces: replaces,
},
@ -236,7 +278,7 @@ func (t *ACLListStorageBuilder) parseACLChange(ch *ACLChange) (convCh *aclrecord
return convCh
}
func (t *ACLListStorageBuilder) encryptReadKeysWithPubKey(keys []string, encKey encryptionkey.PrivKey) (enc [][]byte) {
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)
@ -249,47 +291,43 @@ func (t *ACLListStorageBuilder) encryptReadKeysWithPubKey(keys []string, encKey
return
}
func (t *ACLListStorageBuilder) encryptReadKeysWithSymKey(keys []string, key *symmetric.Key) (enc [][]byte) {
for _, k := range keys {
realKey := t.keychain.GetKey(k).(*SymKey).Key.Bytes()
res, err := key.Encrypt(realKey)
if err != nil {
panic(err)
}
enc = append(enc, res)
}
return
}
func (t *ACLListStorageBuilder) convertPermission(perm string) aclrecordproto.ACLUserPermissions {
func (t *ACLListStorageBuilder) convertPermission(perm string) aclrecordproto2.ACLUserPermissions {
switch perm {
case "admin":
return aclrecordproto.ACLUserPermissions_Admin
return aclrecordproto2.ACLUserPermissions_Admin
case "writer":
return aclrecordproto.ACLUserPermissions_Writer
return aclrecordproto2.ACLUserPermissions_Writer
case "reader":
return aclrecordproto.ACLUserPermissions_Reader
return aclrecordproto2.ACLUserPermissions_Reader
default:
panic(fmt.Sprintf("incorrect permission: %s", perm))
}
}
func (t *ACLListStorageBuilder) traverseFromHead(f func(rec *aclrecordproto.ACLRecord, id string) error) (err error) {
panic("this was removed, add if needed")
func (t *ACLListStorageBuilder) traverseFromHead(f func(rec *aclrecordproto2.ACLRecord, 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) parseRoot(root *Root) (rawRoot *aclrecordproto.RawACLRecordWithId) {
rawSignKey, _ := t.keychain.SigningKeysByYAMLName[root.Identity].GetPublic().Raw()
rawEncKey, _ := t.keychain.EncryptionKeysByYAMLName[root.Identity].GetPublic().Raw()
readKey := t.keychain.ReadKeysByYAMLName[root.Identity]
aclRoot := &aclrecordproto.ACLRoot{
func (t *ACLListStorageBuilder) parseRoot(root *Root) {
rawSignKey, _ := t.keychain.SigningKeysByYAMLIdentity[root.Identity].GetPublic().Raw()
rawEncKey, _ := t.keychain.EncryptionKeysByYAMLIdentity[root.Identity].GetPublic().Raw()
readKey, _ := aclrecordproto2.ACLReadKeyDerive(rawSignKey, rawEncKey)
hasher := fnv.New64()
hasher.Write(readKey.Bytes())
t.root = &aclrecordproto2.ACLRoot{
Identity: rawSignKey,
EncryptionKey: rawEncKey,
SpaceId: root.SpaceId,
EncryptedReadKey: nil,
DerivationScheme: "scheme",
CurrentReadKeyHash: readKey.Hash,
CurrentReadKeyHash: hasher.Sum64(),
}
return t.createRaw(aclRoot, rawSignKey)
t.rawRoot = t.createRaw(t.root, rawSignKey)
t.id = t.rawRoot.Id
}

View File

@ -3,7 +3,7 @@ package tree
import (
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/common"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/list"
storage "github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/storage"
storage2 "github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/storage"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/treechangeproto"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/util/keys/asymmetric/signingkey"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/util/keys/symmetric"
@ -20,7 +20,7 @@ type ObjectTreeCreatePayload struct {
Identity []byte
}
func BuildObjectTree(treeStorage storage.TreeStorage, aclList list.ACLList) (ObjectTree, error) {
func BuildObjectTree(treeStorage storage2.TreeStorage, aclList list.ACLList) (ObjectTree, error) {
rootChange, err := treeStorage.Root()
if err != nil {
return nil, err
@ -32,14 +32,14 @@ func BuildObjectTree(treeStorage storage.TreeStorage, aclList list.ACLList) (Obj
func CreateDerivedObjectTree(
payload ObjectTreeCreatePayload,
aclList list.ACLList,
createStorage storage.TreeStorageCreatorFunc) (objTree ObjectTree, err error) {
createStorage storage2.TreeStorageCreatorFunc) (objTree ObjectTree, err error) {
return createObjectTree(payload, 0, nil, aclList, createStorage)
}
func CreateObjectTree(
payload ObjectTreeCreatePayload,
aclList list.ACLList,
createStorage storage.TreeStorageCreatorFunc) (objTree ObjectTree, err error) {
createStorage storage2.TreeStorageCreatorFunc) (objTree ObjectTree, err error) {
bytes := make([]byte, 32)
_, err = rand.Read(bytes)
if err != nil {
@ -53,7 +53,7 @@ func createObjectTree(
timestamp int64,
seed []byte,
aclList list.ACLList,
createStorage storage.TreeStorageCreatorFunc) (objTree ObjectTree, err error) {
createStorage storage2.TreeStorageCreatorFunc) (objTree ObjectTree, err error) {
aclList.RLock()
aclHeadId := aclList.Head().Id
aclList.RUnlock()
@ -77,7 +77,8 @@ func createObjectTree(
}
// create storage
st, err := createStorage(storage.TreeStorageCreatePayload{
st, err := createStorage(storage2.TreeStorageCreatePayload{
TreeId: raw.Id,
RootRawChange: raw,
Changes: []*treechangeproto.RawTreeChangeWithId{raw},
Heads: []string{raw.Id},
@ -126,7 +127,8 @@ func buildObjectTree(deps objectTreeDeps) (ObjectTree, error) {
}
}
objTree.id = objTree.treeStorage.ID()
objTree.id = objTree.treeStorage.Id()
objTree.root, err = objTree.treeStorage.Root()
if err != nil {
return nil, err

111
node/acl/service.go Normal file
View File

@ -0,0 +1,111 @@
package acl
import (
"context"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/account"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/app"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/app/logger"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/syncservice/synchandler"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/aclrecordproto"
"github.com/anytypeio/go-anytype-infrastructure-experiments/consensus/consensusclient"
"github.com/anytypeio/go-anytype-infrastructure-experiments/consensus/consensusproto"
"time"
)
const CName = "node.acl"
var log = logger.NewNamed(CName)
type Service interface {
app.Component
}
type service struct {
consService consensusclient.Service
account account.Service
}
func (s *service) Init(a *app.App) (err error) {
s.consService = a.MustComponent(consensusclient.CName).(consensusclient.Service)
s.account = a.MustComponent(account.CName).(account.Service)
return
}
func (s *service) Name() (name string) {
return CName
}
func (s *service) CreateLog(ctx context.Context, aclId string, rec *aclrecordproto.RawACLRecordWithId) (err error) {
logId, err := cidToByte(aclId)
if err != nil {
return
}
recId, err := cidToByte(rec.Id)
if err != nil {
return
}
acc := s.account.Account()
rec.AcceptorIdentity = acc.Identity
if rec.AcceptorSignature, err = acc.SignKey.Sign(rec.Payload); err != nil {
return
}
recPayload, err := rec.Marshal()
if err != nil {
return
}
return s.consService.AddLog(ctx, &consensusproto.Log{
Id: logId,
Records: []*consensusproto.Record{
{
Id: recId,
Payload: recPayload,
CreatedUnix: uint64(time.Now().Unix()),
},
},
})
}
func (s *service) AddRecord(ctx context.Context, aclId string, rec *aclrecordproto.RawACLRecordWithId) (err error) {
logId, err := cidToByte(aclId)
if err != nil {
return
}
recId, err := cidToByte(rec.Id)
if err != nil {
return
}
acc := s.account.Account()
rec.AcceptorIdentity = acc.Identity
if rec.AcceptorSignature, err = acc.SignKey.Sign(rec.Payload); err != nil {
return
}
recPayload, err := rec.Marshal()
if err != nil {
return
}
return s.consService.AddRecord(ctx, logId, &consensusproto.Record{
Id: recId,
PrevId: nil, //TODO:
Payload: recPayload,
CreatedUnix: uint64(time.Now().Unix()),
})
}
func (s *service) Watch(ctx context.Context, spaceId, aclId string, h synchandler.SyncHandler) (err error) {
w, err := newWatcher(spaceId, aclId, h)
if err != nil {
return
}
if err = s.consService.Watch(w.logId, w); err != nil {
return err
}
return w.Ready(ctx)
}
func (s *service) UnWatch(aclId string) (err error) {
logId, err := cidToByte(aclId)
if err != nil {
return
}
return s.consService.UnWatch(logId)
}

19
node/acl/util.go Normal file
View File

@ -0,0 +1,19 @@
package acl
import "github.com/ipfs/go-cid"
func cidToString(b []byte) (s string, err error) {
rcid, err := cid.Cast(b)
if err != nil {
return
}
return rcid.String(), nil
}
func cidToByte(s string) (b []byte, err error) {
rcid, err := cid.Decode(s)
if err != nil {
return
}
return rcid.Bytes(), nil
}

16
node/acl/util_test.go Normal file
View File

@ -0,0 +1,16 @@
package acl
import (
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/util/cid"
"github.com/stretchr/testify/assert"
"testing"
)
func TestCIDLen(t *testing.T) {
s, _ := cid.NewCIDFromBytes([]byte("some data"))
t.Log(s, len(s))
b, _ := cidToByte(s)
t.Log(b, len(b))
s2, _ := cidToString(b)
assert.Equal(t, s, s2)
}

93
node/acl/watcher.go Normal file
View File

@ -0,0 +1,93 @@
package acl
import (
"context"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/spacesyncproto"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/syncservice/synchandler"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/aclrecordproto"
"github.com/anytypeio/go-anytype-infrastructure-experiments/consensus/consensusproto"
"go.uber.org/zap"
"sync"
)
func newWatcher(spaceId, aclId string, h synchandler.SyncHandler) (w *watcher, err error) {
w = &watcher{
aclId: aclId,
spaceId: spaceId,
handler: h,
ready: make(chan struct{}),
}
if w.logId, err = cidToByte(aclId); err != nil {
return nil, err
}
return
}
type watcher struct {
spaceId string
aclId string
logId []byte
handler synchandler.SyncHandler
ready chan struct{}
isReady sync.Once
err error
}
func (w *watcher) AddConsensusRecords(recs []*consensusproto.Record) {
w.isReady.Do(func() {
close(w.ready)
})
records := make([]*aclrecordproto.RawACLRecordWithId, 0, len(recs))
for _, rec := range recs {
recId, err := cidToString(rec.Id)
if err != nil {
log.Error("received invalid id from consensus node", zap.Error(err))
continue
}
records = append(records, &aclrecordproto.RawACLRecordWithId{
Payload: rec.Payload,
Id: recId,
})
}
aclReq := &aclrecordproto.ACLSyncMessage{
Content: &aclrecordproto.ACLSyncContentValue{
Value: &aclrecordproto.ACLSyncContentValue_AddRecords{
AddRecords: &aclrecordproto.ACLAddRecords{
Records: records,
},
},
},
}
payload, err := aclReq.Marshal()
if err != nil {
log.Error("acl payload marshal error", zap.Error(err))
return
}
req := &spacesyncproto.ObjectSyncMessage{
SpaceId: w.spaceId,
Payload: payload,
ObjectId: w.aclId,
}
if err = w.handler.HandleMessage(context.TODO(), "", req); err != nil {
log.Warn("handle message error", zap.Error(err))
}
}
func (w *watcher) AddConsensusError(err error) {
w.isReady.Do(func() {
w.err = err
close(w.ready)
})
}
func (w *watcher) Ready(ctx context.Context) (err error) {
select {
case <-w.ready:
return w.err
case <-ctx.Done():
return ctx.Err()
}
}

View File

@ -82,7 +82,7 @@ func createListStorage(db *pogreb.DB, root *aclrecordproto.RawACLRecordWithId) (
return
}
func (l *listStorage) ID() string {
func (l *listStorage) Id() string {
return l.id
}

View File

@ -5,7 +5,7 @@ import (
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/app/logger"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/spacesyncproto"
spacestorage "github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/storage"
storage2 "github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/storage"
storage "github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/storage"
"go.uber.org/zap"
"path"
"sync"
@ -22,7 +22,7 @@ type spaceStorage struct {
spaceId string
objDb *pogreb.DB
keys spaceKeys
aclStorage storage2.ListStorage
aclStorage storage.ListStorage
header *spacesyncproto.RawSpaceHeaderWithId
mx sync.Mutex
}
@ -88,8 +88,8 @@ func createSpaceStorage(rootPath string, payload spacestorage.SpaceStorageCreate
}
defer func() {
log.With(zap.String("id", payload.SpaceHeaderWithId.Id), zap.Error(err)).Warn("failed to create storage")
if err != nil {
log.With(zap.String("id", payload.SpaceHeaderWithId.Id), zap.Error(err)).Warn("failed to create storage")
db.Close()
}
}()
@ -129,15 +129,15 @@ func createSpaceStorage(rootPath string, payload spacestorage.SpaceStorageCreate
return
}
func (s *spaceStorage) ID() string {
func (s *spaceStorage) Id() string {
return s.spaceId
}
func (s *spaceStorage) TreeStorage(id string) (storage2.TreeStorage, error) {
func (s *spaceStorage) TreeStorage(id string) (storage.TreeStorage, error) {
return newTreeStorage(s.objDb, id)
}
func (s *spaceStorage) CreateTreeStorage(payload storage2.TreeStorageCreatePayload) (ts storage2.TreeStorage, err error) {
func (s *spaceStorage) CreateTreeStorage(payload storage.TreeStorageCreatePayload) (ts storage.TreeStorage, err error) {
// we have mutex here, so we prevent overwriting the heads of a tree on concurrent creation
s.mx.Lock()
defer s.mx.Unlock()
@ -145,7 +145,7 @@ func (s *spaceStorage) CreateTreeStorage(payload storage2.TreeStorageCreatePaylo
return createTreeStorage(s.objDb, payload)
}
func (s *spaceStorage) ACLStorage() (storage2.ListStorage, error) {
func (s *spaceStorage) ACLStorage() (storage.ListStorage, error) {
return s.aclStorage, nil
}
@ -156,13 +156,13 @@ func (s *spaceStorage) SpaceHeader() (header *spacesyncproto.RawSpaceHeaderWithI
func (s *spaceStorage) StoredIds() (ids []string, err error) {
index := s.objDb.Items()
key, _, err := index.Next()
key, val, err := index.Next()
for err == nil {
strKey := string(key)
if isRootIdKey(strKey) {
ids = append(ids, getRootId(strKey))
ids = append(ids, string(val))
}
key, _, err = index.Next()
key, val, err = index.Next()
}
if err != pogreb.ErrIterationDone {

View File

@ -3,7 +3,7 @@ package storage
import (
"context"
"github.com/akrylysov/pogreb"
storage "github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/storage"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/storage"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/treechangeproto"
)
@ -48,7 +48,7 @@ func newTreeStorage(db *pogreb.DB, treeId string) (ts storage.TreeStorage, err e
}
func createTreeStorage(db *pogreb.DB, payload storage.TreeStorageCreatePayload) (ts storage.TreeStorage, err error) {
keys := newTreeKeys(payload.RootRawChange.Id)
keys := newTreeKeys(payload.TreeId)
has, err := db.Has(keys.HeadsKey())
if err != nil {
return
@ -86,7 +86,7 @@ func createTreeStorage(db *pogreb.DB, payload storage.TreeStorageCreatePayload)
return
}
func (t *treeStorage) ID() string {
func (t *treeStorage) Id() string {
return t.id
}