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

View File

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

View File

@ -2,7 +2,7 @@ package storage
import ( import (
"context" "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/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/treechangeproto"
"github.com/dgraph-io/badger/v3" "github.com/dgraph-io/badger/v3"
) )
@ -40,14 +40,11 @@ func newTreeStorage(db *badger.DB, spaceId, treeId string) (ts storage.TreeStora
} }
return nil return nil
}) })
if err == badger.ErrKeyNotFound {
err = storage.ErrUnknownTreeId
}
return return
} }
func createTreeStorage(db *badger.DB, spaceId string, payload storage.TreeStorageCreatePayload) (ts storage.TreeStorage, err error) { 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()) { if hasDB(db, keys.RootIdKey()) {
err = storage.ErrTreeExists err = storage.ErrTreeExists
return return
@ -88,7 +85,7 @@ func createTreeStorage(db *badger.DB, spaceId string, payload storage.TreeStorag
return return
} }
func (t *treeStorage) ID() string { func (t *treeStorage) Id() string {
return t.id 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"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/app/logger" "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/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/storage"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/syncservice" "github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/syncservice"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/treegetter" "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/config"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/net/peer"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/net/pool" "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/nodeconf"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/aclrecordproto"
) )
const CName = "common.commonspace" const CName = "common.commonspace"
@ -29,12 +26,11 @@ type Service interface {
DeriveSpace(ctx context.Context, payload SpaceDerivePayload) (string, error) DeriveSpace(ctx context.Context, payload SpaceDerivePayload) (string, error)
CreateSpace(ctx context.Context, payload SpaceCreatePayload) (string, error) CreateSpace(ctx context.Context, payload SpaceCreatePayload) (string, error)
GetSpace(ctx context.Context, id string) (sp Space, err error) GetSpace(ctx context.Context, id string) (sp Space, err error)
AddSpace(ctx context.Context, spaceDescription SpaceDescription) (err error)
app.Component app.Component
} }
type service struct { type service struct {
config config2.Space config config.Space
account account.Service account account.Service
configurationService nodeconf.Service configurationService nodeconf.Service
storageProvider storage.SpaceStorageProvider storageProvider storage.SpaceStorageProvider
@ -43,7 +39,7 @@ type service struct {
} }
func (s *service) Init(a *app.App) (err error) { 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.account = a.MustComponent(account.CName).(account.Service)
s.storageProvider = a.MustComponent(storage.CName).(storage.SpaceStorageProvider) s.storageProvider = a.MustComponent(storage.CName).(storage.SpaceStorageProvider)
s.configurationService = a.MustComponent(nodeconf.CName).(nodeconf.Service) s.configurationService = a.MustComponent(nodeconf.CName).(nodeconf.Service)
@ -56,9 +52,7 @@ func (s *service) Name() (name string) {
return CName return CName
} }
func (s *service) CreateSpace( func (s *service) CreateSpace(ctx context.Context, payload SpaceCreatePayload) (id string, err error) {
ctx context.Context,
payload SpaceCreatePayload) (id string, err error) {
storageCreate, err := storagePayloadForSpaceCreate(payload) storageCreate, err := storagePayloadForSpaceCreate(payload)
if err != nil { if err != nil {
return return
@ -68,12 +62,10 @@ func (s *service) CreateSpace(
return return
} }
return store.ID(), nil return store.Id(), nil
} }
func (s *service) DeriveSpace( func (s *service) DeriveSpace(ctx context.Context, payload SpaceDerivePayload) (id string, err error) {
ctx context.Context,
payload SpaceDerivePayload) (id string, err error) {
storageCreate, err := storagePayloadForSpaceDerive(payload) storageCreate, err := storagePayloadForSpaceDerive(payload)
if err != nil { if err != nil {
return return
@ -83,53 +75,15 @@ func (s *service) DeriveSpace(
return return
} }
return store.ID(), nil 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
} }
func (s *service) GetSpace(ctx context.Context, id string) (Space, error) { func (s *service) GetSpace(ctx context.Context, id string) (Space, error) {
st, err := s.storageProvider.SpaceStorage(id) st, err := s.storageProvider.SpaceStorage(id)
if err != nil { if err != nil {
if err != spacesyncproto.ErrSpaceMissing {
return nil, err return nil, err
} }
st, err = s.getSpaceStorageFromRemote(ctx, id)
if err != nil {
err = storage.ErrSpaceStorageMissing
return nil, err
}
}
lastConfiguration := s.configurationService.GetLast() lastConfiguration := s.configurationService.GetLast()
confConnector := nodeconf.NewConfConnector(lastConfiguration, s.pool) confConnector := nodeconf.NewConfConnector(lastConfiguration, s.pool)
diffService := diffservice.NewDiffService(id, s.config.SyncPeriod, st, confConnector, s.treeGetter, log) diffService := diffservice.NewDiffService(id, s.config.SyncPeriod, st, confConnector, s.treeGetter, log)
@ -148,38 +102,3 @@ func (s *service) GetSpace(ctx context.Context, id string) (Space, error) {
} }
return sp, nil 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) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateTreeStorage", reflect.TypeOf((*MockSpaceStorage)(nil).CreateTreeStorage), arg0)
} }
// ID mocks base method. // Id mocks base method.
func (m *MockSpaceStorage) ID() string { func (m *MockSpaceStorage) Id() string {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "ID") ret := m.ctrl.Call(m, "Id")
ret0, _ := ret[0].(string) ret0, _ := ret[0].(string)
return ret0 return ret0
} }
// ID indicates an expected call of ID. // Id indicates an expected call of Id.
func (mr *MockSpaceStorageMockRecorder) ID() *gomock.Call { func (mr *MockSpaceStorageMockRecorder) Id() *gomock.Call {
mr.mock.ctrl.T.Helper() 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. // 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/app"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/spacesyncproto" "github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/spacesyncproto"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/aclrecordproto" "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" const CName = "commonspace.storage"
@ -16,7 +16,7 @@ var ErrSpaceStorageMissing = errors.New("space storage missing")
type SpaceStorage interface { type SpaceStorage interface {
storage.Provider storage.Provider
ID() string Id() string
ACLStorage() (storage.ListStorage, error) ACLStorage() (storage.ListStorage, error)
SpaceHeader() (*spacesyncproto.RawSpaceHeaderWithId, error) SpaceHeader() (*spacesyncproto.RawSpaceHeaderWithId, error)
StoredIds() ([]string, error) StoredIds() ([]string, error)

File diff suppressed because it is too large Load Diff

View File

@ -10,6 +10,8 @@ message RawACLRecord {
message RawACLRecordWithId { message RawACLRecordWithId {
bytes payload = 1; bytes payload = 1;
string id = 2; string id = 2;
bytes acceptorIdentity = 3;
bytes acceptorSignature = 4;
} }
message ACLRecord { message ACLRecord {
@ -63,24 +65,31 @@ message ACLUserAdd {
ACLUserPermissions permissions = 4; ACLUserPermissions permissions = 4;
} }
// accept key, encrypt key, invite id
// GetSpace(id) -> ... (space header + acl root) -> diff
// Join(ACLJoinRecord) -> Ok
message ACLUserInvite { message ACLUserInvite {
bytes acceptPublicKey = 1; bytes acceptPublicKey = 1;
uint64 encryptSymKeyHash = 2; // TODO: change to read key
bytes encryptPublicKey = 2;
repeated bytes encryptedReadKeys = 3; repeated bytes encryptedReadKeys = 3;
ACLUserPermissions permissions = 4; 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 { message ACLUserJoin {
bytes identity = 1; bytes identity = 1;
bytes encryptionKey = 2; bytes encryptionKey = 2;
bytes acceptSignature = 3; bytes acceptSignature = 3;
bytes acceptPubKey = 4; string inviteId = 4;
repeated bytes encryptedReadKeys = 5; repeated bytes encryptedReadKeys = 5;
} }
message ACLUserRemove { message ACLUserRemove {
bytes identity = 1; bytes identity = 1;
repeated ACLReadKeyReplace readKeyReplaces = 2; repeated ACLReadKeyReplace readKeyReplaces = 3;
} }
message ACLReadKeyReplace { message ACLReadKeyReplace {
@ -99,3 +108,19 @@ enum ACLUserPermissions {
Writer = 1; Writer = 1;
Reader = 2; 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 { type ACLList interface {
RWLocker RWLocker
ID() string ID() string
Root() *aclrecordproto.RawACLRecordWithId Root() *aclrecordproto.ACLRoot
Records() []*ACLRecord Records() []*ACLRecord
ACLState() *ACLState ACLState() *ACLState
IsAfter(first string, second string) (bool, error) IsAfter(first string, second string) (bool, error)
AddRawRecords(ctx context.Context, rec []*aclrecordproto.RawACLRecordWithId) (err error)
Head() *ACLRecord Head() *ACLRecord
Get(id string) (*ACLRecord, error) Get(id string) (*ACLRecord, error)
Iterate(iterFunc IterFunc) Iterate(iterFunc IterFunc)
@ -38,32 +37,38 @@ type ACLList interface {
} }
type aclList struct { type aclList struct {
root *aclrecordproto.RawACLRecordWithId root *aclrecordproto.ACLRoot
records []*ACLRecord records []*ACLRecord
indexes map[string]int indexes map[string]int
id string id string
stateBuilder *aclStateBuilder builder *aclStateBuilder
recordBuilder ACLRecordBuilder
aclState *ACLState aclState *ACLState
keychain *common.Keychain keychain *common.Keychain
storage storage.ListStorage
sync.RWMutex sync.RWMutex
} }
func BuildACLListWithIdentity(acc *account.AccountData, storage storage.ListStorage) (ACLList, error) { func BuildACLListWithIdentity(acc *account.AccountData, storage storage.ListStorage) (ACLList, error) {
id := storage.ID()
builder := newACLStateBuilderWithIdentity(acc) 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) { func BuildACLList(storage storage.ListStorage) (ACLList, error) {
id := storage.ID() return build(storage.Id(), newACLStateBuilder(), newACLRecordBuilder(storage.Id(), common.NewKeychain()), storage)
return build(id, newACLStateBuilder(), newACLRecordBuilder(id, common.NewKeychain()), storage)
} }
func build(id string, stateBuilder *aclStateBuilder, recBuilder ACLRecordBuilder, storage storage.ListStorage) (list ACLList, err error) { 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() head, err := storage.Head()
if err != nil { if err != nil {
return return
@ -80,7 +85,7 @@ func build(id string, stateBuilder *aclStateBuilder, recBuilder ACLRecordBuilder
} }
records := []*ACLRecord{record} records := []*ACLRecord{record}
for record.PrevId != "" { for record.PrevId != "" && record.PrevId != id {
rawRecordWithId, err = storage.GetRawRecord(context.Background(), record.PrevId) rawRecordWithId, err = storage.GetRawRecord(context.Background(), record.PrevId)
if err != nil { if err != nil {
return return
@ -92,6 +97,8 @@ func build(id string, stateBuilder *aclStateBuilder, recBuilder ACLRecordBuilder
} }
records = append(records, record) records = append(records, record)
} }
// adding root in the end, because we already parsed it
records = append(records, aclRecRoot)
indexes := make(map[string]int) indexes := make(map[string]int)
for i, j := 0, len(records)-1; i < j; i, j = i+1, j-1 { for i, j := 0, len(records)-1; i < j; i, j = i+1, j-1 {
@ -110,19 +117,12 @@ func build(id string, stateBuilder *aclStateBuilder, recBuilder ACLRecordBuilder
return return
} }
rootWithId, err := storage.Root()
if err != nil {
return
}
list = &aclList{ list = &aclList{
root: rootWithId, root: aclRecRoot.Model.(*aclrecordproto.ACLRoot),
records: records, records: records,
indexes: indexes, indexes: indexes,
stateBuilder: stateBuilder, builder: stateBuilder,
recordBuilder: recBuilder,
aclState: state, aclState: state,
storage: storage,
id: id, id: id,
RWMutex: sync.RWMutex{}, RWMutex: sync.RWMutex{},
} }
@ -137,44 +137,10 @@ func (a *aclList) ID() string {
return a.id return a.id
} }
func (a *aclList) Root() *aclrecordproto.RawACLRecordWithId { func (a *aclList) Root() *aclrecordproto.ACLRoot {
return a.root 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 { func (a *aclList) ACLState() *ACLState {
return a.aclState return a.aclState
} }

View File

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

View File

@ -12,7 +12,7 @@ var ErrACLExists = errors.New("acl already exists")
var ErrUnknownRecord = errors.New("record doesn't exist") var ErrUnknownRecord = errors.New("record doesn't exist")
type ListStorage interface { type ListStorage interface {
ID() string Id() string
Root() (*aclrecordproto.RawACLRecordWithId, error) Root() (*aclrecordproto.RawACLRecordWithId, error)
Head() (string, error) Head() (string, error)
SetHead(headId 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)) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Head", reflect.TypeOf((*MockListStorage)(nil).Head))
} }
// ID mocks base method. // Id mocks base method.
func (m *MockListStorage) ID() string { func (m *MockListStorage) Id() string {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "ID") ret := m.ctrl.Call(m, "Id")
ret0, _ := ret[0].(string) ret0, _ := ret[0].(string)
return ret0 return ret0
} }
// ID indicates an expected call of ID. // Id indicates an expected call of Id.
func (mr *MockListStorageMockRecorder) ID() *gomock.Call { func (mr *MockListStorageMockRecorder) Id() *gomock.Call {
mr.mock.ctrl.T.Helper() 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. // 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)) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Heads", reflect.TypeOf((*MockTreeStorage)(nil).Heads))
} }
// ID mocks base method. // Id mocks base method.
func (m *MockTreeStorage) ID() string { func (m *MockTreeStorage) Id() string {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "ID") ret := m.ctrl.Call(m, "Id")
ret0, _ := ret[0].(string) ret0, _ := ret[0].(string)
return ret0 return ret0
} }
// ID indicates an expected call of ID. // Id indicates an expected call of Id.
func (mr *MockTreeStorageMockRecorder) ID() *gomock.Call { func (mr *MockTreeStorageMockRecorder) Id() *gomock.Call {
mr.mock.ctrl.T.Helper() 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. // Root mocks base method.

View File

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

View File

@ -3,13 +3,13 @@ package acllistbuilder
import ( import (
"context" "context"
"fmt" "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/storage"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/testutils/yamltests" "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/cid"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/util/keys/asymmetric/encryptionkey" "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/asymmetric/signingkey"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/util/keys/symmetric" "hash/fnv"
"io/ioutil" "io/ioutil"
"path" "path"
"time" "time"
@ -19,12 +19,20 @@ import (
) )
type ACLListStorageBuilder struct { type ACLListStorageBuilder struct {
storage.ListStorage aclList string
records []*aclrecordproto2.ACLRecord
rawRecords []*aclrecordproto2.RawACLRecordWithId
indexes map[string]int
keychain *YAMLKeychain keychain *YAMLKeychain
rawRoot *aclrecordproto2.RawACLRecordWithId
root *aclrecordproto2.ACLRoot
id string
} }
func NewACLListStorageBuilder(keychain *YAMLKeychain) *ACLListStorageBuilder { func NewACLListStorageBuilder(keychain *YAMLKeychain) *ACLListStorageBuilder {
return &ACLListStorageBuilder{ return &ACLListStorageBuilder{
records: make([]*aclrecordproto2.ACLRecord, 0),
indexes: make(map[string]int),
keychain: keychain, keychain: keychain,
} }
} }
@ -52,7 +60,7 @@ func NewACLListStorageBuilderFromFile(file string) (*ACLListStorageBuilder, erro
return tb, nil 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() protoMarshalled, err := rec.Marshal()
if err != nil { if err != nil {
panic("should be able to marshal final acl message!") 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!") panic("should be able to sign final acl message!")
} }
rawRec := &aclrecordproto.RawACLRecord{ rawRec := &aclrecordproto2.RawACLRecord{
Payload: protoMarshalled, Payload: protoMarshalled,
Signature: signature, Signature: signature,
} }
@ -75,62 +83,94 @@ func (t *ACLListStorageBuilder) createRaw(rec proto.Marshaler, identity []byte)
id, _ := cid.NewCIDFromBytes(rawMarshalled) id, _ := cid.NewCIDFromBytes(rawMarshalled)
return &aclrecordproto.RawACLRecordWithId{ return &aclrecordproto2.RawACLRecordWithId{
Payload: rawMarshalled, Payload: rawMarshalled,
Id: id, 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 { func (t *ACLListStorageBuilder) GetKeychain() *YAMLKeychain {
return t.keychain 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 // Just to clarify - we are generating new identities for the ones that
// are specified in the yml file, because our identities should be Ed25519 // are specified in the yml file, because our identities should be Ed25519
// the same thing is happening for the encryption keys // the same thing is happening for the encryption keys
t.keychain.ParseKeys(&l.Keys) t.keychain.ParseKeys(&tree.Keys)
rawRoot := t.parseRoot(l.Root) t.parseRoot(tree.Root)
var err error prevId := t.id
t.ListStorage, err = storage.NewInMemoryACLListStorage(rawRoot.Id, []*aclrecordproto.RawACLRecordWithId{rawRoot}) for idx, rec := range tree.Records {
if err != nil {
panic(err)
}
prevId := rawRoot.Id
for _, rec := range l.Records {
newRecord := t.parseRecord(rec, prevId) newRecord := t.parseRecord(rec, prevId)
rawRecord := t.createRaw(newRecord, newRecord.Identity) rawRecord := t.createRaw(newRecord, newRecord.Identity)
err = t.AddRawRecord(context.Background(), rawRecord) t.records = append(t.records, newRecord)
if err != nil { t.rawRecords = append(t.rawRecords, rawRecord)
panic(err) t.indexes[rawRecord.Id] = idx
}
prevId = rawRecord.Id 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) k := t.keychain.GetKey(rec.ReadKey).(*SymKey)
var aclChangeContents []*aclrecordproto.ACLContentValue var aclChangeContents []*aclrecordproto2.ACLContentValue
for _, ch := range rec.AclChanges { for _, ch := range rec.AclChanges {
aclChangeContent := t.parseACLChange(ch) aclChangeContent := t.parseACLChange(ch)
aclChangeContents = append(aclChangeContents, aclChangeContent) aclChangeContents = append(aclChangeContents, aclChangeContent)
} }
data := &aclrecordproto.ACLData{ data := &aclrecordproto2.ACLData{
AclContent: aclChangeContents, AclContent: aclChangeContents,
} }
bytes, _ := data.Marshal() bytes, _ := data.Marshal()
return &aclrecordproto.ACLRecord{ return &aclrecordproto2.ACLRecord{
PrevId: prevId, PrevId: prevId,
Identity: []byte(t.keychain.GetIdentity(rec.Identity)), Identity: []byte(t.keychain.GetIdentity(rec.Identity)),
Data: bytes, Data: bytes,
CurrentReadKeyHash: k.Hash, 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 { switch {
case ch.UserAdd != nil: case ch.UserAdd != nil:
add := ch.UserAdd add := ch.UserAdd
@ -138,12 +178,12 @@ func (t *ACLListStorageBuilder) parseACLChange(ch *ACLChange) (convCh *aclrecord
encKey := t.keychain.GetKey(add.EncryptionKey).(encryptionkey.PrivKey) encKey := t.keychain.GetKey(add.EncryptionKey).(encryptionkey.PrivKey)
rawKey, _ := encKey.GetPublic().Raw() rawKey, _ := encKey.GetPublic().Raw()
convCh = &aclrecordproto.ACLContentValue{ convCh = &aclrecordproto2.ACLContentValue{
Value: &aclrecordproto.ACLContentValue_UserAdd{ Value: &aclrecordproto2.ACLContentValue_UserAdd{
UserAdd: &aclrecordproto.ACLUserAdd{ UserAdd: &aclrecordproto2.ACLUserAdd{
Identity: []byte(t.keychain.GetIdentity(add.Identity)), Identity: []byte(t.keychain.GetIdentity(add.Identity)),
EncryptionKey: rawKey, EncryptionKey: rawKey,
EncryptedReadKeys: t.encryptReadKeysWithPubKey(add.EncryptedReadKeys, encKey), EncryptedReadKeys: t.encryptReadKeys(add.EncryptedReadKeys, encKey),
Permissions: t.convertPermission(add.Permission), Permissions: t.convertPermission(add.Permission),
}, },
}, },
@ -151,50 +191,52 @@ func (t *ACLListStorageBuilder) parseACLChange(ch *ACLChange) (convCh *aclrecord
case ch.UserJoin != nil: case ch.UserJoin != nil:
join := ch.UserJoin join := ch.UserJoin
encKey := t.keychain.GetKey(join.EncryptionKey).(encryptionkey.PrivKey) encKey := t.keychain.
GetKey(join.EncryptionKey).(encryptionkey.PrivKey)
rawKey, _ := encKey.GetPublic().Raw() rawKey, _ := encKey.GetPublic().Raw()
idKey, _ := t.keychain.SigningKeysByYAMLName[join.Identity].GetPublic().Raw() idKey, _ := t.keychain.SigningKeysByYAMLIdentity[join.Identity].GetPublic().Raw()
signKey := t.keychain.GetKey(join.AcceptKey).(signingkey.PrivKey) signKey := t.keychain.GetKey(join.AcceptSignature).(signingkey.PrivKey)
signature, err := signKey.Sign(idKey) signature, err := signKey.Sign(idKey)
if err != nil { if err != nil {
panic(err) panic(err)
} }
acceptPubKey, _ := signKey.GetPublic().Raw()
convCh = &aclrecordproto.ACLContentValue{ convCh = &aclrecordproto2.ACLContentValue{
Value: &aclrecordproto.ACLContentValue_UserJoin{ Value: &aclrecordproto2.ACLContentValue_UserJoin{
UserJoin: &aclrecordproto.ACLUserJoin{ UserJoin: &aclrecordproto2.ACLUserJoin{
Identity: []byte(t.keychain.GetIdentity(join.Identity)), Identity: []byte(t.keychain.GetIdentity(join.Identity)),
EncryptionKey: rawKey, EncryptionKey: rawKey,
AcceptSignature: signature, AcceptSignature: signature,
AcceptPubKey: acceptPubKey, InviteId: join.InviteId,
EncryptedReadKeys: t.encryptReadKeysWithPubKey(join.EncryptedReadKeys, encKey), EncryptedReadKeys: t.encryptReadKeys(join.EncryptedReadKeys, encKey),
}, },
}, },
} }
case ch.UserInvite != nil: case ch.UserInvite != nil:
invite := ch.UserInvite invite := ch.UserInvite
rawAcceptKey, _ := t.keychain.GetKey(invite.AcceptKey).(signingkey.PrivKey).GetPublic().Raw() rawAcceptKey, _ := t.keychain.GetKey(invite.AcceptKey).(signingkey.PrivKey).GetPublic().Raw()
hash := t.keychain.GetKey(invite.EncryptionKey).(*SymKey).Hash encKey := t.keychain.
encKey := t.keychain.ReadKeysByHash[hash] GetKey(invite.EncryptionKey).(encryptionkey.PrivKey)
rawEncKey, _ := encKey.GetPublic().Raw()
convCh = &aclrecordproto.ACLContentValue{ convCh = &aclrecordproto2.ACLContentValue{
Value: &aclrecordproto.ACLContentValue_UserInvite{ Value: &aclrecordproto2.ACLContentValue_UserInvite{
UserInvite: &aclrecordproto.ACLUserInvite{ UserInvite: &aclrecordproto2.ACLUserInvite{
AcceptPublicKey: rawAcceptKey, AcceptPublicKey: rawAcceptKey,
EncryptSymKeyHash: hash, EncryptPublicKey: rawEncKey,
EncryptedReadKeys: t.encryptReadKeysWithSymKey(invite.EncryptedReadKeys, encKey.Key), EncryptedReadKeys: t.encryptReadKeys(invite.EncryptedReadKeys, encKey),
Permissions: t.convertPermission(invite.Permissions), Permissions: t.convertPermission(invite.Permissions),
InviteId: invite.InviteId,
}, },
}, },
} }
case ch.UserPermissionChange != nil: case ch.UserPermissionChange != nil:
permissionChange := ch.UserPermissionChange permissionChange := ch.UserPermissionChange
convCh = &aclrecordproto.ACLContentValue{ convCh = &aclrecordproto2.ACLContentValue{
Value: &aclrecordproto.ACLContentValue_UserPermissionChange{ Value: &aclrecordproto2.ACLContentValue_UserPermissionChange{
UserPermissionChange: &aclrecordproto.ACLUserPermissionChange{ UserPermissionChange: &aclrecordproto2.ACLUserPermissionChange{
Identity: []byte(t.keychain.GetIdentity(permissionChange.Identity)), Identity: []byte(t.keychain.GetIdentity(permissionChange.Identity)),
Permissions: t.convertPermission(permissionChange.Permission), Permissions: t.convertPermission(permissionChange.Permission),
}, },
@ -205,24 +247,24 @@ func (t *ACLListStorageBuilder) parseACLChange(ch *ACLChange) (convCh *aclrecord
newReadKey := t.keychain.GetKey(remove.NewReadKey).(*SymKey) newReadKey := t.keychain.GetKey(remove.NewReadKey).(*SymKey)
var replaces []*aclrecordproto.ACLReadKeyReplace var replaces []*aclrecordproto2.ACLReadKeyReplace
for _, id := range remove.IdentitiesLeft { for _, id := range remove.IdentitiesLeft {
encKey := t.keychain.EncryptionKeysByYAMLName[id] encKey := t.keychain.EncryptionKeysByYAMLIdentity[id]
rawEncKey, _ := encKey.GetPublic().Raw() rawEncKey, _ := encKey.GetPublic().Raw()
encReadKey, err := encKey.GetPublic().Encrypt(newReadKey.Key.Bytes()) encReadKey, err := encKey.GetPublic().Encrypt(newReadKey.Key.Bytes())
if err != nil { if err != nil {
panic(err) panic(err)
} }
replaces = append(replaces, &aclrecordproto.ACLReadKeyReplace{ replaces = append(replaces, &aclrecordproto2.ACLReadKeyReplace{
Identity: []byte(t.keychain.GetIdentity(id)), Identity: []byte(t.keychain.GetIdentity(id)),
EncryptionKey: rawEncKey, EncryptionKey: rawEncKey,
EncryptedReadKey: encReadKey, EncryptedReadKey: encReadKey,
}) })
} }
convCh = &aclrecordproto.ACLContentValue{ convCh = &aclrecordproto2.ACLContentValue{
Value: &aclrecordproto.ACLContentValue_UserRemove{ Value: &aclrecordproto2.ACLContentValue_UserRemove{
UserRemove: &aclrecordproto.ACLUserRemove{ UserRemove: &aclrecordproto2.ACLUserRemove{
Identity: []byte(t.keychain.GetIdentity(remove.RemovedIdentity)), Identity: []byte(t.keychain.GetIdentity(remove.RemovedIdentity)),
ReadKeyReplaces: replaces, ReadKeyReplaces: replaces,
}, },
@ -236,7 +278,7 @@ func (t *ACLListStorageBuilder) parseACLChange(ch *ACLChange) (convCh *aclrecord
return convCh 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 { for _, k := range keys {
realKey := t.keychain.GetKey(k).(*SymKey).Key.Bytes() realKey := t.keychain.GetKey(k).(*SymKey).Key.Bytes()
res, err := encKey.GetPublic().Encrypt(realKey) res, err := encKey.GetPublic().Encrypt(realKey)
@ -249,47 +291,43 @@ func (t *ACLListStorageBuilder) encryptReadKeysWithPubKey(keys []string, encKey
return return
} }
func (t *ACLListStorageBuilder) encryptReadKeysWithSymKey(keys []string, key *symmetric.Key) (enc [][]byte) { func (t *ACLListStorageBuilder) convertPermission(perm string) aclrecordproto2.ACLUserPermissions {
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 {
switch perm { switch perm {
case "admin": case "admin":
return aclrecordproto.ACLUserPermissions_Admin return aclrecordproto2.ACLUserPermissions_Admin
case "writer": case "writer":
return aclrecordproto.ACLUserPermissions_Writer return aclrecordproto2.ACLUserPermissions_Writer
case "reader": case "reader":
return aclrecordproto.ACLUserPermissions_Reader return aclrecordproto2.ACLUserPermissions_Reader
default: default:
panic(fmt.Sprintf("incorrect permission: %s", perm)) panic(fmt.Sprintf("incorrect permission: %s", perm))
} }
} }
func (t *ACLListStorageBuilder) traverseFromHead(f func(rec *aclrecordproto.ACLRecord, id string) error) (err error) { func (t *ACLListStorageBuilder) traverseFromHead(f func(rec *aclrecordproto2.ACLRecord, id string) error) (err error) {
panic("this was removed, add if needed") 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) { func (t *ACLListStorageBuilder) parseRoot(root *Root) {
rawSignKey, _ := t.keychain.SigningKeysByYAMLName[root.Identity].GetPublic().Raw() rawSignKey, _ := t.keychain.SigningKeysByYAMLIdentity[root.Identity].GetPublic().Raw()
rawEncKey, _ := t.keychain.EncryptionKeysByYAMLName[root.Identity].GetPublic().Raw() rawEncKey, _ := t.keychain.EncryptionKeysByYAMLIdentity[root.Identity].GetPublic().Raw()
readKey := t.keychain.ReadKeysByYAMLName[root.Identity] readKey, _ := aclrecordproto2.ACLReadKeyDerive(rawSignKey, rawEncKey)
aclRoot := &aclrecordproto.ACLRoot{ hasher := fnv.New64()
hasher.Write(readKey.Bytes())
t.root = &aclrecordproto2.ACLRoot{
Identity: rawSignKey, Identity: rawSignKey,
EncryptionKey: rawEncKey, EncryptionKey: rawEncKey,
SpaceId: root.SpaceId, SpaceId: root.SpaceId,
EncryptedReadKey: nil, EncryptedReadKey: nil,
DerivationScheme: "scheme", 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 ( import (
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/common" "github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/common"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/list" "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/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/asymmetric/signingkey"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/util/keys/symmetric" "github.com/anytypeio/go-anytype-infrastructure-experiments/common/util/keys/symmetric"
@ -20,7 +20,7 @@ type ObjectTreeCreatePayload struct {
Identity []byte 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() rootChange, err := treeStorage.Root()
if err != nil { if err != nil {
return nil, err return nil, err
@ -32,14 +32,14 @@ func BuildObjectTree(treeStorage storage.TreeStorage, aclList list.ACLList) (Obj
func CreateDerivedObjectTree( func CreateDerivedObjectTree(
payload ObjectTreeCreatePayload, payload ObjectTreeCreatePayload,
aclList list.ACLList, aclList list.ACLList,
createStorage storage.TreeStorageCreatorFunc) (objTree ObjectTree, err error) { createStorage storage2.TreeStorageCreatorFunc) (objTree ObjectTree, err error) {
return createObjectTree(payload, 0, nil, aclList, createStorage) return createObjectTree(payload, 0, nil, aclList, createStorage)
} }
func CreateObjectTree( func CreateObjectTree(
payload ObjectTreeCreatePayload, payload ObjectTreeCreatePayload,
aclList list.ACLList, aclList list.ACLList,
createStorage storage.TreeStorageCreatorFunc) (objTree ObjectTree, err error) { createStorage storage2.TreeStorageCreatorFunc) (objTree ObjectTree, err error) {
bytes := make([]byte, 32) bytes := make([]byte, 32)
_, err = rand.Read(bytes) _, err = rand.Read(bytes)
if err != nil { if err != nil {
@ -53,7 +53,7 @@ func createObjectTree(
timestamp int64, timestamp int64,
seed []byte, seed []byte,
aclList list.ACLList, aclList list.ACLList,
createStorage storage.TreeStorageCreatorFunc) (objTree ObjectTree, err error) { createStorage storage2.TreeStorageCreatorFunc) (objTree ObjectTree, err error) {
aclList.RLock() aclList.RLock()
aclHeadId := aclList.Head().Id aclHeadId := aclList.Head().Id
aclList.RUnlock() aclList.RUnlock()
@ -77,7 +77,8 @@ func createObjectTree(
} }
// create storage // create storage
st, err := createStorage(storage.TreeStorageCreatePayload{ st, err := createStorage(storage2.TreeStorageCreatePayload{
TreeId: raw.Id,
RootRawChange: raw, RootRawChange: raw,
Changes: []*treechangeproto.RawTreeChangeWithId{raw}, Changes: []*treechangeproto.RawTreeChangeWithId{raw},
Heads: []string{raw.Id}, 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() objTree.root, err = objTree.treeStorage.Root()
if err != nil { if err != nil {
return nil, err 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 return
} }
func (l *listStorage) ID() string { func (l *listStorage) Id() string {
return l.id 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/app/logger"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/spacesyncproto" "github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/spacesyncproto"
spacestorage "github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/storage" 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" "go.uber.org/zap"
"path" "path"
"sync" "sync"
@ -22,7 +22,7 @@ type spaceStorage struct {
spaceId string spaceId string
objDb *pogreb.DB objDb *pogreb.DB
keys spaceKeys keys spaceKeys
aclStorage storage2.ListStorage aclStorage storage.ListStorage
header *spacesyncproto.RawSpaceHeaderWithId header *spacesyncproto.RawSpaceHeaderWithId
mx sync.Mutex mx sync.Mutex
} }
@ -88,8 +88,8 @@ func createSpaceStorage(rootPath string, payload spacestorage.SpaceStorageCreate
} }
defer func() { defer func() {
if err != nil {
log.With(zap.String("id", payload.SpaceHeaderWithId.Id), zap.Error(err)).Warn("failed to create storage") log.With(zap.String("id", payload.SpaceHeaderWithId.Id), zap.Error(err)).Warn("failed to create storage")
if err != nil {
db.Close() db.Close()
} }
}() }()
@ -129,15 +129,15 @@ func createSpaceStorage(rootPath string, payload spacestorage.SpaceStorageCreate
return return
} }
func (s *spaceStorage) ID() string { func (s *spaceStorage) Id() string {
return s.spaceId 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) 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 // we have mutex here, so we prevent overwriting the heads of a tree on concurrent creation
s.mx.Lock() s.mx.Lock()
defer s.mx.Unlock() defer s.mx.Unlock()
@ -145,7 +145,7 @@ func (s *spaceStorage) CreateTreeStorage(payload storage2.TreeStorageCreatePaylo
return createTreeStorage(s.objDb, payload) return createTreeStorage(s.objDb, payload)
} }
func (s *spaceStorage) ACLStorage() (storage2.ListStorage, error) { func (s *spaceStorage) ACLStorage() (storage.ListStorage, error) {
return s.aclStorage, nil return s.aclStorage, nil
} }
@ -156,13 +156,13 @@ func (s *spaceStorage) SpaceHeader() (header *spacesyncproto.RawSpaceHeaderWithI
func (s *spaceStorage) StoredIds() (ids []string, err error) { func (s *spaceStorage) StoredIds() (ids []string, err error) {
index := s.objDb.Items() index := s.objDb.Items()
key, _, err := index.Next() key, val, err := index.Next()
for err == nil { for err == nil {
strKey := string(key) strKey := string(key)
if isRootIdKey(strKey) { 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 { if err != pogreb.ErrIterationDone {

View File

@ -3,7 +3,7 @@ package storage
import ( import (
"context" "context"
"github.com/akrylysov/pogreb" "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" "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) { 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()) has, err := db.Has(keys.HeadsKey())
if err != nil { if err != nil {
return return
@ -86,7 +86,7 @@ func createTreeStorage(db *pogreb.DB, payload storage.TreeStorageCreatePayload)
return return
} }
func (t *treeStorage) ID() string { func (t *treeStorage) Id() string {
return t.id return t.id
} }