WIP ACLTree and moving files to packages
This commit is contained in:
parent
3df678bc80
commit
45024837ff
@ -17,7 +17,7 @@ var ErrUserRemoved = errors.New("user was removed from the document")
|
|||||||
var ErrDocumentForbidden = errors.New("your user was forbidden access to the document")
|
var ErrDocumentForbidden = errors.New("your user was forbidden access to the document")
|
||||||
var ErrUserAlreadyExists = errors.New("user already exists")
|
var ErrUserAlreadyExists = errors.New("user already exists")
|
||||||
|
|
||||||
type aclState struct {
|
type ACLState struct {
|
||||||
currentReadKeyHash uint64
|
currentReadKeyHash uint64
|
||||||
userReadKeys map[uint64]*symmetric.Key
|
userReadKeys map[uint64]*symmetric.Key
|
||||||
userStates map[string]*pb.ACLChangeUserState
|
userStates map[string]*pb.ACLChangeUserState
|
||||||
@ -27,12 +27,12 @@ type aclState struct {
|
|||||||
identity string
|
identity string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewACLStateFromSnapshot(
|
func newACLStateFromSnapshot(
|
||||||
snapshot *pb.ACLChangeACLSnapshot,
|
snapshot *pb.ACLChangeACLSnapshot,
|
||||||
identity string,
|
identity string,
|
||||||
encryptionKey keys.EncryptionPrivKey,
|
encryptionKey keys.EncryptionPrivKey,
|
||||||
signingPubKeyDecoder keys.SigningPubKeyDecoder) (*aclState, error) {
|
signingPubKeyDecoder keys.SigningPubKeyDecoder) (*ACLState, error) {
|
||||||
st := &aclState{
|
st := &ACLState{
|
||||||
identity: identity,
|
identity: identity,
|
||||||
encryptionKey: encryptionKey,
|
encryptionKey: encryptionKey,
|
||||||
userReadKeys: make(map[uint64]*symmetric.Key),
|
userReadKeys: make(map[uint64]*symmetric.Key),
|
||||||
@ -47,7 +47,7 @@ func NewACLStateFromSnapshot(
|
|||||||
return st, nil
|
return st, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *aclState) recreateFromSnapshot(snapshot *pb.ACLChangeACLSnapshot) error {
|
func (st *ACLState) recreateFromSnapshot(snapshot *pb.ACLChangeACLSnapshot) error {
|
||||||
state := snapshot.AclState
|
state := snapshot.AclState
|
||||||
for _, userState := range state.UserStates {
|
for _, userState := range state.UserStates {
|
||||||
st.userStates[userState.Identity] = userState
|
st.userStates[userState.Identity] = userState
|
||||||
@ -74,21 +74,21 @@ func (st *aclState) recreateFromSnapshot(snapshot *pb.ACLChangeACLSnapshot) erro
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *aclState) ApplyChange(changeId string, change *pb.ACLChange) error {
|
func (st *ACLState) applyChange(changeId string, change *pb.ACLChange) error {
|
||||||
// we can't check this for the user which is joining, because it will not be in our list
|
// we can't check this for the user which is joining, because it will not be in our list
|
||||||
if !st.isUserJoin(change) {
|
if !st.isUserJoin(change) {
|
||||||
// we check signature when we add this to the tree, so no need to do it here
|
// we check signature when we add this to the Tree, so no need to do it here
|
||||||
if _, exists := st.userStates[change.Identity]; !exists {
|
if _, exists := st.userStates[change.Identity]; !exists {
|
||||||
return ErrNoSuchUser
|
return ErrNoSuchUser
|
||||||
}
|
}
|
||||||
|
|
||||||
if !st.HasPermission(change.Identity, pb.ACLChange_Admin) {
|
if !st.hasPermission(change.Identity, pb.ACLChange_Admin) {
|
||||||
return fmt.Errorf("user %s must have admin permissions", change.Identity)
|
return fmt.Errorf("user %s must have admin permissions", change.Identity)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, ch := range change.GetAclData().GetAclContent() {
|
for _, ch := range change.GetAclData().GetAclContent() {
|
||||||
if err := st.applyChange(changeId, ch); err != nil {
|
if err := st.applyChangeContent(changeId, ch); err != nil {
|
||||||
//log.Infof("error while applying changes: %v; ignore", err)
|
//log.Infof("error while applying changes: %v; ignore", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -98,7 +98,7 @@ func (st *aclState) ApplyChange(changeId string, change *pb.ACLChange) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: remove changeId, because it is not needed
|
// TODO: remove changeId, because it is not needed
|
||||||
func (st *aclState) applyChange(changeId string, ch *pb.ACLChangeACLContentValue) error {
|
func (st *ACLState) applyChangeContent(changeId string, ch *pb.ACLChangeACLContentValue) error {
|
||||||
switch {
|
switch {
|
||||||
case ch.GetUserPermissionChange() != nil:
|
case ch.GetUserPermissionChange() != nil:
|
||||||
return st.applyUserPermissionChange(ch.GetUserPermissionChange())
|
return st.applyUserPermissionChange(ch.GetUserPermissionChange())
|
||||||
@ -117,7 +117,7 @@ func (st *aclState) applyChange(changeId string, ch *pb.ACLChangeACLContentValue
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *aclState) applyUserPermissionChange(ch *pb.ACLChangeUserPermissionChange) error {
|
func (st *ACLState) applyUserPermissionChange(ch *pb.ACLChangeUserPermissionChange) error {
|
||||||
if _, exists := st.userStates[ch.Identity]; !exists {
|
if _, exists := st.userStates[ch.Identity]; !exists {
|
||||||
return ErrNoSuchUser
|
return ErrNoSuchUser
|
||||||
}
|
}
|
||||||
@ -126,12 +126,12 @@ func (st *aclState) applyUserPermissionChange(ch *pb.ACLChangeUserPermissionChan
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *aclState) applyUserInvite(changeId string, ch *pb.ACLChangeUserInvite) error {
|
func (st *ACLState) applyUserInvite(changeId string, ch *pb.ACLChangeUserInvite) error {
|
||||||
st.userInvites[changeId] = ch
|
st.userInvites[changeId] = ch
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *aclState) applyUserJoin(ch *pb.ACLChangeUserJoin) error {
|
func (st *ACLState) applyUserJoin(ch *pb.ACLChangeUserJoin) error {
|
||||||
invite, exists := st.userInvites[ch.UserInviteChangeId]
|
invite, exists := st.userInvites[ch.UserInviteChangeId]
|
||||||
if !exists {
|
if !exists {
|
||||||
return fmt.Errorf("no such invite with id %s", ch.UserInviteChangeId)
|
return fmt.Errorf("no such invite with id %s", ch.UserInviteChangeId)
|
||||||
@ -188,7 +188,7 @@ func (st *aclState) applyUserJoin(ch *pb.ACLChangeUserJoin) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *aclState) applyUserAdd(ch *pb.ACLChangeUserAdd) error {
|
func (st *ACLState) applyUserAdd(ch *pb.ACLChangeUserAdd) error {
|
||||||
if _, exists := st.userStates[ch.Identity]; exists {
|
if _, exists := st.userStates[ch.Identity]; exists {
|
||||||
return ErrUserAlreadyExists
|
return ErrUserAlreadyExists
|
||||||
}
|
}
|
||||||
@ -203,7 +203,7 @@ func (st *aclState) applyUserAdd(ch *pb.ACLChangeUserAdd) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *aclState) applyUserRemove(ch *pb.ACLChangeUserRemove) error {
|
func (st *ACLState) applyUserRemove(ch *pb.ACLChangeUserRemove) error {
|
||||||
if ch.Identity == st.identity {
|
if ch.Identity == st.identity {
|
||||||
return ErrDocumentForbidden
|
return ErrDocumentForbidden
|
||||||
}
|
}
|
||||||
@ -235,7 +235,7 @@ func (st *aclState) applyUserRemove(ch *pb.ACLChangeUserRemove) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *aclState) applyUserConfirm(ch *pb.ACLChangeUserConfirm) error {
|
func (st *ACLState) applyUserConfirm(ch *pb.ACLChangeUserConfirm) error {
|
||||||
if _, exists := st.userStates[ch.Identity]; !exists {
|
if _, exists := st.userStates[ch.Identity]; !exists {
|
||||||
return ErrNoSuchUser
|
return ErrNoSuchUser
|
||||||
}
|
}
|
||||||
@ -245,7 +245,7 @@ func (st *aclState) applyUserConfirm(ch *pb.ACLChangeUserConfirm) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *aclState) decryptReadKeyAndHash(msg []byte) (*symmetric.Key, uint64, error) {
|
func (st *ACLState) decryptReadKeyAndHash(msg []byte) (*symmetric.Key, uint64, error) {
|
||||||
decrypted, err := st.encryptionKey.Decrypt(msg)
|
decrypted, err := st.encryptionKey.Decrypt(msg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, ErrFailedToDecrypt
|
return nil, 0, ErrFailedToDecrypt
|
||||||
@ -261,7 +261,7 @@ func (st *aclState) decryptReadKeyAndHash(msg []byte) (*symmetric.Key, uint64, e
|
|||||||
return key, hasher.Sum64(), nil
|
return key, hasher.Sum64(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *aclState) HasPermission(identity string, permission pb.ACLChangeUserPermissions) bool {
|
func (st *ACLState) hasPermission(identity string, permission pb.ACLChangeUserPermissions) bool {
|
||||||
state, exists := st.userStates[identity]
|
state, exists := st.userStates[identity]
|
||||||
if !exists {
|
if !exists {
|
||||||
return false
|
return false
|
||||||
@ -270,12 +270,12 @@ func (st *aclState) HasPermission(identity string, permission pb.ACLChangeUserPe
|
|||||||
return state.Permissions == permission
|
return state.Permissions == permission
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *aclState) isUserJoin(ch *pb.ACLChange) bool {
|
func (st *ACLState) isUserJoin(ch *pb.ACLChange) bool {
|
||||||
// if we have a UserJoin, then it should always be the first one applied
|
// if we have a UserJoin, then it should always be the first one applied
|
||||||
return ch.AclData.GetAclContent() != nil && ch.AclData.GetAclContent()[0].GetUserJoin() != nil
|
return ch.AclData.GetAclContent() != nil && ch.AclData.GetAclContent()[0].GetUserJoin() != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *aclState) GetPermissionDecreasedUsers(ch *pb.ACLChange) (identities []*pb.ACLChangeUserPermissionChange) {
|
func (st *ACLState) getPermissionDecreasedUsers(ch *pb.ACLChange) (identities []*pb.ACLChangeUserPermissionChange) {
|
||||||
// this should be called after general checks are completed
|
// this should be called after general checks are completed
|
||||||
if ch.GetAclData().GetAclContent() == nil {
|
if ch.GetAclData().GetAclContent() == nil {
|
||||||
return nil
|
return nil
|
||||||
@ -307,7 +307,7 @@ func (st *aclState) GetPermissionDecreasedUsers(ch *pb.ACLChange) (identities []
|
|||||||
return identities
|
return identities
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *aclState) Equal(other *aclState) bool {
|
func (st *ACLState) equal(other *ACLState) bool {
|
||||||
if st == nil && other == nil {
|
if st == nil && other == nil {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -350,3 +350,8 @@ func (st *aclState) Equal(other *aclState) bool {
|
|||||||
// TODO: add detailed user invites comparison + compare other stuff
|
// TODO: add detailed user invites comparison + compare other stuff
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (st *ACLState) GetUserStates() map[string]*pb.ACLChangeUserState {
|
||||||
|
// TODO: we should provide better API that would not allow to change this map from the outside
|
||||||
|
return st.userStates
|
||||||
|
}
|
||||||
|
|||||||
@ -8,9 +8,9 @@ import (
|
|||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ACLStateBuilder struct {
|
type aclStateBuilder struct {
|
||||||
tree *Tree
|
tree *Tree
|
||||||
aclState *aclState
|
aclState *ACLState
|
||||||
identity string
|
identity string
|
||||||
key keys.EncryptionPrivKey
|
key keys.EncryptionPrivKey
|
||||||
decoder keys.SigningPubKeyDecoder
|
decoder keys.SigningPubKeyDecoder
|
||||||
@ -21,33 +21,33 @@ type decreasedPermissionsParameters struct {
|
|||||||
startChange string
|
startChange string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewACLStateBuilder(decoder keys.SigningPubKeyDecoder, accountData *account.AccountData) *ACLStateBuilder {
|
func newACLStateBuilder(decoder keys.SigningPubKeyDecoder, accountData *account.AccountData) *aclStateBuilder {
|
||||||
return &ACLStateBuilder{
|
return &aclStateBuilder{
|
||||||
decoder: decoder,
|
decoder: decoder,
|
||||||
identity: accountData.Identity,
|
identity: accountData.Identity,
|
||||||
key: accountData.EncKey,
|
key: accountData.EncKey,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sb *ACLStateBuilder) Build() (*aclState, error) {
|
func (sb *aclStateBuilder) build() (*ACLState, error) {
|
||||||
state, _, err := sb.BuildBefore("")
|
state, _, err := sb.buildBefore("")
|
||||||
return state, err
|
return state, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sb *ACLStateBuilder) Init(tree *Tree) error {
|
func (sb *aclStateBuilder) init(tree *Tree) error {
|
||||||
root := tree.Root()
|
root := tree.Root()
|
||||||
if !root.IsSnapshot {
|
if !root.IsSnapshot {
|
||||||
return fmt.Errorf("root should always be a snapshot")
|
return fmt.Errorf("root should always be a snapshot")
|
||||||
}
|
}
|
||||||
|
|
||||||
snapshot := root.Content.GetAclData().GetAclSnapshot()
|
snapshot := root.Content.GetAclData().GetAclSnapshot()
|
||||||
state, err := NewACLStateFromSnapshot(
|
state, err := newACLStateFromSnapshot(
|
||||||
snapshot,
|
snapshot,
|
||||||
sb.identity,
|
sb.identity,
|
||||||
sb.key,
|
sb.key,
|
||||||
sb.decoder)
|
sb.decoder)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not build aclState from snapshot: %w", err)
|
return fmt.Errorf("could not build ACLState from snapshot: %w", err)
|
||||||
}
|
}
|
||||||
sb.tree = tree
|
sb.tree = tree
|
||||||
sb.aclState = state
|
sb.aclState = state
|
||||||
@ -56,7 +56,7 @@ func (sb *ACLStateBuilder) Init(tree *Tree) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: we can probably have only one state builder, because we can build both at the same time
|
// TODO: we can probably have only one state builder, because we can build both at the same time
|
||||||
func (sb *ACLStateBuilder) BuildBefore(beforeId string) (*aclState, bool, error) {
|
func (sb *aclStateBuilder) buildBefore(beforeId string) (*ACLState, bool, error) {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
startChange = sb.tree.root
|
startChange = sb.tree.root
|
||||||
@ -83,8 +83,8 @@ func (sb *ACLStateBuilder) BuildBefore(beforeId string) (*aclState, bool, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
// TODO: we should optimize this method to just remember last state of iterator and not iterate from the start and skip if nothing was removed from the tree
|
// TODO: we should optimize this method to just remember last state of iterator and not iterate from the start and skip if nothing was removed from the Tree
|
||||||
sb.tree.iterateSkip(sb.tree.root, startChange, func(c *Change) (isContinue bool) {
|
sb.tree.IterateSkip(sb.tree.root.Id, startChange.Id, func(c *Change) (isContinue bool) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
startChange = c
|
startChange = c
|
||||||
@ -101,13 +101,13 @@ func (sb *ACLStateBuilder) BuildBefore(beforeId string) (*aclState, bool, error)
|
|||||||
|
|
||||||
idSeenMap[c.Content.Identity] = append(idSeenMap[c.Content.Identity], c)
|
idSeenMap[c.Content.Identity] = append(idSeenMap[c.Content.Identity], c)
|
||||||
if c.Content.GetAclData() != nil {
|
if c.Content.GetAclData() != nil {
|
||||||
err = sb.aclState.ApplyChange(c.Id, c.Content)
|
err = sb.aclState.applyChange(c.Id, c.Content)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we have some users who have less permissions now
|
// if we have some users who have less permissions now
|
||||||
users := sb.aclState.GetPermissionDecreasedUsers(c.Content)
|
users := sb.aclState.getPermissionDecreasedUsers(c.Content)
|
||||||
if len(users) > 0 {
|
if len(users) > 0 {
|
||||||
decreasedPermissions = &decreasedPermissionsParameters{
|
decreasedPermissions = &decreasedPermissionsParameters{
|
||||||
users: users,
|
users: users,
|
||||||
@ -118,7 +118,7 @@ func (sb *ACLStateBuilder) BuildBefore(beforeId string) (*aclState, bool, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// the user can't make changes
|
// the user can't make changes
|
||||||
if !sb.aclState.HasPermission(c.Content.Identity, pb.ACLChange_Writer) && !sb.aclState.HasPermission(c.Content.Identity, pb.ACLChange_Admin) {
|
if !sb.aclState.hasPermission(c.Content.Identity, pb.ACLChange_Writer) && !sb.aclState.hasPermission(c.Content.Identity, pb.ACLChange_Admin) {
|
||||||
err = fmt.Errorf("user %s cannot make changes", c.Content.Identity)
|
err = fmt.Errorf("user %s cannot make changes", c.Content.Identity)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -168,8 +168,8 @@ func (sb *ACLStateBuilder) BuildBefore(beforeId string) (*aclState, bool, error)
|
|||||||
|
|
||||||
decreasedPermissions = nil
|
decreasedPermissions = nil
|
||||||
if removed {
|
if removed {
|
||||||
// starting from the beginning but with updated tree
|
// starting from the beginning but with updated Tree
|
||||||
return sb.BuildBefore(beforeId)
|
return sb.buildBefore(beforeId)
|
||||||
}
|
}
|
||||||
} else if err == nil {
|
} else if err == nil {
|
||||||
// we can finish the acl state building process
|
// we can finish the acl state building process
|
||||||
|
|||||||
@ -14,13 +14,13 @@ import (
|
|||||||
|
|
||||||
type ACLContext struct {
|
type ACLContext struct {
|
||||||
Tree *Tree
|
Tree *Tree
|
||||||
ACLState *aclState
|
ACLState *ACLState
|
||||||
}
|
}
|
||||||
|
|
||||||
func createTreeFromThread(t thread.Thread, fromStart bool) (*Tree, error) {
|
func createTreeFromThread(t thread.Thread, fromStart bool) (*Tree, error) {
|
||||||
treeBuilder := NewTreeBuilder(t, keys.NewEd25519Decoder())
|
treeBuilder := newTreeBuilder(t, keys.NewEd25519Decoder())
|
||||||
treeBuilder.Init()
|
treeBuilder.init()
|
||||||
return treeBuilder.Build(fromStart)
|
return treeBuilder.build(fromStart)
|
||||||
}
|
}
|
||||||
|
|
||||||
func createACLStateFromThread(
|
func createACLStateFromThread(
|
||||||
@ -39,33 +39,33 @@ func createACLStateFromThread(
|
|||||||
EncKey: key,
|
EncKey: key,
|
||||||
}
|
}
|
||||||
|
|
||||||
aclTreeBuilder := NewACLTreeBuilder(t, decoder)
|
aclTreeBuilder := newACLTreeBuilder(t, decoder)
|
||||||
aclTreeBuilder.Init()
|
aclTreeBuilder.init()
|
||||||
aclTree, err := aclTreeBuilder.Build()
|
aclTree, err := aclTreeBuilder.build()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !fromStart {
|
if !fromStart {
|
||||||
snapshotValidator := NewSnapshotValidator(decoder, accountData)
|
snapshotValidator := newSnapshotValidator(decoder, accountData)
|
||||||
snapshotValidator.Init(aclTree)
|
snapshotValidator.init(aclTree)
|
||||||
valid, err := snapshotValidator.ValidateSnapshot(tree.root)
|
valid, err := snapshotValidator.validateSnapshot(tree.root)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if !valid {
|
if !valid {
|
||||||
// TODO: think about what to do if the snapshot is invalid - should we rebuild the tree without it
|
// TODO: think about what to do if the snapshot is invalid - should we rebuild the Tree without it
|
||||||
return createACLStateFromThread(t, identity, key, decoder, true)
|
return createACLStateFromThread(t, identity, key, decoder, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
aclBuilder := NewACLStateBuilder(decoder, accountData)
|
aclBuilder := newACLStateBuilder(decoder, accountData)
|
||||||
err = aclBuilder.Init(tree)
|
err = aclBuilder.init(tree)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
aclState, err := aclBuilder.Build()
|
aclState, err := aclBuilder.build()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -88,7 +88,7 @@ func TestACLStateBuilder_UserJoinBuild(t *testing.T) {
|
|||||||
keys.NewEd25519Decoder(),
|
keys.NewEd25519Decoder(),
|
||||||
false)
|
false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("should build acl aclState without err: %v", err)
|
t.Fatalf("should build acl ACLState without err: %v", err)
|
||||||
}
|
}
|
||||||
aclState := ctx.ACLState
|
aclState := ctx.ACLState
|
||||||
//fmt.Println(ctx.Tree.Graph())
|
//fmt.Println(ctx.Tree.Graph())
|
||||||
@ -102,7 +102,7 @@ func TestACLStateBuilder_UserJoinBuild(t *testing.T) {
|
|||||||
assert.Equal(t, aclState.userStates[cId].Permissions, pb.ACLChange_Reader)
|
assert.Equal(t, aclState.userStates[cId].Permissions, pb.ACLChange_Reader)
|
||||||
|
|
||||||
var changeIds []string
|
var changeIds []string
|
||||||
ctx.Tree.iterate(ctx.Tree.root, func(c *Change) (isContinue bool) {
|
ctx.Tree.Iterate(ctx.Tree.root.Id, func(c *Change) (isContinue bool) {
|
||||||
changeIds = append(changeIds, c.Id)
|
changeIds = append(changeIds, c.Id)
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
@ -122,7 +122,7 @@ func TestACLStateBuilder_UserRemoveBuild(t *testing.T) {
|
|||||||
keys.NewEd25519Decoder(),
|
keys.NewEd25519Decoder(),
|
||||||
false)
|
false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("should build acl aclState without err: %v", err)
|
t.Fatalf("should build acl ACLState without err: %v", err)
|
||||||
}
|
}
|
||||||
aclState := ctx.ACLState
|
aclState := ctx.ACLState
|
||||||
//fmt.Println(ctx.Tree.Graph())
|
//fmt.Println(ctx.Tree.Graph())
|
||||||
@ -132,7 +132,7 @@ func TestACLStateBuilder_UserRemoveBuild(t *testing.T) {
|
|||||||
assert.Equal(t, aclState.userStates[aId].Permissions, pb.ACLChange_Admin)
|
assert.Equal(t, aclState.userStates[aId].Permissions, pb.ACLChange_Admin)
|
||||||
|
|
||||||
var changeIds []string
|
var changeIds []string
|
||||||
ctx.Tree.iterate(ctx.Tree.root, func(c *Change) (isContinue bool) {
|
ctx.Tree.Iterate(ctx.Tree.root.Id, func(c *Change) (isContinue bool) {
|
||||||
changeIds = append(changeIds, c.Id)
|
changeIds = append(changeIds, c.Id)
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
@ -152,7 +152,7 @@ func TestACLStateBuilder_UserRemoveBeforeBuild(t *testing.T) {
|
|||||||
keys.NewEd25519Decoder(),
|
keys.NewEd25519Decoder(),
|
||||||
false)
|
false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("should build acl aclState without err: %v", err)
|
t.Fatalf("should build acl ACLState without err: %v", err)
|
||||||
}
|
}
|
||||||
aclState := ctx.ACLState
|
aclState := ctx.ACLState
|
||||||
//fmt.Println(ctx.Tree.Graph())
|
//fmt.Println(ctx.Tree.Graph())
|
||||||
@ -163,7 +163,7 @@ func TestACLStateBuilder_UserRemoveBeforeBuild(t *testing.T) {
|
|||||||
assert.Nil(t, aclState.userStates[keychain.GetIdentity("B")])
|
assert.Nil(t, aclState.userStates[keychain.GetIdentity("B")])
|
||||||
|
|
||||||
var changeIds []string
|
var changeIds []string
|
||||||
ctx.Tree.iterate(ctx.Tree.root, func(c *Change) (isContinue bool) {
|
ctx.Tree.Iterate(ctx.Tree.root.Id, func(c *Change) (isContinue bool) {
|
||||||
changeIds = append(changeIds, c.Id)
|
changeIds = append(changeIds, c.Id)
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
@ -183,7 +183,7 @@ func TestACLStateBuilder_InvalidSnapshotBuild(t *testing.T) {
|
|||||||
keys.NewEd25519Decoder(),
|
keys.NewEd25519Decoder(),
|
||||||
false)
|
false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("should build acl aclState without err: %v", err)
|
t.Fatalf("should build acl ACLState without err: %v", err)
|
||||||
}
|
}
|
||||||
aclState := ctx.ACLState
|
aclState := ctx.ACLState
|
||||||
//fmt.Println(ctx.Tree.Graph())
|
//fmt.Println(ctx.Tree.Graph())
|
||||||
@ -193,7 +193,7 @@ func TestACLStateBuilder_InvalidSnapshotBuild(t *testing.T) {
|
|||||||
assert.Equal(t, aclState.identity, keychain.GetIdentity("A"))
|
assert.Equal(t, aclState.identity, keychain.GetIdentity("A"))
|
||||||
|
|
||||||
var changeIds []string
|
var changeIds []string
|
||||||
ctx.Tree.iterate(ctx.Tree.root, func(c *Change) (isContinue bool) {
|
ctx.Tree.Iterate(ctx.Tree.root.Id, func(c *Change) (isContinue bool) {
|
||||||
changeIds = append(changeIds, c.Id)
|
changeIds = append(changeIds, c.Id)
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
@ -213,7 +213,7 @@ func TestACLStateBuilder_ValidSnapshotBuild(t *testing.T) {
|
|||||||
keys.NewEd25519Decoder(),
|
keys.NewEd25519Decoder(),
|
||||||
false)
|
false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("should build acl aclState without err: %v", err)
|
t.Fatalf("should build acl ACLState without err: %v", err)
|
||||||
}
|
}
|
||||||
aclState := ctx.ACLState
|
aclState := ctx.ACLState
|
||||||
//fmt.Println(ctx.Tree.Graph())
|
//fmt.Println(ctx.Tree.Graph())
|
||||||
@ -223,7 +223,7 @@ func TestACLStateBuilder_ValidSnapshotBuild(t *testing.T) {
|
|||||||
assert.Equal(t, aclState.identity, keychain.GetIdentity("A"))
|
assert.Equal(t, aclState.identity, keychain.GetIdentity("A"))
|
||||||
|
|
||||||
var changeIds []string
|
var changeIds []string
|
||||||
ctx.Tree.iterate(ctx.Tree.root, func(c *Change) (isContinue bool) {
|
ctx.Tree.Iterate(ctx.Tree.root.Id, func(c *Change) (isContinue bool) {
|
||||||
changeIds = append(changeIds, c.Id)
|
changeIds = append(changeIds, c.Id)
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
|||||||
39
acltree/acltree.go
Normal file
39
acltree/acltree.go
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
package acltree
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/account"
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/thread"
|
||||||
|
)
|
||||||
|
|
||||||
|
func BuildACLTree(t thread.Thread, acc *account.AccountData) (ACLTree, error) {
|
||||||
|
// build tree from thread
|
||||||
|
// validate snapshot
|
||||||
|
// build aclstate -> filter tree
|
||||||
|
// return ACLTree(aclstate+)
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type AddResultSummary int
|
||||||
|
|
||||||
|
const (
|
||||||
|
AddResultSummaryNothing AddResultSummary = iota
|
||||||
|
AddResultSummaryAppend
|
||||||
|
AddResultSummaryRebuild
|
||||||
|
)
|
||||||
|
|
||||||
|
type AddResult struct {
|
||||||
|
AttachedChanges []*Change
|
||||||
|
InvalidChanges []*Change
|
||||||
|
UnattachedChanges []*Change
|
||||||
|
|
||||||
|
Summary AddResultSummary
|
||||||
|
}
|
||||||
|
|
||||||
|
type ACLTree interface {
|
||||||
|
ACLState() *ACLState
|
||||||
|
AddChanges(changes ...*Change) (AddResult, error)
|
||||||
|
Heads() []string
|
||||||
|
Iterate(func(change *Change) bool)
|
||||||
|
IterateFrom(string, func(change *Change) bool)
|
||||||
|
HasChange(change *Change)
|
||||||
|
}
|
||||||
@ -9,7 +9,7 @@ import (
|
|||||||
gothread "github.com/textileio/go-threads/core/thread"
|
gothread "github.com/textileio/go-threads/core/thread"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ACLTreeBuilder struct {
|
type aclTreeBuilder struct {
|
||||||
cache map[string]*Change
|
cache map[string]*Change
|
||||||
identityKeys map[string]keys.SigningPubKey
|
identityKeys map[string]keys.SigningPubKey
|
||||||
signingPubKeyDecoder keys.SigningPubKeyDecoder
|
signingPubKeyDecoder keys.SigningPubKeyDecoder
|
||||||
@ -19,8 +19,8 @@ type ACLTreeBuilder struct {
|
|||||||
*changeLoader
|
*changeLoader
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewACLTreeBuilder(t thread.Thread, decoder keys.SigningPubKeyDecoder) *ACLTreeBuilder {
|
func newACLTreeBuilder(t thread.Thread, decoder keys.SigningPubKeyDecoder) *aclTreeBuilder {
|
||||||
return &ACLTreeBuilder{
|
return &aclTreeBuilder{
|
||||||
signingPubKeyDecoder: decoder,
|
signingPubKeyDecoder: decoder,
|
||||||
thread: t,
|
thread: t,
|
||||||
changeLoader: newChangeLoader(
|
changeLoader: newChangeLoader(
|
||||||
@ -30,14 +30,14 @@ func NewACLTreeBuilder(t thread.Thread, decoder keys.SigningPubKeyDecoder) *ACLT
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tb *ACLTreeBuilder) Init() {
|
func (tb *aclTreeBuilder) init() {
|
||||||
tb.cache = make(map[string]*Change)
|
tb.cache = make(map[string]*Change)
|
||||||
tb.identityKeys = make(map[string]keys.SigningPubKey)
|
tb.identityKeys = make(map[string]keys.SigningPubKey)
|
||||||
tb.tree = &Tree{}
|
tb.tree = &Tree{}
|
||||||
tb.changeLoader.init(tb.cache, tb.identityKeys)
|
tb.changeLoader.init(tb.cache, tb.identityKeys)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tb *ACLTreeBuilder) Build() (*Tree, error) {
|
func (tb *aclTreeBuilder) build() (*Tree, error) {
|
||||||
heads := tb.thread.MaybeHeads()
|
heads := tb.thread.MaybeHeads()
|
||||||
aclHeads, err := tb.getACLHeads(heads)
|
aclHeads, err := tb.getACLHeads(heads)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -52,10 +52,10 @@ func (tb *ACLTreeBuilder) Build() (*Tree, error) {
|
|||||||
return tb.tree, nil
|
return tb.tree, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tb *ACLTreeBuilder) buildTreeFromStart(heads []string) (err error) {
|
func (tb *aclTreeBuilder) buildTreeFromStart(heads []string) (err error) {
|
||||||
changes, possibleRoots, err := tb.dfsFromStart(heads)
|
changes, possibleRoots, err := tb.dfsFromStart(heads)
|
||||||
if len(possibleRoots) == 0 {
|
if len(possibleRoots) == 0 {
|
||||||
return fmt.Errorf("cannot have tree without root")
|
return fmt.Errorf("cannot have Tree without root")
|
||||||
}
|
}
|
||||||
root, err := tb.getRoot(possibleRoots)
|
root, err := tb.getRoot(possibleRoots)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -67,7 +67,7 @@ func (tb *ACLTreeBuilder) buildTreeFromStart(heads []string) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tb *ACLTreeBuilder) dfsFromStart(heads []string) (buf []*Change, possibleRoots []*Change, err error) {
|
func (tb *aclTreeBuilder) dfsFromStart(heads []string) (buf []*Change, possibleRoots []*Change, err error) {
|
||||||
stack := make([]string, len(heads), len(heads)*2)
|
stack := make([]string, len(heads), len(heads)*2)
|
||||||
copy(stack, heads)
|
copy(stack, heads)
|
||||||
|
|
||||||
@ -98,7 +98,7 @@ func (tb *ACLTreeBuilder) dfsFromStart(heads []string) (buf []*Change, possibleR
|
|||||||
return buf, possibleRoots, nil
|
return buf, possibleRoots, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tb *ACLTreeBuilder) getRoot(possibleRoots []*Change) (*Change, error) {
|
func (tb *aclTreeBuilder) getRoot(possibleRoots []*Change) (*Change, error) {
|
||||||
threadId, err := gothread.Decode(tb.thread.ID())
|
threadId, err := gothread.Decode(tb.thread.ID())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -123,7 +123,7 @@ func (tb *ACLTreeBuilder) getRoot(possibleRoots []*Change) (*Change, error) {
|
|||||||
return nil, fmt.Errorf("could not find any root")
|
return nil, fmt.Errorf("could not find any root")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tb *ACLTreeBuilder) getACLHeads(heads []string) (aclTreeHeads []string, err error) {
|
func (tb *aclTreeBuilder) getACLHeads(heads []string) (aclTreeHeads []string, err error) {
|
||||||
for _, head := range heads {
|
for _, head := range heads {
|
||||||
if slice.FindPos(aclTreeHeads, head) != -1 { // do not scan known heads
|
if slice.FindPos(aclTreeHeads, head) != -1 { // do not scan known heads
|
||||||
continue
|
continue
|
||||||
@ -147,7 +147,7 @@ func (tb *ACLTreeBuilder) getACLHeads(heads []string) (aclTreeHeads []string, er
|
|||||||
return aclTreeHeads, nil
|
return aclTreeHeads, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tb *ACLTreeBuilder) getPrecedingACLHeads(head string) ([]string, error) {
|
func (tb *aclTreeBuilder) getPrecedingACLHeads(head string) ([]string, error) {
|
||||||
headChange, err := tb.loadChange(head)
|
headChange, err := tb.loadChange(head)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@ -76,7 +76,7 @@ func (c *changeLoader) verify(identity string, payload, signature []byte) (isVer
|
|||||||
func (c *changeLoader) makeVerifiedACLChange(change *thread.RawChange) (aclChange *pb.ACLChange, err error) {
|
func (c *changeLoader) makeVerifiedACLChange(change *thread.RawChange) (aclChange *pb.ACLChange, err error) {
|
||||||
aclChange = new(pb.ACLChange)
|
aclChange = new(pb.ACLChange)
|
||||||
|
|
||||||
// TODO: think what should we do with such cases, because this can be used by attacker to break our tree
|
// TODO: think what should we do with such cases, because this can be used by attacker to break our Tree
|
||||||
if err = proto.Unmarshal(change.Payload, aclChange); err != nil {
|
if err = proto.Unmarshal(change.Payload, aclChange); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,44 +7,44 @@ import (
|
|||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SnapshotValidator struct {
|
type snapshotValidator struct {
|
||||||
aclTree *Tree
|
aclTree *Tree
|
||||||
identity string
|
identity string
|
||||||
key keys.EncryptionPrivKey
|
key keys.EncryptionPrivKey
|
||||||
decoder keys.SigningPubKeyDecoder
|
decoder keys.SigningPubKeyDecoder
|
||||||
stateBuilder *ACLStateBuilder
|
stateBuilder *aclStateBuilder
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSnapshotValidator(
|
func newSnapshotValidator(
|
||||||
decoder keys.SigningPubKeyDecoder,
|
decoder keys.SigningPubKeyDecoder,
|
||||||
accountData *account.AccountData) *SnapshotValidator {
|
accountData *account.AccountData) *snapshotValidator {
|
||||||
return &SnapshotValidator{
|
return &snapshotValidator{
|
||||||
identity: accountData.Identity,
|
identity: accountData.Identity,
|
||||||
key: accountData.EncKey,
|
key: accountData.EncKey,
|
||||||
decoder: decoder,
|
decoder: decoder,
|
||||||
stateBuilder: NewACLStateBuilder(decoder, accountData),
|
stateBuilder: newACLStateBuilder(decoder, accountData),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SnapshotValidator) Init(aclTree *Tree) error {
|
func (s *snapshotValidator) init(aclTree *Tree) error {
|
||||||
s.aclTree = aclTree
|
s.aclTree = aclTree
|
||||||
return s.stateBuilder.Init(aclTree)
|
return s.stateBuilder.init(aclTree)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SnapshotValidator) ValidateSnapshot(ch *Change) (bool, error) {
|
func (s *snapshotValidator) validateSnapshot(ch *Change) (bool, error) {
|
||||||
st, found, err := s.stateBuilder.BuildBefore(ch.Id)
|
st, found, err := s.stateBuilder.buildBefore(ch.Id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !found {
|
if !found {
|
||||||
return false, fmt.Errorf("didn't find snapshot in ACL tree")
|
return false, fmt.Errorf("didn't find snapshot in ACL Tree")
|
||||||
}
|
}
|
||||||
|
|
||||||
otherSt, err := NewACLStateFromSnapshot(ch.Content.GetAclData().GetAclSnapshot(), s.identity, s.key, s.decoder)
|
otherSt, err := newACLStateFromSnapshot(ch.Content.GetAclData().GetAclSnapshot(), s.identity, s.key, s.decoder)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return st.Equal(otherSt), nil
|
return st.equal(otherSt), nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,6 +15,7 @@ const (
|
|||||||
Nothing
|
Nothing
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TODO: consider abstracting into separate package with iterator
|
||||||
type Tree struct {
|
type Tree struct {
|
||||||
root *Change
|
root *Change
|
||||||
headIds []string
|
headIds []string
|
||||||
@ -295,6 +296,12 @@ func (t *Tree) iterateSkip(start *Change, skipBefore *Change, f func(c *Change)
|
|||||||
it.iterateSkip(start, skipBefore, f)
|
it.iterateSkip(start, skipBefore, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *Tree) IterateSkip(startId string, skipBeforeId string, f func(c *Change) (isContinue bool)) {
|
||||||
|
it := newIterator()
|
||||||
|
defer freeIterator(it)
|
||||||
|
it.iterateSkip(t.attached[startId], t.attached[skipBeforeId], f)
|
||||||
|
}
|
||||||
|
|
||||||
func (t *Tree) Iterate(startId string, f func(c *Change) (isContinue bool)) {
|
func (t *Tree) Iterate(startId string, f func(c *Change) (isContinue bool)) {
|
||||||
t.iterate(t.attached[startId], f)
|
t.iterate(t.attached[startId], f)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,7 +17,7 @@ var (
|
|||||||
ErrEmpty = errors.New("logs empty")
|
ErrEmpty = errors.New("logs empty")
|
||||||
)
|
)
|
||||||
|
|
||||||
type TreeBuilder struct {
|
type treeBuilder struct {
|
||||||
cache map[string]*Change
|
cache map[string]*Change
|
||||||
identityKeys map[string]keys.SigningPubKey
|
identityKeys map[string]keys.SigningPubKey
|
||||||
signingPubKeyDecoder keys.SigningPubKeyDecoder
|
signingPubKeyDecoder keys.SigningPubKeyDecoder
|
||||||
@ -27,8 +27,8 @@ type TreeBuilder struct {
|
|||||||
*changeLoader
|
*changeLoader
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTreeBuilder(t thread.Thread, decoder keys.SigningPubKeyDecoder) *TreeBuilder {
|
func newTreeBuilder(t thread.Thread, decoder keys.SigningPubKeyDecoder) *treeBuilder {
|
||||||
return &TreeBuilder{
|
return &treeBuilder{
|
||||||
signingPubKeyDecoder: decoder,
|
signingPubKeyDecoder: decoder,
|
||||||
thread: t,
|
thread: t,
|
||||||
changeLoader: newChangeLoader(
|
changeLoader: newChangeLoader(
|
||||||
@ -38,14 +38,14 @@ func NewTreeBuilder(t thread.Thread, decoder keys.SigningPubKeyDecoder) *TreeBui
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tb *TreeBuilder) Init() {
|
func (tb *treeBuilder) init() {
|
||||||
tb.cache = make(map[string]*Change)
|
tb.cache = make(map[string]*Change)
|
||||||
tb.identityKeys = make(map[string]keys.SigningPubKey)
|
tb.identityKeys = make(map[string]keys.SigningPubKey)
|
||||||
tb.tree = &Tree{}
|
tb.tree = &Tree{}
|
||||||
tb.changeLoader.init(tb.cache, tb.identityKeys)
|
tb.changeLoader.init(tb.cache, tb.identityKeys)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tb *TreeBuilder) Build(fromStart bool) (*Tree, error) {
|
func (tb *treeBuilder) build(fromStart bool) (*Tree, error) {
|
||||||
heads := tb.thread.MaybeHeads()
|
heads := tb.thread.MaybeHeads()
|
||||||
|
|
||||||
if fromStart {
|
if fromStart {
|
||||||
@ -68,10 +68,10 @@ func (tb *TreeBuilder) Build(fromStart bool) (*Tree, error) {
|
|||||||
return tb.tree, nil
|
return tb.tree, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tb *TreeBuilder) buildTreeFromStart(heads []string) (err error) {
|
func (tb *treeBuilder) buildTreeFromStart(heads []string) (err error) {
|
||||||
changes, possibleRoots, err := tb.dfsFromStart(heads)
|
changes, possibleRoots, err := tb.dfsFromStart(heads)
|
||||||
if len(possibleRoots) == 0 {
|
if len(possibleRoots) == 0 {
|
||||||
return fmt.Errorf("cannot have tree without root")
|
return fmt.Errorf("cannot have Tree without root")
|
||||||
}
|
}
|
||||||
root, err := tb.getRoot(possibleRoots)
|
root, err := tb.getRoot(possibleRoots)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -83,7 +83,7 @@ func (tb *TreeBuilder) buildTreeFromStart(heads []string) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tb *TreeBuilder) dfsFromStart(heads []string) (buf []*Change, possibleRoots []*Change, err error) {
|
func (tb *treeBuilder) dfsFromStart(heads []string) (buf []*Change, possibleRoots []*Change, err error) {
|
||||||
stack := make([]string, len(heads), len(heads)*2)
|
stack := make([]string, len(heads), len(heads)*2)
|
||||||
copy(stack, heads)
|
copy(stack, heads)
|
||||||
|
|
||||||
@ -114,7 +114,7 @@ func (tb *TreeBuilder) dfsFromStart(heads []string) (buf []*Change, possibleRoot
|
|||||||
return buf, possibleRoots, nil
|
return buf, possibleRoots, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tb *TreeBuilder) buildTree(heads []string, breakpoint string) (err error) {
|
func (tb *treeBuilder) buildTree(heads []string, breakpoint string) (err error) {
|
||||||
ch, err := tb.loadChange(breakpoint)
|
ch, err := tb.loadChange(breakpoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
@ -126,7 +126,7 @@ func (tb *TreeBuilder) buildTree(heads []string, breakpoint string) (err error)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tb *TreeBuilder) dfs(heads []string, breakpoint string) (buf []*Change, err error) {
|
func (tb *treeBuilder) dfs(heads []string, breakpoint string) (buf []*Change, err error) {
|
||||||
stack := make([]string, len(heads), len(heads)*2)
|
stack := make([]string, len(heads), len(heads)*2)
|
||||||
copy(stack, heads)
|
copy(stack, heads)
|
||||||
|
|
||||||
@ -154,7 +154,7 @@ func (tb *TreeBuilder) dfs(heads []string, breakpoint string) (buf []*Change, er
|
|||||||
return buf, nil
|
return buf, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tb *TreeBuilder) findBreakpoint(heads []string) (breakpoint string, err error) {
|
func (tb *treeBuilder) findBreakpoint(heads []string) (breakpoint string, err error) {
|
||||||
var (
|
var (
|
||||||
ch *Change
|
ch *Change
|
||||||
snapshotIds []string
|
snapshotIds []string
|
||||||
@ -171,7 +171,7 @@ func (tb *TreeBuilder) findBreakpoint(heads []string) (breakpoint string, err er
|
|||||||
return tb.findCommonSnapshot(snapshotIds)
|
return tb.findCommonSnapshot(snapshotIds)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tb *TreeBuilder) findCommonSnapshot(snapshotIds []string) (snapshotId string, err error) {
|
func (tb *treeBuilder) findCommonSnapshot(snapshotIds []string) (snapshotId string, err error) {
|
||||||
if len(snapshotIds) == 1 {
|
if len(snapshotIds) == 1 {
|
||||||
return snapshotIds[0], nil
|
return snapshotIds[0], nil
|
||||||
} else if len(snapshotIds) == 0 {
|
} else if len(snapshotIds) == 0 {
|
||||||
@ -190,7 +190,7 @@ func (tb *TreeBuilder) findCommonSnapshot(snapshotIds []string) (snapshotId stri
|
|||||||
return snapshotIds[0], nil
|
return snapshotIds[0], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tb *TreeBuilder) findCommonForTwoSnapshots(s1, s2 string) (s string, err error) {
|
func (tb *treeBuilder) findCommonForTwoSnapshots(s1, s2 string) (s string, err error) {
|
||||||
// fast cases
|
// fast cases
|
||||||
if s1 == s2 {
|
if s1 == s2 {
|
||||||
return s1, nil
|
return s1, nil
|
||||||
@ -249,14 +249,14 @@ func (tb *TreeBuilder) findCommonForTwoSnapshots(s1, s2 string) (s string, err e
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Warnf("changes build tree: possible versions split")
|
log.Warnf("changes build Tree: possible versions split")
|
||||||
|
|
||||||
// prefer not first snapshot
|
// prefer not first snapshot
|
||||||
if len(ch1.PreviousIds) == 0 && len(ch2.PreviousIds) > 0 {
|
if len(ch1.PreviousIds) == 0 && len(ch2.PreviousIds) > 0 {
|
||||||
log.Warnf("changes build tree: prefer %s(%d prevIds) over %s(%d prevIds)", s2, len(ch2.PreviousIds), s1, len(ch1.PreviousIds))
|
log.Warnf("changes build Tree: prefer %s(%d prevIds) over %s(%d prevIds)", s2, len(ch2.PreviousIds), s1, len(ch1.PreviousIds))
|
||||||
return s2, nil
|
return s2, nil
|
||||||
} else if len(ch1.PreviousIds) > 0 && len(ch2.PreviousIds) == 0 {
|
} else if len(ch1.PreviousIds) > 0 && len(ch2.PreviousIds) == 0 {
|
||||||
log.Warnf("changes build tree: prefer %s(%d prevIds) over %s(%d prevIds)", s1, len(ch1.PreviousIds), s2, len(ch2.PreviousIds))
|
log.Warnf("changes build Tree: prefer %s(%d prevIds) over %s(%d prevIds)", s1, len(ch1.PreviousIds), s2, len(ch2.PreviousIds))
|
||||||
return s1, nil
|
return s1, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -268,25 +268,25 @@ func (tb *TreeBuilder) findCommonForTwoSnapshots(s1, s2 string) (s string, err e
|
|||||||
// TODO: can we even have empty snapshots?
|
// TODO: can we even have empty snapshots?
|
||||||
// prefer not empty snapshot
|
// prefer not empty snapshot
|
||||||
if isEmptySnapshot(ch1) && !isEmptySnapshot(ch2) {
|
if isEmptySnapshot(ch1) && !isEmptySnapshot(ch2) {
|
||||||
log.Warnf("changes build tree: prefer %s(not empty) over %s(empty)", s2, s1)
|
log.Warnf("changes build Tree: prefer %s(not empty) over %s(empty)", s2, s1)
|
||||||
return s2, nil
|
return s2, nil
|
||||||
} else if isEmptySnapshot(ch2) && !isEmptySnapshot(ch1) {
|
} else if isEmptySnapshot(ch2) && !isEmptySnapshot(ch1) {
|
||||||
log.Warnf("changes build tree: prefer %s(not empty) over %s(empty)", s1, s2)
|
log.Warnf("changes build Tree: prefer %s(not empty) over %s(empty)", s1, s2)
|
||||||
return s1, nil
|
return s1, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: add virtual change mechanics
|
// TODO: add virtual change mechanics
|
||||||
// unexpected behavior - just return lesser id
|
// unexpected behavior - just return lesser id
|
||||||
if s1 < s2 {
|
if s1 < s2 {
|
||||||
log.Warnf("changes build tree: prefer %s (%s<%s)", s1, s1, s2)
|
log.Warnf("changes build Tree: prefer %s (%s<%s)", s1, s1, s2)
|
||||||
return s1, nil
|
return s1, nil
|
||||||
}
|
}
|
||||||
log.Warnf("changes build tree: prefer %s (%s<%s)", s2, s2, s1)
|
log.Warnf("changes build Tree: prefer %s (%s<%s)", s2, s2, s1)
|
||||||
|
|
||||||
return s2, nil
|
return s2, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tb *TreeBuilder) getRoot(possibleRoots []*Change) (*Change, error) {
|
func (tb *treeBuilder) getRoot(possibleRoots []*Change) (*Change, error) {
|
||||||
threadId, err := gothread.Decode(tb.thread.ID())
|
threadId, err := gothread.Decode(tb.thread.ID())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@ -8,11 +8,11 @@ package acltree
|
|||||||
//
|
//
|
||||||
// res, err := createTreeFromThread(thread)
|
// res, err := createTreeFromThread(thread)
|
||||||
// if err != nil {
|
// if err != nil {
|
||||||
// t.Fatalf("build tree should not result in an error: %v", res)
|
// t.Fatalf("build Tree should not result in an error: %v", res)
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// assert.Equal(t, res.Heads(), []string{"C.1.1"})
|
// assert.equal(t, res.Heads(), []string{"C.1.1"})
|
||||||
// assert.Equal(t, res.Len(), 4)
|
// assert.equal(t, res.Len(), 4)
|
||||||
//}
|
//}
|
||||||
//
|
//
|
||||||
//func TestTreeBuilder_UserJoinTestTreeIterate(t *testing.T) {
|
//func TestTreeBuilder_UserJoinTestTreeIterate(t *testing.T) {
|
||||||
@ -23,17 +23,17 @@ package acltree
|
|||||||
//
|
//
|
||||||
// res, err := createTreeFromThread(thread)
|
// res, err := createTreeFromThread(thread)
|
||||||
// if err != nil {
|
// if err != nil {
|
||||||
// t.Fatalf("build tree should not result in an error: %v", res)
|
// t.Fatalf("build Tree should not result in an error: %v", res)
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// assert.Equal(t, res.Heads(), []string{"C.1.1"})
|
// assert.equal(t, res.Heads(), []string{"C.1.1"})
|
||||||
// assert.Equal(t, res.Len(), 4)
|
// assert.equal(t, res.Len(), 4)
|
||||||
// var changeIds []string
|
// var changeIds []string
|
||||||
// res.iterate(res.root, func(c *Change) (isContinue bool) {
|
// res.iterate(res.root, func(c *Change) (isContinue bool) {
|
||||||
// changeIds = append(changeIds, c.Id)
|
// changeIds = append(changeIds, c.Id)
|
||||||
// return true
|
// return true
|
||||||
// })
|
// })
|
||||||
// assert.Equal(t, changeIds, []string{"A.1.1", "A.1.2", "B.1.1", "C.1.1"})
|
// assert.equal(t, changeIds, []string{"A.1.1", "A.1.2", "B.1.1", "C.1.1"})
|
||||||
//}
|
//}
|
||||||
//
|
//
|
||||||
//func TestTreeBuilder_UserRemoveTestTreeIterate(t *testing.T) {
|
//func TestTreeBuilder_UserRemoveTestTreeIterate(t *testing.T) {
|
||||||
@ -44,15 +44,15 @@ package acltree
|
|||||||
//
|
//
|
||||||
// res, err := createTreeFromThread(thread)
|
// res, err := createTreeFromThread(thread)
|
||||||
// if err != nil {
|
// if err != nil {
|
||||||
// t.Fatalf("build tree should not result in an error: %v", res)
|
// t.Fatalf("build Tree should not result in an error: %v", res)
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// assert.Equal(t, res.Heads(), []string{"A.1.3"})
|
// assert.equal(t, res.Heads(), []string{"A.1.3"})
|
||||||
// assert.Equal(t, res.Len(), 4)
|
// assert.equal(t, res.Len(), 4)
|
||||||
// var changeIds []string
|
// var changeIds []string
|
||||||
// res.iterate(res.root, func(c *Change) (isContinue bool) {
|
// res.iterate(res.root, func(c *Change) (isContinue bool) {
|
||||||
// changeIds = append(changeIds, c.Id)
|
// changeIds = append(changeIds, c.Id)
|
||||||
// return true
|
// return true
|
||||||
// })
|
// })
|
||||||
// assert.Equal(t, changeIds, []string{"A.1.1", "A.1.2", "B.1.1", "A.1.3"})
|
// assert.equal(t, changeIds, []string{"A.1.1", "A.1.2", "B.1.1", "A.1.3"})
|
||||||
//}
|
//}
|
||||||
|
|||||||
@ -109,7 +109,7 @@ func (t *Tree) Graph() (data string, err error) {
|
|||||||
if n, ok := nodes[id]; ok {
|
if n, ok := nodes[id]; ok {
|
||||||
return n, nil
|
return n, nil
|
||||||
}
|
}
|
||||||
n, err := graph.CreateNode(fmt.Sprintf("%s: not in tree", id))
|
n, err := graph.CreateNode(fmt.Sprintf("%s: not in Tree", id))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,10 +21,10 @@ type Document struct {
|
|||||||
accountData *AccountData
|
accountData *AccountData
|
||||||
decoder threadmodels.SigningPubKeyDecoder
|
decoder threadmodels.SigningPubKeyDecoder
|
||||||
|
|
||||||
treeBuilder *acltree.TreeBuilder
|
treeBuilder *acltree.treeBuilder
|
||||||
aclTreeBuilder *acltree.ACLTreeBuilder
|
aclTreeBuilder *acltree.aclTreeBuilder
|
||||||
aclStateBuilder *acltree.ACLStateBuilder
|
aclStateBuilder *acltree.aclStateBuilder
|
||||||
snapshotValidator *acltree.SnapshotValidator
|
snapshotValidator *acltree.snapshotValidator
|
||||||
docStateBuilder *acltree.documentStateBuilder
|
docStateBuilder *acltree.documentStateBuilder
|
||||||
|
|
||||||
docContext *acltree.documentContext
|
docContext *acltree.documentContext
|
||||||
@ -55,10 +55,10 @@ func NewDocument(
|
|||||||
stateProvider: stateProvider,
|
stateProvider: stateProvider,
|
||||||
accountData: accountData,
|
accountData: accountData,
|
||||||
decoder: decoder,
|
decoder: decoder,
|
||||||
aclTreeBuilder: acltree.NewACLTreeBuilder(thread, decoder),
|
aclTreeBuilder: acltree.newACLTreeBuilder(thread, decoder),
|
||||||
treeBuilder: acltree.NewTreeBuilder(thread, decoder),
|
treeBuilder: acltree.newTreeBuilder(thread, decoder),
|
||||||
snapshotValidator: acltree.NewSnapshotValidator(decoder, accountData),
|
snapshotValidator: acltree.newSnapshotValidator(decoder, accountData),
|
||||||
aclStateBuilder: acltree.NewACLStateBuilder(decoder, accountData),
|
aclStateBuilder: acltree.newACLStateBuilder(decoder, accountData),
|
||||||
docStateBuilder: acltree.newDocumentStateBuilder(stateProvider),
|
docStateBuilder: acltree.newDocumentStateBuilder(stateProvider),
|
||||||
docContext: &acltree.documentContext{},
|
docContext: &acltree.documentContext{},
|
||||||
}
|
}
|
||||||
@ -242,28 +242,28 @@ func (d *Document) getPrecedingACLHeads(head string) []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *Document) build(fromStart bool) (DocumentState, error) {
|
func (d *Document) build(fromStart bool) (DocumentState, error) {
|
||||||
d.treeBuilder.Init()
|
d.treeBuilder.init()
|
||||||
d.aclTreeBuilder.Init()
|
d.aclTreeBuilder.init()
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
d.docContext.fullTree, err = d.treeBuilder.Build(fromStart)
|
d.docContext.fullTree, err = d.treeBuilder.build(fromStart)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: remove this from context as this is used only to validate snapshot
|
// TODO: remove this from context as this is used only to validate snapshot
|
||||||
d.docContext.aclTree, err = d.aclTreeBuilder.Build()
|
d.docContext.aclTree, err = d.aclTreeBuilder.build()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !fromStart {
|
if !fromStart {
|
||||||
err = d.snapshotValidator.Init(d.docContext.aclTree)
|
err = d.snapshotValidator.init(d.docContext.aclTree)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
valid, err := d.snapshotValidator.ValidateSnapshot(d.docContext.fullTree.root)
|
valid, err := d.snapshotValidator.validateSnapshot(d.docContext.fullTree.root)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -271,12 +271,12 @@ func (d *Document) build(fromStart bool) (DocumentState, error) {
|
|||||||
return d.build(true)
|
return d.build(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
err = d.aclStateBuilder.Init(d.docContext.fullTree)
|
err = d.aclStateBuilder.init(d.docContext.fullTree)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
d.docContext.aclState, err = d.aclStateBuilder.Build()
|
d.docContext.aclState, err = d.aclStateBuilder.build()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,8 +3,8 @@ package exampledocument
|
|||||||
import "github.com/anytypeio/go-anytype-infrastructure-experiments/acltree"
|
import "github.com/anytypeio/go-anytype-infrastructure-experiments/acltree"
|
||||||
|
|
||||||
type documentContext struct {
|
type documentContext struct {
|
||||||
aclTree *acltree.Tree // TODO: remove it, because we don't use it
|
aclTree *acltree.tree // TODO: remove it, because we don't use it
|
||||||
fullTree *acltree.Tree
|
fullTree *acltree.tree
|
||||||
aclState *acltree.aclState
|
aclState *acltree.aclState
|
||||||
docState DocumentState
|
docState DocumentState
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import (
|
|||||||
// example ->
|
// example ->
|
||||||
|
|
||||||
type documentStateBuilder struct {
|
type documentStateBuilder struct {
|
||||||
tree *acltree.Tree
|
tree *acltree.tree
|
||||||
aclState *acltree.aclState // TODO: decide if this is needed or not
|
aclState *acltree.aclState // TODO: decide if this is needed or not
|
||||||
stateProvider InitialStateProvider
|
stateProvider InitialStateProvider
|
||||||
}
|
}
|
||||||
@ -19,7 +19,7 @@ func newDocumentStateBuilder(stateProvider InitialStateProvider) *documentStateB
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *documentStateBuilder) init(aclState *acltree.aclState, tree *acltree.Tree) {
|
func (d *documentStateBuilder) init(aclState *acltree.aclState, tree *acltree.tree) {
|
||||||
d.tree = tree
|
d.tree = tree
|
||||||
d.aclState = aclState
|
d.aclState = aclState
|
||||||
}
|
}
|
||||||
@ -67,7 +67,7 @@ func (d *documentStateBuilder) build() (s DocumentState, err error) {
|
|||||||
func (d *documentStateBuilder) appendFrom(fromId string, init DocumentState) (s DocumentState, err error) {
|
func (d *documentStateBuilder) appendFrom(fromId string, init DocumentState) (s DocumentState, err error) {
|
||||||
// TODO: we should do something like state copy probably
|
// TODO: we should do something like state copy probably
|
||||||
s = init
|
s = init
|
||||||
// TODO: we should have the same logic as in ACLStateBuilder, that means we should either pass in both methods state from the outside or save the state inside the builder
|
// TODO: we should have the same logic as in aclStateBuilder, that means we should either pass in both methods state from the outside or save the state inside the builder
|
||||||
d.tree.Iterate(fromId, func(c *acltree.Change) (isContinue bool) {
|
d.tree.Iterate(fromId, func(c *acltree.Change) (isContinue bool) {
|
||||||
if c.Id == fromId {
|
if c.Id == fromId {
|
||||||
return true
|
return true
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user