mirror of
https://github.com/status-im/status-go.git
synced 2025-02-18 01:37:22 +00:00
feat: rekey community on control node change
closes: status-im/status-desktop#11963
This commit is contained in:
parent
246b68a8c0
commit
3292c1c883
@ -2207,7 +2207,10 @@ func (o *Community) createChat(chatID string, chat *protobuf.CommunityChat) erro
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
chat.Members = o.config.CommunityDescription.Members
|
chat.Members = make(map[string]*protobuf.CommunityMember)
|
||||||
|
for pubKey, member := range o.config.CommunityDescription.Members {
|
||||||
|
chat.Members[pubKey] = member
|
||||||
|
}
|
||||||
|
|
||||||
o.config.CommunityDescription.Chats[chatID] = chat
|
o.config.CommunityDescription.Chats[chatID] = chat
|
||||||
|
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
package communities
|
package communities
|
||||||
|
|
||||||
import "github.com/status-im/status-go/protocol/protobuf"
|
import (
|
||||||
|
"crypto/ecdsa"
|
||||||
|
|
||||||
|
"github.com/status-im/status-go/protocol/protobuf"
|
||||||
|
)
|
||||||
|
|
||||||
type CommunityChatChanges struct {
|
type CommunityChatChanges struct {
|
||||||
ChatModified *protobuf.CommunityChat
|
ChatModified *protobuf.CommunityChat
|
||||||
@ -14,6 +18,8 @@ type CommunityChatChanges struct {
|
|||||||
type CommunityChanges struct {
|
type CommunityChanges struct {
|
||||||
Community *Community `json:"community"`
|
Community *Community `json:"community"`
|
||||||
|
|
||||||
|
ControlNodeChanged *ecdsa.PublicKey `json:"controlNodeChanged"`
|
||||||
|
|
||||||
MembersAdded map[string]*protobuf.CommunityMember `json:"membersAdded"`
|
MembersAdded map[string]*protobuf.CommunityMember `json:"membersAdded"`
|
||||||
MembersRemoved map[string]*protobuf.CommunityMember `json:"membersRemoved"`
|
MembersRemoved map[string]*protobuf.CommunityMember `json:"membersRemoved"`
|
||||||
|
|
||||||
@ -82,6 +88,10 @@ func (c *CommunityChanges) HasMemberLeft(identity string) bool {
|
|||||||
func EvaluateCommunityChanges(origin, modified *Community) *CommunityChanges {
|
func EvaluateCommunityChanges(origin, modified *Community) *CommunityChanges {
|
||||||
changes := evaluateCommunityChangesByDescription(origin.Description(), modified.Description())
|
changes := evaluateCommunityChangesByDescription(origin.Description(), modified.Description())
|
||||||
|
|
||||||
|
if origin.ControlNode() != nil && !modified.ControlNode().Equal(origin.ControlNode()) {
|
||||||
|
changes.ControlNodeChanged = modified.ControlNode()
|
||||||
|
}
|
||||||
|
|
||||||
originTokenPermissions := origin.tokenPermissions()
|
originTokenPermissions := origin.tokenPermissions()
|
||||||
modifiedTokenPermissions := modified.tokenPermissions()
|
modifiedTokenPermissions := modified.tokenPermissions()
|
||||||
|
|
||||||
|
@ -57,7 +57,14 @@ func evaluateCommunityLevelEncryptionKeyAction(origin, modified *Community, chan
|
|||||||
originBecomeMemberPermissions := origin.TokenPermissionsByType(protobuf.CommunityTokenPermission_BECOME_MEMBER)
|
originBecomeMemberPermissions := origin.TokenPermissionsByType(protobuf.CommunityTokenPermission_BECOME_MEMBER)
|
||||||
modifiedBecomeMemberPermissions := modified.TokenPermissionsByType(protobuf.CommunityTokenPermission_BECOME_MEMBER)
|
modifiedBecomeMemberPermissions := modified.TokenPermissionsByType(protobuf.CommunityTokenPermission_BECOME_MEMBER)
|
||||||
|
|
||||||
return evaluateEncryptionKeyAction(originBecomeMemberPermissions, modifiedBecomeMemberPermissions, modified.config.CommunityDescription.Members, changes.MembersAdded, changes.MembersRemoved)
|
return evaluateEncryptionKeyAction(
|
||||||
|
originBecomeMemberPermissions,
|
||||||
|
modifiedBecomeMemberPermissions,
|
||||||
|
modified.config.CommunityDescription.Members,
|
||||||
|
changes.MembersAdded,
|
||||||
|
changes.MembersRemoved,
|
||||||
|
changes.ControlNodeChanged != nil,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func evaluateChannelLevelEncryptionKeyActions(origin, modified *Community, changes *CommunityChanges) *map[string]EncryptionKeyAction {
|
func evaluateChannelLevelEncryptionKeyActions(origin, modified *Community, changes *CommunityChanges) *map[string]EncryptionKeyAction {
|
||||||
@ -83,13 +90,20 @@ func evaluateChannelLevelEncryptionKeyActions(origin, modified *Community, chang
|
|||||||
membersRemoved = chatChanges.MembersRemoved
|
membersRemoved = chatChanges.MembersRemoved
|
||||||
}
|
}
|
||||||
|
|
||||||
result[channelID] = *evaluateEncryptionKeyAction(originChannelPermissions, modifiedChannelPermissions, modified.config.CommunityDescription.Chats[channelID].Members, membersAdded, membersRemoved)
|
result[channelID] = *evaluateEncryptionKeyAction(
|
||||||
|
originChannelPermissions,
|
||||||
|
modifiedChannelPermissions,
|
||||||
|
modified.config.CommunityDescription.Chats[channelID].Members,
|
||||||
|
membersAdded, membersRemoved,
|
||||||
|
changes.ControlNodeChanged != nil,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &result
|
return &result
|
||||||
}
|
}
|
||||||
|
|
||||||
func evaluateEncryptionKeyAction(originPermissions, modifiedPermissions []*CommunityTokenPermission, allMembers, membersAdded, membersRemoved map[string]*protobuf.CommunityMember) *EncryptionKeyAction {
|
func evaluateEncryptionKeyAction(originPermissions, modifiedPermissions []*CommunityTokenPermission,
|
||||||
|
allMembers, membersAdded, membersRemoved map[string]*protobuf.CommunityMember, controlNodeChanged bool) *EncryptionKeyAction {
|
||||||
result := &EncryptionKeyAction{
|
result := &EncryptionKeyAction{
|
||||||
ActionType: EncryptionKeyNone,
|
ActionType: EncryptionKeyNone,
|
||||||
Members: map[string]*protobuf.CommunityMember{},
|
Members: map[string]*protobuf.CommunityMember{},
|
||||||
@ -103,6 +117,13 @@ func evaluateEncryptionKeyAction(originPermissions, modifiedPermissions []*Commu
|
|||||||
return to
|
return to
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// control node changed on closed community/channel
|
||||||
|
if controlNodeChanged && len(modifiedPermissions) > 0 {
|
||||||
|
result.ActionType = EncryptionKeyRekey
|
||||||
|
result.Members = copyMap(allMembers)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
// permission was just added
|
// permission was just added
|
||||||
if len(modifiedPermissions) > 0 && len(originPermissions) == 0 {
|
if len(modifiedPermissions) > 0 && len(originPermissions) == 0 {
|
||||||
result.ActionType = EncryptionKeyAdd
|
result.ActionType = EncryptionKeyAdd
|
||||||
|
@ -2,6 +2,7 @@ package communities
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
@ -26,6 +27,7 @@ func createTestCommunity(identity *ecdsa.PrivateKey) (*Community, error) {
|
|||||||
CommunityTokensMetadata: []*protobuf.CommunityTokenMetadata{},
|
CommunityTokensMetadata: []*protobuf.CommunityTokenMetadata{},
|
||||||
},
|
},
|
||||||
ID: &identity.PublicKey,
|
ID: &identity.PublicKey,
|
||||||
|
ControlNode: &identity.PublicKey,
|
||||||
Joined: true,
|
Joined: true,
|
||||||
MemberIdentity: &identity.PublicKey,
|
MemberIdentity: &identity.PublicKey,
|
||||||
}
|
}
|
||||||
@ -825,3 +827,160 @@ func (s *CommunityEncryptionKeyActionSuite) TestNilOrigin() {
|
|||||||
s.Require().NotNil(actions.ChannelKeysActions[channelID])
|
s.Require().NotNil(actions.ChannelKeysActions[channelID])
|
||||||
s.Require().Equal(actions.ChannelKeysActions[channelID].ActionType, EncryptionKeyAdd)
|
s.Require().Equal(actions.ChannelKeysActions[channelID].ActionType, EncryptionKeyAdd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *CommunityEncryptionKeyActionSuite) TestControlNodeChange() {
|
||||||
|
channelID := "1234"
|
||||||
|
chatID := types.EncodeHex(crypto.CompressPubkey(&s.identity.PublicKey)) + channelID
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
permissions []*protobuf.CommunityTokenPermission
|
||||||
|
members []*ecdsa.PublicKey
|
||||||
|
channelMembers []*ecdsa.PublicKey
|
||||||
|
expectedActions EncryptionKeyActions
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "change control node in open community",
|
||||||
|
permissions: []*protobuf.CommunityTokenPermission{},
|
||||||
|
members: []*ecdsa.PublicKey{&s.member1.PublicKey, &s.member2.PublicKey},
|
||||||
|
channelMembers: []*ecdsa.PublicKey{&s.member1.PublicKey},
|
||||||
|
expectedActions: EncryptionKeyActions{
|
||||||
|
CommunityKeyAction: EncryptionKeyAction{
|
||||||
|
ActionType: EncryptionKeyNone,
|
||||||
|
Members: map[string]*protobuf.CommunityMember{},
|
||||||
|
},
|
||||||
|
ChannelKeysActions: map[string]EncryptionKeyAction{
|
||||||
|
channelID: EncryptionKeyAction{
|
||||||
|
ActionType: EncryptionKeyNone,
|
||||||
|
Members: map[string]*protobuf.CommunityMember{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "change control node in token-gated community",
|
||||||
|
permissions: []*protobuf.CommunityTokenPermission{
|
||||||
|
&protobuf.CommunityTokenPermission{
|
||||||
|
Id: "some-id",
|
||||||
|
Type: protobuf.CommunityTokenPermission_BECOME_MEMBER,
|
||||||
|
TokenCriteria: make([]*protobuf.TokenCriteria, 0),
|
||||||
|
ChatIds: []string{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
members: []*ecdsa.PublicKey{&s.member1.PublicKey, &s.member2.PublicKey},
|
||||||
|
channelMembers: []*ecdsa.PublicKey{&s.member1.PublicKey},
|
||||||
|
expectedActions: EncryptionKeyActions{
|
||||||
|
CommunityKeyAction: EncryptionKeyAction{
|
||||||
|
ActionType: EncryptionKeyRekey,
|
||||||
|
Members: map[string]*protobuf.CommunityMember{
|
||||||
|
s.member1Key: &protobuf.CommunityMember{},
|
||||||
|
s.member2Key: &protobuf.CommunityMember{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ChannelKeysActions: map[string]EncryptionKeyAction{
|
||||||
|
channelID: EncryptionKeyAction{
|
||||||
|
ActionType: EncryptionKeyNone,
|
||||||
|
Members: map[string]*protobuf.CommunityMember{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "change control node in open community with token-gated channel",
|
||||||
|
permissions: []*protobuf.CommunityTokenPermission{
|
||||||
|
&protobuf.CommunityTokenPermission{
|
||||||
|
Id: "some-id",
|
||||||
|
Type: protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL,
|
||||||
|
TokenCriteria: make([]*protobuf.TokenCriteria, 0),
|
||||||
|
ChatIds: []string{chatID},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
members: []*ecdsa.PublicKey{&s.member1.PublicKey, &s.member2.PublicKey},
|
||||||
|
channelMembers: []*ecdsa.PublicKey{&s.member1.PublicKey},
|
||||||
|
expectedActions: EncryptionKeyActions{
|
||||||
|
CommunityKeyAction: EncryptionKeyAction{
|
||||||
|
ActionType: EncryptionKeyNone,
|
||||||
|
Members: map[string]*protobuf.CommunityMember{},
|
||||||
|
},
|
||||||
|
ChannelKeysActions: map[string]EncryptionKeyAction{
|
||||||
|
channelID: EncryptionKeyAction{
|
||||||
|
ActionType: EncryptionKeyRekey,
|
||||||
|
Members: map[string]*protobuf.CommunityMember{
|
||||||
|
s.member1Key: &protobuf.CommunityMember{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "change control node in token-gated community with token-gated channel",
|
||||||
|
permissions: []*protobuf.CommunityTokenPermission{
|
||||||
|
&protobuf.CommunityTokenPermission{
|
||||||
|
Id: "some-id-1",
|
||||||
|
Type: protobuf.CommunityTokenPermission_BECOME_MEMBER,
|
||||||
|
TokenCriteria: make([]*protobuf.TokenCriteria, 0),
|
||||||
|
ChatIds: []string{},
|
||||||
|
},
|
||||||
|
&protobuf.CommunityTokenPermission{
|
||||||
|
Id: "some-id-2",
|
||||||
|
Type: protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL,
|
||||||
|
TokenCriteria: make([]*protobuf.TokenCriteria, 0),
|
||||||
|
ChatIds: []string{chatID},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
members: []*ecdsa.PublicKey{&s.member1.PublicKey, &s.member2.PublicKey},
|
||||||
|
channelMembers: []*ecdsa.PublicKey{&s.member1.PublicKey},
|
||||||
|
expectedActions: EncryptionKeyActions{
|
||||||
|
CommunityKeyAction: EncryptionKeyAction{
|
||||||
|
ActionType: EncryptionKeyRekey,
|
||||||
|
Members: map[string]*protobuf.CommunityMember{
|
||||||
|
s.member1Key: &protobuf.CommunityMember{},
|
||||||
|
s.member2Key: &protobuf.CommunityMember{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ChannelKeysActions: map[string]EncryptionKeyAction{
|
||||||
|
channelID: EncryptionKeyAction{
|
||||||
|
ActionType: EncryptionKeyRekey,
|
||||||
|
Members: map[string]*protobuf.CommunityMember{
|
||||||
|
s.member1Key: &protobuf.CommunityMember{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
s.Run(tc.name, func() {
|
||||||
|
origin, err := createTestCommunity(s.identity)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
_, err = origin.CreateChat(channelID, &protobuf.CommunityChat{
|
||||||
|
Members: map[string]*protobuf.CommunityMember{},
|
||||||
|
Permissions: &protobuf.CommunityPermissions{Access: protobuf.CommunityPermissions_NO_MEMBERSHIP},
|
||||||
|
Identity: &protobuf.ChatIdentity{},
|
||||||
|
})
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
for _, permission := range tc.permissions {
|
||||||
|
_, err := origin.UpsertTokenPermission(permission)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
}
|
||||||
|
for _, member := range tc.members {
|
||||||
|
_, err := origin.AddMember(member, []protobuf.CommunityMember_Roles{})
|
||||||
|
s.Require().NoError(err)
|
||||||
|
}
|
||||||
|
for _, member := range tc.channelMembers {
|
||||||
|
_, err = origin.AddMemberToChat(channelID, member, []protobuf.CommunityMember_Roles{})
|
||||||
|
s.Require().NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// change control node to arbitrary member
|
||||||
|
modified := origin.CreateDeepCopy()
|
||||||
|
modified.setControlNode(&s.member1.PublicKey)
|
||||||
|
|
||||||
|
actions := EvaluateCommunityEncryptionKeyActions(origin, modified)
|
||||||
|
s.Require().True(reflect.DeepEqual(tc.expectedActions, *actions))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user