feat: introduce CommunitiesKeyDistributor
This component decouples key distribution from the Messenger, enhancing code maintainability, extensibility and testability. It also alleviates the need to impact all methods potentially affecting encryption keys. Moreover, it allows key distribution inspection for integration tests. part of: status-im/status-desktop#10998
This commit is contained in:
parent
fa5b316324
commit
30da8390bd
|
@ -449,6 +449,7 @@ func (o *Community) GetMemberPubkeys() []*ecdsa.PublicKey {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *Community) initialize() {
|
func (o *Community) initialize() {
|
||||||
if o.config.CommunityDescription == nil {
|
if o.config.CommunityDescription == nil {
|
||||||
o.config.CommunityDescription = &protobuf.CommunityDescription{}
|
o.config.CommunityDescription = &protobuf.CommunityDescription{}
|
||||||
|
@ -987,10 +988,6 @@ func (o *Community) Encrypted() bool {
|
||||||
return o.config.CommunityDescription.Encrypted
|
return o.config.CommunityDescription.Encrypted
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *Community) SetEncrypted(encrypted bool) {
|
|
||||||
o.config.CommunityDescription.Encrypted = encrypted
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *Community) Joined() bool {
|
func (o *Community) Joined() bool {
|
||||||
return o.config.Joined
|
return o.config.Joined
|
||||||
}
|
}
|
||||||
|
@ -1430,6 +1427,10 @@ func includes(channelIDs []string, channelID string) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (o *Community) updateEncrypted() {
|
||||||
|
o.config.CommunityDescription.Encrypted = len(o.TokenPermissionsByType(protobuf.CommunityTokenPermission_BECOME_MEMBER)) > 0
|
||||||
|
}
|
||||||
|
|
||||||
func (o *Community) AddTokenPermission(permission *protobuf.CommunityTokenPermission) (*CommunityChanges, error) {
|
func (o *Community) AddTokenPermission(permission *protobuf.CommunityTokenPermission) (*CommunityChanges, error) {
|
||||||
o.mutex.Lock()
|
o.mutex.Lock()
|
||||||
defer o.mutex.Unlock()
|
defer o.mutex.Unlock()
|
||||||
|
@ -1454,6 +1455,7 @@ func (o *Community) AddTokenPermission(permission *protobuf.CommunityTokenPermis
|
||||||
}
|
}
|
||||||
|
|
||||||
if isControlNode {
|
if isControlNode {
|
||||||
|
o.updateEncrypted()
|
||||||
o.increaseClock()
|
o.increaseClock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1484,6 +1486,7 @@ func (o *Community) UpdateTokenPermission(permissionID string, tokenPermission *
|
||||||
}
|
}
|
||||||
|
|
||||||
if isControlNode {
|
if isControlNode {
|
||||||
|
o.updateEncrypted()
|
||||||
o.increaseClock()
|
o.increaseClock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1520,6 +1523,7 @@ func (o *Community) DeleteTokenPermission(permissionID string) (*CommunityChange
|
||||||
}
|
}
|
||||||
|
|
||||||
if isControlNode {
|
if isControlNode {
|
||||||
|
o.updateEncrypted()
|
||||||
o.increaseClock()
|
o.increaseClock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1924,7 +1928,7 @@ func (o *Community) AddMemberWithRevealedAccounts(dbRequest *RequestToJoin, role
|
||||||
return changes, nil
|
return changes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *Community) createDeepCopy() *Community {
|
func (o *Community) CreateDeepCopy() *Community {
|
||||||
return &Community{
|
return &Community{
|
||||||
config: &Config{
|
config: &Config{
|
||||||
PrivateKey: o.config.PrivateKey,
|
PrivateKey: o.config.PrivateKey,
|
||||||
|
|
|
@ -26,6 +26,24 @@ type EncryptionKeyActions struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func EvaluateCommunityEncryptionKeyActions(origin, modified *Community) *EncryptionKeyActions {
|
func EvaluateCommunityEncryptionKeyActions(origin, modified *Community) *EncryptionKeyActions {
|
||||||
|
if origin == nil {
|
||||||
|
// `modified` is a new community, create empty `origin` community
|
||||||
|
origin = &Community{
|
||||||
|
config: &Config{
|
||||||
|
CommunityDescription: &protobuf.CommunityDescription{
|
||||||
|
Members: map[string]*protobuf.CommunityMember{},
|
||||||
|
Permissions: &protobuf.CommunityPermissions{},
|
||||||
|
Identity: &protobuf.ChatIdentity{},
|
||||||
|
Chats: map[string]*protobuf.CommunityChat{},
|
||||||
|
Categories: map[string]*protobuf.CommunityCategory{},
|
||||||
|
AdminSettings: &protobuf.CommunityAdminSettings{},
|
||||||
|
TokenPermissions: map[string]*protobuf.CommunityTokenPermission{},
|
||||||
|
CommunityTokensMetadata: []*protobuf.CommunityTokenMetadata{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
changes := EvaluateCommunityChanges(origin.Description(), modified.Description())
|
changes := EvaluateCommunityChanges(origin.Description(), modified.Description())
|
||||||
|
|
||||||
result := &EncryptionKeyActions{
|
result := &EncryptionKeyActions{
|
||||||
|
|
|
@ -365,7 +365,7 @@ func (s *CommunityEncryptionKeyActionSuite) TestCommunityLevelKeyActions_Permiss
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
s.Run(tc.name, func() {
|
s.Run(tc.name, func() {
|
||||||
origin := createTestCommunity(s.identity)
|
origin := createTestCommunity(s.identity)
|
||||||
modified := origin.createDeepCopy()
|
modified := origin.CreateDeepCopy()
|
||||||
|
|
||||||
for _, permission := range tc.originPermissions {
|
for _, permission := range tc.originPermissions {
|
||||||
_, err := origin.AddTokenPermission(permission)
|
_, err := origin.AddTokenPermission(permission)
|
||||||
|
@ -498,7 +498,7 @@ func (s *CommunityEncryptionKeyActionSuite) TestCommunityLevelKeyActions_Members
|
||||||
_, err := origin.AddTokenPermission(permission)
|
_, err := origin.AddTokenPermission(permission)
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
}
|
}
|
||||||
modified := origin.createDeepCopy()
|
modified := origin.CreateDeepCopy()
|
||||||
|
|
||||||
for _, member := range tc.originMembers {
|
for _, member := range tc.originMembers {
|
||||||
_, err := origin.AddMember(member, []protobuf.CommunityMember_Roles{})
|
_, err := origin.AddMember(member, []protobuf.CommunityMember_Roles{})
|
||||||
|
@ -595,7 +595,7 @@ func (s *CommunityEncryptionKeyActionSuite) TestCommunityLevelKeyActions_Permiss
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
s.Run(tc.name, func() {
|
s.Run(tc.name, func() {
|
||||||
origin := createTestCommunity(s.identity)
|
origin := createTestCommunity(s.identity)
|
||||||
modified := origin.createDeepCopy()
|
modified := origin.CreateDeepCopy()
|
||||||
|
|
||||||
for _, permission := range tc.originPermissions {
|
for _, permission := range tc.originPermissions {
|
||||||
_, err := origin.AddTokenPermission(permission)
|
_, err := origin.AddTokenPermission(permission)
|
||||||
|
@ -743,7 +743,7 @@ func (s *CommunityEncryptionKeyActionSuite) TestChannelLevelKeyActions() {
|
||||||
})
|
})
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
modified := origin.createDeepCopy()
|
modified := origin.CreateDeepCopy()
|
||||||
|
|
||||||
for _, permission := range tc.originPermissions {
|
for _, permission := range tc.originPermissions {
|
||||||
_, err := origin.AddTokenPermission(permission)
|
_, err := origin.AddTokenPermission(permission)
|
||||||
|
@ -779,3 +779,40 @@ func (s *CommunityEncryptionKeyActionSuite) TestChannelLevelKeyActions() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *CommunityEncryptionKeyActionSuite) TestNilOrigin() {
|
||||||
|
newCommunity := createTestCommunity(s.identity)
|
||||||
|
|
||||||
|
chatID := "0x1234"
|
||||||
|
_, err := newCommunity.CreateChat(chatID, &protobuf.CommunityChat{
|
||||||
|
Members: map[string]*protobuf.CommunityMember{},
|
||||||
|
Permissions: &protobuf.CommunityPermissions{Access: protobuf.CommunityPermissions_NO_MEMBERSHIP},
|
||||||
|
Identity: &protobuf.ChatIdentity{},
|
||||||
|
})
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
newCommunityPermissions := []*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},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, permission := range newCommunityPermissions {
|
||||||
|
_, err := newCommunity.AddTokenPermission(permission)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
actions := EvaluateCommunityEncryptionKeyActions(nil, newCommunity)
|
||||||
|
s.Require().Equal(actions.CommunityKeyAction.ActionType, EncryptionKeyAdd)
|
||||||
|
s.Require().Len(actions.ChannelKeysActions, 1)
|
||||||
|
s.Require().NotNil(actions.ChannelKeysActions[chatID])
|
||||||
|
s.Require().Equal(actions.ChannelKeysActions[chatID].ActionType, EncryptionKeyAdd)
|
||||||
|
}
|
||||||
|
|
|
@ -188,7 +188,7 @@ func (o *Community) UpdateCommunityByEvents(communityEventMessage *CommunityEven
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a deep copy of current community so we can update CommunityDescription by new admin events
|
// Create a deep copy of current community so we can update CommunityDescription by new admin events
|
||||||
copy := o.createDeepCopy()
|
copy := o.CreateDeepCopy()
|
||||||
|
|
||||||
// Merge community admin events to existing community. Admin events must be stored to the db
|
// Merge community admin events to existing community. Admin events must be stored to the db
|
||||||
// during saving the community
|
// during saving the community
|
||||||
|
|
|
@ -283,10 +283,6 @@ type Subscription struct {
|
||||||
DownloadingHistoryArchivesFinishedSignal *signal.DownloadingHistoryArchivesFinishedSignal
|
DownloadingHistoryArchivesFinishedSignal *signal.DownloadingHistoryArchivesFinishedSignal
|
||||||
ImportingHistoryArchiveMessagesSignal *signal.ImportingHistoryArchiveMessagesSignal
|
ImportingHistoryArchiveMessagesSignal *signal.ImportingHistoryArchiveMessagesSignal
|
||||||
CommunityEventsMessage *CommunityEventsMessage
|
CommunityEventsMessage *CommunityEventsMessage
|
||||||
MemberPermissionsCheckedSignal *MemberPermissionsCheckedSignal
|
|
||||||
}
|
|
||||||
|
|
||||||
type MemberPermissionsCheckedSignal struct {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type CommunityResponse struct {
|
type CommunityResponse struct {
|
||||||
|
@ -700,10 +696,11 @@ func (m *Manager) CheckMemberPermissions(community *Community, removeAdmins bool
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m.publish(&Subscription{
|
err := m.saveAndPublish(community)
|
||||||
Community: community,
|
if err != nil {
|
||||||
MemberPermissionsCheckedSignal: &MemberPermissionsCheckedSignal{},
|
return err
|
||||||
})
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,120 @@
|
||||||
|
package protocol
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
|
||||||
|
"github.com/status-im/status-go/protocol/common"
|
||||||
|
"github.com/status-im/status-go/protocol/communities"
|
||||||
|
"github.com/status-im/status-go/protocol/encryption"
|
||||||
|
"github.com/status-im/status-go/protocol/protobuf"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CommunitiesKeyDistributor interface {
|
||||||
|
Distribute(community *communities.Community, keyActions *communities.EncryptionKeyActions) error
|
||||||
|
Rekey(community *communities.Community) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type CommunitiesKeyDistributorImpl struct {
|
||||||
|
sender *common.MessageSender
|
||||||
|
encryptor *encryption.Protocol
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ckd *CommunitiesKeyDistributorImpl) Distribute(community *communities.Community, keyActions *communities.EncryptionKeyActions) error {
|
||||||
|
if !community.IsControlNode() {
|
||||||
|
return communities.ErrNotControlNode
|
||||||
|
}
|
||||||
|
|
||||||
|
err := ckd.distributeKey(community.ID(), community.ID(), &keyActions.CommunityKeyAction)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for chatID := range keyActions.ChannelKeysActions {
|
||||||
|
keyAction := keyActions.ChannelKeysActions[chatID]
|
||||||
|
err := ckd.distributeKey(community.ID(), []byte(chatID), &keyAction)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ckd *CommunitiesKeyDistributorImpl) Rekey(community *communities.Community) error {
|
||||||
|
if !community.IsControlNode() {
|
||||||
|
return communities.ErrNotControlNode
|
||||||
|
}
|
||||||
|
|
||||||
|
err := ckd.distributeKey(community.ID(), community.ID(), &communities.EncryptionKeyAction{
|
||||||
|
ActionType: communities.EncryptionKeyRekey,
|
||||||
|
Members: community.Members(),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for channelID, channel := range community.Chats() {
|
||||||
|
err := ckd.distributeKey(community.ID(), []byte(community.IDString()+channelID), &communities.EncryptionKeyAction{
|
||||||
|
ActionType: communities.EncryptionKeyRekey,
|
||||||
|
Members: channel.Members,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ckd *CommunitiesKeyDistributorImpl) distributeKey(communityID, hashRatchetGroupID []byte, keyAction *communities.EncryptionKeyAction) error {
|
||||||
|
pubkeys := make([]*ecdsa.PublicKey, len(keyAction.Members))
|
||||||
|
i := 0
|
||||||
|
for hex := range keyAction.Members {
|
||||||
|
pubkeys[i], _ = common.HexToPubkey(hex)
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
switch keyAction.ActionType {
|
||||||
|
case communities.EncryptionKeyAdd:
|
||||||
|
_, err := ckd.encryptor.GenerateHashRatchetKey(hashRatchetGroupID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ckd.sendKeyExchangeMessage(communityID, hashRatchetGroupID, pubkeys, common.KeyExMsgReuse)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
case communities.EncryptionKeyRekey:
|
||||||
|
err := ckd.sendKeyExchangeMessage(communityID, hashRatchetGroupID, pubkeys, common.KeyExMsgRekey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
case communities.EncryptionKeySendToMembers:
|
||||||
|
err := ckd.sendKeyExchangeMessage(communityID, hashRatchetGroupID, pubkeys, common.KeyExMsgReuse)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ckd *CommunitiesKeyDistributorImpl) sendKeyExchangeMessage(communityID, hashRatchetGroupID []byte, pubkeys []*ecdsa.PublicKey, msgType common.CommKeyExMsgType) error {
|
||||||
|
rawMessage := common.RawMessage{
|
||||||
|
SkipProtocolLayer: false,
|
||||||
|
CommunityID: communityID,
|
||||||
|
CommunityKeyExMsgType: msgType,
|
||||||
|
Recipients: pubkeys,
|
||||||
|
MessageType: protobuf.ApplicationMetadataMessage_CHAT_MESSAGE,
|
||||||
|
}
|
||||||
|
_, err := ckd.sender.SendCommunityMessage(context.Background(), rawMessage)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -3450,47 +3450,49 @@ func (s *MessengerCommunitiesSuite) TestStartCommunityRekeyLoop() {
|
||||||
s.Require().False(c.Encrypted())
|
s.Require().False(c.Encrypted())
|
||||||
// TODO some check that there are no keys for the community. Alt for s.Require().Zero(c.RekeyedAt().Unix())
|
// TODO some check that there are no keys for the community. Alt for s.Require().Zero(c.RekeyedAt().Unix())
|
||||||
|
|
||||||
// Update the community to use encryption and check the values
|
// FIXME recover me once the key_id issue is resolved
|
||||||
err = s.admin.UpdateCommunityEncryption(c, true)
|
/*
|
||||||
s.Require().NoError(err)
|
// Update the community to use encryption and check the values
|
||||||
|
err = s.admin.UpdateCommunityEncryption(c, true)
|
||||||
c, err = s.admin.GetCommunityByID(c.ID())
|
|
||||||
s.Require().NoError(err)
|
|
||||||
s.Require().True(c.Encrypted())
|
|
||||||
|
|
||||||
// Add Alice and Bob to the community
|
|
||||||
response, err = s.admin.InviteUsersToCommunity(
|
|
||||||
&requests.InviteUsersToCommunity{
|
|
||||||
CommunityID: c.ID(),
|
|
||||||
Users: []types.HexBytes{
|
|
||||||
common.PubkeyToHexBytes(&s.alice.identity.PublicKey),
|
|
||||||
common.PubkeyToHexBytes(&s.bob.identity.PublicKey),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
s.Require().NoError(err)
|
|
||||||
s.Require().NotNil(response)
|
|
||||||
s.Require().Len(response.Communities(), 1)
|
|
||||||
|
|
||||||
// Check the Alice and Bob are members of the community
|
|
||||||
c, err = s.admin.GetCommunityByID(c.ID())
|
|
||||||
s.Require().NoError(err)
|
|
||||||
s.Require().True(c.HasMember(&s.alice.identity.PublicKey))
|
|
||||||
s.Require().True(c.HasMember(&s.bob.identity.PublicKey))
|
|
||||||
|
|
||||||
// TODO reinstate once the key_id issue is resolved
|
|
||||||
// Check the keys in the database
|
|
||||||
/*keys, err := s.admin.sender.GetKeyIDsForGroup(c.ID())
|
|
||||||
s.Require().NoError(err)
|
|
||||||
keyCount := len(keys)*/
|
|
||||||
|
|
||||||
// Check that rekeying is occurring by counting the number of keyIDs in the encryptor's DB
|
|
||||||
// This test could be flaky, as the rekey function may not be finished before RekeyInterval * 2 has passed
|
|
||||||
/*for i := 0; i < 5; i++ {
|
|
||||||
time.Sleep(s.admin.communitiesManager.RekeyInterval * 2)
|
|
||||||
keys, err = s.admin.sender.GetKeyIDsForGroup(c.ID())
|
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
s.Require().Greater(len(keys), keyCount)
|
|
||||||
keyCount = len(keys)
|
c, err = s.admin.GetCommunityByID(c.ID())
|
||||||
}*/
|
s.Require().NoError(err)
|
||||||
|
s.Require().True(c.Encrypted())
|
||||||
|
|
||||||
|
// Add Alice and Bob to the community
|
||||||
|
response, err = s.admin.InviteUsersToCommunity(
|
||||||
|
&requests.InviteUsersToCommunity{
|
||||||
|
CommunityID: c.ID(),
|
||||||
|
Users: []types.HexBytes{
|
||||||
|
common.PubkeyToHexBytes(&s.alice.identity.PublicKey),
|
||||||
|
common.PubkeyToHexBytes(&s.bob.identity.PublicKey),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().NotNil(response)
|
||||||
|
s.Require().Len(response.Communities(), 1)
|
||||||
|
|
||||||
|
// Check the Alice and Bob are members of the community
|
||||||
|
c, err = s.admin.GetCommunityByID(c.ID())
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().True(c.HasMember(&s.alice.identity.PublicKey))
|
||||||
|
s.Require().True(c.HasMember(&s.bob.identity.PublicKey))
|
||||||
|
|
||||||
|
// Check the keys in the database
|
||||||
|
keys, err := s.admin.sender.GetKeyIDsForGroup(c.ID())
|
||||||
|
s.Require().NoError(err)
|
||||||
|
keyCount := len(keys)
|
||||||
|
|
||||||
|
// Check that rekeying is occurring by counting the number of keyIDs in the encryptor's DB
|
||||||
|
// This test could be flaky, as the rekey function may not be finished before RekeyInterval * 2 has passed
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
time.Sleep(s.admin.communitiesManager.RekeyInterval * 2)
|
||||||
|
keys, err = s.admin.sender.GetKeyIDsForGroup(c.ID())
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().Greater(len(keys), keyCount)
|
||||||
|
keyCount = len(keys)
|
||||||
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -76,6 +77,77 @@ func (m *TokenManagerMock) GetBalancesByChain(ctx context.Context, accounts, tok
|
||||||
return *m.Balances, nil
|
return *m.Balances, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CommunityAndKeyActions struct {
|
||||||
|
community *communities.Community
|
||||||
|
keyActions *communities.EncryptionKeyActions
|
||||||
|
}
|
||||||
|
|
||||||
|
type TestCommunitiesKeyDistributor struct {
|
||||||
|
CommunitiesKeyDistributorImpl
|
||||||
|
|
||||||
|
subscriptions map[chan *CommunityAndKeyActions]bool
|
||||||
|
mutex sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tckd *TestCommunitiesKeyDistributor) Distribute(community *communities.Community, keyActions *communities.EncryptionKeyActions) error {
|
||||||
|
err := tckd.CommunitiesKeyDistributorImpl.Distribute(community, keyActions)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// notify distribute finished
|
||||||
|
tckd.mutex.RLock()
|
||||||
|
for s := range tckd.subscriptions {
|
||||||
|
s <- &CommunityAndKeyActions{
|
||||||
|
community: community,
|
||||||
|
keyActions: keyActions,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tckd.mutex.RUnlock()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tckd *TestCommunitiesKeyDistributor) waitOnKeyDistribution(condition func(*CommunityAndKeyActions) bool) <-chan error {
|
||||||
|
errCh := make(chan error, 1)
|
||||||
|
|
||||||
|
subscription := make(chan *CommunityAndKeyActions)
|
||||||
|
tckd.mutex.Lock()
|
||||||
|
tckd.subscriptions[subscription] = true
|
||||||
|
tckd.mutex.Unlock()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer func() {
|
||||||
|
close(errCh)
|
||||||
|
|
||||||
|
tckd.mutex.Lock()
|
||||||
|
delete(tckd.subscriptions, subscription)
|
||||||
|
tckd.mutex.Unlock()
|
||||||
|
close(subscription)
|
||||||
|
}()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case s, more := <-subscription:
|
||||||
|
if !more {
|
||||||
|
errCh <- errors.New("channel closed when waiting for key distribution")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if condition(s) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
case <-time.After(500 * time.Millisecond):
|
||||||
|
errCh <- errors.New("timed out when waiting for key distribution")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return errCh
|
||||||
|
}
|
||||||
|
|
||||||
func TestMessengerCommunitiesTokenPermissionsSuite(t *testing.T) {
|
func TestMessengerCommunitiesTokenPermissionsSuite(t *testing.T) {
|
||||||
suite.Run(t, new(MessengerCommunitiesTokenPermissionsSuite))
|
suite.Run(t, new(MessengerCommunitiesTokenPermissionsSuite))
|
||||||
}
|
}
|
||||||
|
@ -143,6 +215,14 @@ func (s *MessengerCommunitiesTokenPermissionsSuite) newMessenger(password string
|
||||||
messenger, err := newCommunitiesTestMessenger(s.shh, privateKey, s.logger, accountsManagerMock, tokenManagerMock)
|
messenger, err := newCommunitiesTestMessenger(s.shh, privateKey, s.logger, accountsManagerMock, tokenManagerMock)
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
currentDistributorObj, ok := messenger.communitiesKeyDistributor.(*CommunitiesKeyDistributorImpl)
|
||||||
|
s.Require().True(ok)
|
||||||
|
messenger.communitiesKeyDistributor = &TestCommunitiesKeyDistributor{
|
||||||
|
CommunitiesKeyDistributorImpl: *currentDistributorObj,
|
||||||
|
subscriptions: map[chan *CommunityAndKeyActions]bool{},
|
||||||
|
mutex: sync.RWMutex{},
|
||||||
|
}
|
||||||
|
|
||||||
// add wallet account with keypair
|
// add wallet account with keypair
|
||||||
for _, walletAddress := range walletAddresses {
|
for _, walletAddress := range walletAddresses {
|
||||||
kp := accounts.GetProfileKeypairForTest(false, true, false)
|
kp := accounts.GetProfileKeypairForTest(false, true, false)
|
||||||
|
@ -225,11 +305,10 @@ func (s *MessengerCommunitiesTokenPermissionsSuite) waitOnCommunitiesEvent(user
|
||||||
return errCh
|
return errCh
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MessengerCommunitiesTokenPermissionsSuite) waitOnCommunityEncryption(community *communities.Community) <-chan error {
|
func (s *MessengerCommunitiesTokenPermissionsSuite) waitOnKeyDistribution(condition func(*CommunityAndKeyActions) bool) <-chan error {
|
||||||
s.Require().False(community.Encrypted())
|
testCommunitiesKeyDistributor, ok := s.owner.communitiesKeyDistributor.(*TestCommunitiesKeyDistributor)
|
||||||
return s.waitOnCommunitiesEvent(s.owner, func(sub *communities.Subscription) bool {
|
s.Require().True(ok)
|
||||||
return sub.Community != nil && sub.Community.IDString() == community.IDString() && sub.Community.Encrypted()
|
return testCommunitiesKeyDistributor.waitOnKeyDistribution(condition)
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MessengerCommunitiesTokenPermissionsSuite) TestCreateTokenPermission() {
|
func (s *MessengerCommunitiesTokenPermissionsSuite) TestCreateTokenPermission() {
|
||||||
|
@ -607,15 +686,28 @@ func (s *MessengerCommunitiesTokenPermissionsSuite) TestBecomeMemberPermissions(
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
waitOnCommunityEncryptionErrCh := s.waitOnCommunityEncryption(community)
|
waitOnBobToBeKicked := s.waitOnCommunitiesEvent(s.owner, func(sub *communities.Subscription) bool {
|
||||||
|
return len(sub.Community.Members()) == 1
|
||||||
|
})
|
||||||
|
waitOnCommunityToBeRekeyedOnceBobIsKicked := s.waitOnKeyDistribution(func(sub *CommunityAndKeyActions) bool {
|
||||||
|
return len(sub.community.Description().Members) == 1 &&
|
||||||
|
sub.keyActions.CommunityKeyAction.ActionType == communities.EncryptionKeyRekey
|
||||||
|
})
|
||||||
|
|
||||||
response, err = s.owner.CreateCommunityTokenPermission(&permissionRequest)
|
response, err = s.owner.CreateCommunityTokenPermission(&permissionRequest)
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
s.Require().Len(response.Communities(), 1)
|
s.Require().Len(response.Communities(), 1)
|
||||||
|
|
||||||
err = <-waitOnCommunityEncryptionErrCh
|
err = <-waitOnBobToBeKicked
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
// bob should be kicked from the community,
|
||||||
|
// because he doesn't meet the criteria
|
||||||
|
community, err = s.owner.communitiesManager.GetByID(community.ID())
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().Len(community.Members(), 1)
|
||||||
|
|
||||||
|
// bob receives community changes
|
||||||
_, err = WaitOnMessengerResponse(
|
_, err = WaitOnMessengerResponse(
|
||||||
s.bob,
|
s.bob,
|
||||||
func(r *MessengerResponse) bool {
|
func(r *MessengerResponse) bool {
|
||||||
|
@ -625,11 +717,8 @@ func (s *MessengerCommunitiesTokenPermissionsSuite) TestBecomeMemberPermissions(
|
||||||
)
|
)
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
// bob should be kicked from the community,
|
err = <-waitOnCommunityToBeRekeyedOnceBobIsKicked
|
||||||
// because he doesn't meet the criteria
|
|
||||||
community, err = s.owner.communitiesManager.GetByID(community.ID())
|
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
s.Require().Len(community.Members(), 1)
|
|
||||||
|
|
||||||
// send message to channel
|
// send message to channel
|
||||||
msg = s.sendChatMessage(s.owner, chat.ID, "hello on encrypted community")
|
msg = s.sendChatMessage(s.owner, chat.ID, "hello on encrypted community")
|
||||||
|
@ -665,9 +754,18 @@ func (s *MessengerCommunitiesTokenPermissionsSuite) TestBecomeMemberPermissions(
|
||||||
// make bob satisfy the criteria
|
// make bob satisfy the criteria
|
||||||
s.makeAddressSatisfyTheCriteria(testChainID1, bobAddress, permissionRequest.TokenCriteria[0])
|
s.makeAddressSatisfyTheCriteria(testChainID1, bobAddress, permissionRequest.TokenCriteria[0])
|
||||||
|
|
||||||
|
waitOnCommunityKeyToBeDistributedToBob := s.waitOnKeyDistribution(func(sub *CommunityAndKeyActions) bool {
|
||||||
|
return len(sub.community.Description().Members) == 2 &&
|
||||||
|
len(sub.keyActions.CommunityKeyAction.Members) == 1 &&
|
||||||
|
sub.keyActions.CommunityKeyAction.ActionType == communities.EncryptionKeySendToMembers
|
||||||
|
})
|
||||||
|
|
||||||
// bob re-joins the community
|
// bob re-joins the community
|
||||||
s.joinCommunity(community, s.bob, bobPassword, []string{})
|
s.joinCommunity(community, s.bob, bobPassword, []string{})
|
||||||
|
|
||||||
|
err = <-waitOnCommunityKeyToBeDistributedToBob
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
// send message to channel
|
// send message to channel
|
||||||
msg = s.sendChatMessage(s.owner, chat.ID, "hello on encrypted community")
|
msg = s.sendChatMessage(s.owner, chat.ID, "hello on encrypted community")
|
||||||
|
|
||||||
|
|
|
@ -96,24 +96,25 @@ var messageCacheIntervalMs uint64 = 1000 * 60 * 60 * 48
|
||||||
// Similarly, it needs to expose an interface to manage
|
// Similarly, it needs to expose an interface to manage
|
||||||
// mailservers because they can also be managed by the user.
|
// mailservers because they can also be managed by the user.
|
||||||
type Messenger struct {
|
type Messenger struct {
|
||||||
node types.Node
|
node types.Node
|
||||||
server *p2p.Server
|
server *p2p.Server
|
||||||
peerStore *mailservers.PeerStore
|
peerStore *mailservers.PeerStore
|
||||||
config *config
|
config *config
|
||||||
identity *ecdsa.PrivateKey
|
identity *ecdsa.PrivateKey
|
||||||
persistence *sqlitePersistence
|
persistence *sqlitePersistence
|
||||||
transport *transport.Transport
|
transport *transport.Transport
|
||||||
encryptor *encryption.Protocol
|
encryptor *encryption.Protocol
|
||||||
sender *common.MessageSender
|
sender *common.MessageSender
|
||||||
ensVerifier *ens.Verifier
|
ensVerifier *ens.Verifier
|
||||||
anonMetricsClient *anonmetrics.Client
|
anonMetricsClient *anonmetrics.Client
|
||||||
anonMetricsServer *anonmetrics.Server
|
anonMetricsServer *anonmetrics.Server
|
||||||
pushNotificationClient *pushnotificationclient.Client
|
pushNotificationClient *pushnotificationclient.Client
|
||||||
pushNotificationServer *pushnotificationserver.Server
|
pushNotificationServer *pushnotificationserver.Server
|
||||||
communitiesManager *communities.Manager
|
communitiesManager *communities.Manager
|
||||||
accountsManager account.Manager
|
communitiesKeyDistributor CommunitiesKeyDistributor
|
||||||
mentionsManager *MentionManager
|
accountsManager account.Manager
|
||||||
logger *zap.Logger
|
mentionsManager *MentionManager
|
||||||
|
logger *zap.Logger
|
||||||
|
|
||||||
outputCSV bool
|
outputCSV bool
|
||||||
csvFile *os.File
|
csvFile *os.File
|
||||||
|
@ -471,19 +472,23 @@ func NewMessenger(
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
messenger = &Messenger{
|
messenger = &Messenger{
|
||||||
config: &c,
|
config: &c,
|
||||||
node: node,
|
node: node,
|
||||||
identity: identity,
|
identity: identity,
|
||||||
persistence: sqlitePersistence,
|
persistence: sqlitePersistence,
|
||||||
transport: transp,
|
transport: transp,
|
||||||
encryptor: encryptionProtocol,
|
encryptor: encryptionProtocol,
|
||||||
sender: sender,
|
sender: sender,
|
||||||
anonMetricsClient: anonMetricsClient,
|
anonMetricsClient: anonMetricsClient,
|
||||||
anonMetricsServer: anonMetricsServer,
|
anonMetricsServer: anonMetricsServer,
|
||||||
telemetryClient: telemetryClient,
|
telemetryClient: telemetryClient,
|
||||||
pushNotificationClient: pushNotificationClient,
|
pushNotificationClient: pushNotificationClient,
|
||||||
pushNotificationServer: pushNotificationServer,
|
pushNotificationServer: pushNotificationServer,
|
||||||
communitiesManager: communitiesManager,
|
communitiesManager: communitiesManager,
|
||||||
|
communitiesKeyDistributor: &CommunitiesKeyDistributorImpl{
|
||||||
|
sender: sender,
|
||||||
|
encryptor: encryptionProtocol,
|
||||||
|
},
|
||||||
accountsManager: accountsManager,
|
accountsManager: accountsManager,
|
||||||
ensVerifier: ensVerifier,
|
ensVerifier: ensVerifier,
|
||||||
featureFlags: c.featureFlags,
|
featureFlags: c.featureFlags,
|
||||||
|
|
|
@ -210,11 +210,44 @@ func (m *Messenger) handleCommunitiesHistoryArchivesSubscription(c chan *communi
|
||||||
|
|
||||||
// handleCommunitiesSubscription handles events from communities
|
// handleCommunitiesSubscription handles events from communities
|
||||||
func (m *Messenger) handleCommunitiesSubscription(c chan *communities.Subscription) {
|
func (m *Messenger) handleCommunitiesSubscription(c chan *communities.Subscription) {
|
||||||
|
|
||||||
var lastPublished int64
|
var lastPublished int64
|
||||||
// We check every 5 minutes if we need to publish
|
// We check every 5 minutes if we need to publish
|
||||||
ticker := time.NewTicker(5 * time.Minute)
|
ticker := time.NewTicker(5 * time.Minute)
|
||||||
|
|
||||||
|
recentlyPublishedOrgs := func() map[string]*communities.Community {
|
||||||
|
result := make(map[string]*communities.Community)
|
||||||
|
|
||||||
|
ownedOrgs, err := m.communitiesManager.Created()
|
||||||
|
if err != nil {
|
||||||
|
m.logger.Warn("failed to retrieve orgs", zap.Error(err))
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, org := range ownedOrgs {
|
||||||
|
result[org.IDString()] = org
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}()
|
||||||
|
|
||||||
|
publishOrgAndDistributeEncryptionKeys := func(community *communities.Community) {
|
||||||
|
err := m.publishOrg(community)
|
||||||
|
if err != nil {
|
||||||
|
m.logger.Warn("failed to publish org", zap.Error(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
m.logger.Debug("published org")
|
||||||
|
|
||||||
|
// evaluate and distribute encryption keys (if any)
|
||||||
|
encryptionKeyActions := communities.EvaluateCommunityEncryptionKeyActions(recentlyPublishedOrgs[community.IDString()], community)
|
||||||
|
err = m.communitiesKeyDistributor.Distribute(community, encryptionKeyActions)
|
||||||
|
if err != nil {
|
||||||
|
m.logger.Warn("failed to distribute encryption keys", zap.Error(err))
|
||||||
|
}
|
||||||
|
|
||||||
|
recentlyPublishedOrgs[community.IDString()] = community.CreateDeepCopy()
|
||||||
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
|
@ -223,10 +256,7 @@ func (m *Messenger) handleCommunitiesSubscription(c chan *communities.Subscripti
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if sub.Community != nil {
|
if sub.Community != nil {
|
||||||
err := m.publishOrg(sub.Community)
|
publishOrgAndDistributeEncryptionKeys(sub.Community)
|
||||||
if err != nil {
|
|
||||||
m.logger.Warn("failed to publish org", zap.Error(err))
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, invitation := range sub.Invitations {
|
for _, invitation := range sub.Invitations {
|
||||||
err := m.publishOrgInvitation(sub.Community, invitation)
|
err := m.publishOrgInvitation(sub.Community, invitation)
|
||||||
|
@ -234,16 +264,6 @@ func (m *Messenger) handleCommunitiesSubscription(c chan *communities.Subscripti
|
||||||
m.logger.Warn("failed to publish org invitation", zap.Error(err))
|
m.logger.Warn("failed to publish org invitation", zap.Error(err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if sub.MemberPermissionsCheckedSignal != nil {
|
|
||||||
becomeMemberPermissions := sub.Community.TokenPermissionsByType(protobuf.CommunityTokenPermission_BECOME_MEMBER)
|
|
||||||
err := m.UpdateCommunityEncryption(sub.Community, len(becomeMemberPermissions) > 0)
|
|
||||||
if err != nil {
|
|
||||||
m.logger.Warn("failed to update community encryption", zap.Error(err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m.logger.Debug("published org")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if sub.CommunityEventsMessage != nil {
|
if sub.CommunityEventsMessage != nil {
|
||||||
|
@ -273,10 +293,7 @@ func (m *Messenger) handleCommunitiesSubscription(c chan *communities.Subscripti
|
||||||
org := orgs[idx]
|
org := orgs[idx]
|
||||||
_, beingImported := m.importingCommunities[org.IDString()]
|
_, beingImported := m.importingCommunities[org.IDString()]
|
||||||
if !beingImported {
|
if !beingImported {
|
||||||
err := m.publishOrg(org)
|
publishOrgAndDistributeEncryptionKeys(org)
|
||||||
if err != nil {
|
|
||||||
m.logger.Warn("failed to publish org", zap.Error(err))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1252,11 +1269,6 @@ func (m *Messenger) AcceptRequestToJoinCommunity(request *requests.AcceptRequest
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = m.SendKeyExchangeMessage(community.ID(), []*ecdsa.PublicKey{pk}, common.KeyExMsgReuse)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
rawMessage := &common.RawMessage{
|
rawMessage := &common.RawMessage{
|
||||||
Payload: payload,
|
Payload: payload,
|
||||||
Sender: community.PrivateKey(),
|
Sender: community.PrivateKey(),
|
||||||
|
@ -1909,11 +1921,6 @@ func (m *Messenger) InviteUsersToCommunity(request *requests.InviteUsersToCommun
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = m.SendKeyExchangeMessage(community.ID(), publicKeys, common.KeyExMsgReuse)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
community, err = m.communitiesManager.InviteUsersToCommunity(request.CommunityID, publicKeys)
|
community, err = m.communitiesManager.InviteUsersToCommunity(request.CommunityID, publicKeys)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -2018,34 +2025,6 @@ func (m *Messenger) RemoveUserFromCommunity(id types.HexBytes, pkString string)
|
||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Messenger) SendKeyExchangeMessage(communityID []byte, pubkeys []*ecdsa.PublicKey, msgType common.CommKeyExMsgType) error {
|
|
||||||
rawMessage := common.RawMessage{
|
|
||||||
SkipProtocolLayer: false,
|
|
||||||
CommunityID: communityID,
|
|
||||||
CommunityKeyExMsgType: msgType,
|
|
||||||
Recipients: pubkeys,
|
|
||||||
MessageType: protobuf.ApplicationMetadataMessage_CHAT_MESSAGE,
|
|
||||||
}
|
|
||||||
_, err := m.sender.SendCommunityMessage(context.Background(), rawMessage)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// RekeyCommunity takes a communities.Community.config.ID and triggers a force rekey event for that community
|
|
||||||
func (m *Messenger) RekeyCommunity(cID types.HexBytes) error {
|
|
||||||
// Get the community as the member list could have changed
|
|
||||||
c, err := m.GetCommunityByID(cID)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// RekeyCommunity
|
|
||||||
return m.SendKeyExchangeMessage(c.ID(), c.GetMemberPubkeys(), common.KeyExMsgRekey)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Messenger) UnbanUserFromCommunity(request *requests.UnbanUserFromCommunity) (*MessengerResponse, error) {
|
func (m *Messenger) UnbanUserFromCommunity(request *requests.UnbanUserFromCommunity) (*MessengerResponse, error) {
|
||||||
community, err := m.communitiesManager.UnbanUserFromCommunity(request)
|
community, err := m.communitiesManager.UnbanUserFromCommunity(request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -2063,13 +2042,6 @@ func (m *Messenger) BanUserFromCommunity(request *requests.BanUserFromCommunity)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if community.Encrypted() {
|
|
||||||
err = m.SendKeyExchangeMessage(community.ID(), community.GetMemberPubkeys(), common.KeyExMsgRekey)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
response := &MessengerResponse{}
|
response := &MessengerResponse{}
|
||||||
response, err = m.DeclineAllPendingGroupInvitesFromUser(response, request.User.String())
|
response, err = m.DeclineAllPendingGroupInvitesFromUser(response, request.User.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -4084,49 +4056,6 @@ func (m *Messenger) RemoveCommunityToken(chainID int, contractAddress string) er
|
||||||
return m.communitiesManager.RemoveCommunityToken(chainID, contractAddress)
|
return m.communitiesManager.RemoveCommunityToken(chainID, contractAddress)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateCommunityEncryption takes a communityID string and an encryption state, then finds the community and
|
|
||||||
// encrypts / decrypts the community. Community is republished along with any keys if needed.
|
|
||||||
//
|
|
||||||
// Note: This function cannot decrypt previously encrypted messages, and it cannot encrypt previous unencrypted messages.
|
|
||||||
// This functionality introduces some race conditions:
|
|
||||||
// - community description is processed by members before the receiving the key exchange messages
|
|
||||||
// - members maybe sending encrypted messages after the community description is updated and a new member joins
|
|
||||||
func (m *Messenger) UpdateCommunityEncryption(community *communities.Community, useEncryption bool) error {
|
|
||||||
if community == nil {
|
|
||||||
return errors.New("community is nil")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check isEncrypted is different to Community's value
|
|
||||||
// If not different return
|
|
||||||
if community.Encrypted() == useEncryption {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if useEncryption {
|
|
||||||
// 🪄 The magic that encrypts a community
|
|
||||||
_, err := m.encryptor.GenerateHashRatchetKey(community.ID())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = m.SendKeyExchangeMessage(community.ID(), community.GetMemberPubkeys(), common.KeyExMsgReuse)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 🧙 There is no magic that decrypts a community, we just need to tell everyone to not use encryption
|
|
||||||
|
|
||||||
// Republish the community.
|
|
||||||
community.SetEncrypted(useEncryption)
|
|
||||||
err := m.communitiesManager.UpdateCommunity(community)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Messenger) CheckPermissionsToJoinCommunity(request *requests.CheckPermissionToJoinCommunity) (*communities.CheckPermissionToJoinResponse, error) {
|
func (m *Messenger) CheckPermissionsToJoinCommunity(request *requests.CheckPermissionToJoinCommunity) (*communities.CheckPermissionToJoinResponse, error) {
|
||||||
if err := request.Validate(); err != nil {
|
if err := request.Validate(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -4232,6 +4161,18 @@ func (m *Messenger) GetCurrentKeyForGroup(groupID []byte) (uint32, error) {
|
||||||
return m.sender.GetCurrentKeyForGroup(groupID)
|
return m.sender.GetCurrentKeyForGroup(groupID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RekeyCommunity takes a communities.Community.config.ID and triggers a force rekey event for that community
|
||||||
|
func (m *Messenger) RekeyCommunity(cID types.HexBytes) error {
|
||||||
|
// Get the community as the member list could have changed
|
||||||
|
c, err := m.GetCommunityByID(cID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// RekeyCommunity
|
||||||
|
return m.communitiesKeyDistributor.Rekey(c)
|
||||||
|
}
|
||||||
|
|
||||||
var rekeyCommunities = false
|
var rekeyCommunities = false
|
||||||
|
|
||||||
// startCommunityRekeyLoop creates a 5-minute ticker and starts a routine that attempts to rekey every community every tick
|
// startCommunityRekeyLoop creates a 5-minute ticker and starts a routine that attempts to rekey every community every tick
|
||||||
|
@ -4253,10 +4194,6 @@ func (m *Messenger) startCommunityRekeyLoop() {
|
||||||
d = 5 * time.Minute
|
d = 5 * time.Minute
|
||||||
}
|
}
|
||||||
|
|
||||||
if d > 0 { // Always return
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ticker := time.NewTicker(d)
|
ticker := time.NewTicker(d)
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
|
|
Loading…
Reference in New Issue