Community encryption should automatically be enabled/disabled depending on community permissions (closed/open)
This commit is contained in:
parent
44ded4dd64
commit
57b2432290
|
@ -969,8 +969,8 @@ func (o *Community) Encrypted() bool {
|
||||||
return o.config.CommunityDescription.Encrypted
|
return o.config.CommunityDescription.Encrypted
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *Community) Encrypt() {
|
func (o *Community) SetEncrypted(encrypted bool) {
|
||||||
o.config.CommunityDescription.Encrypted = true
|
o.config.CommunityDescription.Encrypted = encrypted
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *Community) Joined() bool {
|
func (o *Community) Joined() bool {
|
||||||
|
@ -1468,6 +1468,7 @@ func (o *Community) AddTokenPermission(permission *protobuf.CommunityTokenPermis
|
||||||
}
|
}
|
||||||
|
|
||||||
o.config.CommunityDescription.TokenPermissions[permission.Id] = permission
|
o.config.CommunityDescription.TokenPermissions[permission.Id] = permission
|
||||||
|
|
||||||
o.increaseClock()
|
o.increaseClock()
|
||||||
changes := o.emptyCommunityChanges()
|
changes := o.emptyCommunityChanges()
|
||||||
|
|
||||||
|
|
|
@ -3334,3 +3334,17 @@ func (m *Manager) SetCommunityActiveMembersCount(communityID string, activeMembe
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateCommunity takes a Community persists it and republishes it.
|
||||||
|
// The clock is incremented meaning even a no change update will be republished by the admin, and parsed by the member.
|
||||||
|
func (m *Manager) UpdateCommunity(c *Community) error {
|
||||||
|
c.increaseClock()
|
||||||
|
|
||||||
|
err := m.persistence.SaveCommunity(c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
m.publish(&Subscription{Community: c})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -13,7 +13,6 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/golang/protobuf/proto"
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
@ -28,7 +27,6 @@ import (
|
||||||
"github.com/status-im/status-go/protocol/common"
|
"github.com/status-im/status-go/protocol/common"
|
||||||
"github.com/status-im/status-go/protocol/communities"
|
"github.com/status-im/status-go/protocol/communities"
|
||||||
"github.com/status-im/status-go/protocol/discord"
|
"github.com/status-im/status-go/protocol/discord"
|
||||||
"github.com/status-im/status-go/protocol/encryption"
|
|
||||||
"github.com/status-im/status-go/protocol/encryption/multidevice"
|
"github.com/status-im/status-go/protocol/encryption/multidevice"
|
||||||
"github.com/status-im/status-go/protocol/protobuf"
|
"github.com/status-im/status-go/protocol/protobuf"
|
||||||
"github.com/status-im/status-go/protocol/requests"
|
"github.com/status-im/status-go/protocol/requests"
|
||||||
|
@ -640,7 +638,6 @@ func (s *MessengerCommunitiesSuite) TestPostToCommunityChat() {
|
||||||
Name: "status",
|
Name: "status",
|
||||||
Color: "#ffffff",
|
Color: "#ffffff",
|
||||||
Description: "status community description",
|
Description: "status community description",
|
||||||
Encrypted: true,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create an community chat
|
// Create an community chat
|
||||||
|
@ -2578,7 +2575,6 @@ func (s *MessengerCommunitiesSuite) TestSyncCommunity() {
|
||||||
createCommunityReq := &requests.CreateCommunity{
|
createCommunityReq := &requests.CreateCommunity{
|
||||||
Membership: protobuf.CommunityPermissions_ON_REQUEST,
|
Membership: protobuf.CommunityPermissions_ON_REQUEST,
|
||||||
Name: "new community",
|
Name: "new community",
|
||||||
Encrypted: true,
|
|
||||||
Color: "#000000",
|
Color: "#000000",
|
||||||
Description: "new community description",
|
Description: "new community description",
|
||||||
}
|
}
|
||||||
|
@ -2593,15 +2589,6 @@ func (s *MessengerCommunitiesSuite) TestSyncCommunity() {
|
||||||
}
|
}
|
||||||
s.Require().NotNil(newCommunity)
|
s.Require().NotNil(newCommunity)
|
||||||
|
|
||||||
// Check HR keys are created
|
|
||||||
encodedKeys, err := s.alice.encryptor.GetAllHREncodedKeys(newCommunity.ID())
|
|
||||||
s.Require().NoError(err)
|
|
||||||
s.Require().NotNil(encodedKeys)
|
|
||||||
|
|
||||||
keys := &encryption.HRKeys{}
|
|
||||||
s.Require().NoError(proto.Unmarshal(encodedKeys, keys))
|
|
||||||
s.Require().Len(keys.Keys, 1)
|
|
||||||
|
|
||||||
// Check that Alice has 2 communities
|
// Check that Alice has 2 communities
|
||||||
cs, err := s.alice.communitiesManager.All()
|
cs, err := s.alice.communitiesManager.All()
|
||||||
s.Require().NoError(err, "communitiesManager.All")
|
s.Require().NoError(err, "communitiesManager.All")
|
||||||
|
@ -2629,15 +2616,6 @@ func (s *MessengerCommunitiesSuite) TestSyncCommunity() {
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
s.Len(tcs, 2, "There must be 2 communities")
|
s.Len(tcs, 2, "There must be 2 communities")
|
||||||
|
|
||||||
// Check HR keys are synced
|
|
||||||
encodedKeys, err = alicesOtherDevice.encryptor.GetAllHREncodedKeys(newCommunity.ID())
|
|
||||||
s.Require().NoError(err)
|
|
||||||
s.Require().NotNil(encodedKeys)
|
|
||||||
|
|
||||||
keys = &encryption.HRKeys{}
|
|
||||||
s.Require().NoError(proto.Unmarshal(encodedKeys, keys))
|
|
||||||
s.Require().Len(keys.Keys, 1)
|
|
||||||
|
|
||||||
s.logger.Debug("", zap.Any("tcs", tcs))
|
s.logger.Debug("", zap.Any("tcs", tcs))
|
||||||
|
|
||||||
// Get the new community from their db
|
// Get the new community from their db
|
||||||
|
|
|
@ -623,7 +623,7 @@ func (s *encryptor) getNextHashRatchetKeyID(groupID []byte) (uint32, error) {
|
||||||
return latestKeyID + 1, nil
|
return latestKeyID + 1, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generates and stores a hash ratchet key given a group ID
|
// GenerateHashRatchetKey Generates and stores a hash ratchet key given a group ID
|
||||||
func (s *encryptor) GenerateHashRatchetKey(groupID []byte) (uint32, error) {
|
func (s *encryptor) GenerateHashRatchetKey(groupID []byte) (uint32, error) {
|
||||||
|
|
||||||
// Randomly generate a hash ratchet key
|
// Randomly generate a hash ratchet key
|
||||||
|
|
|
@ -790,7 +790,7 @@ func (s *sqlitePersistence) GetHashRatchetKeyByID(groupID []byte, keyID uint32,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCurrentKeyIDForGroup retrieves a key ID for given group ID
|
// GetCurrentKeyForGroup retrieves a key ID for given group ID
|
||||||
// (with an assumption that key ids are shared in the group, and
|
// (with an assumption that key ids are shared in the group, and
|
||||||
// at any given time there is a single key used)
|
// at any given time there is a single key used)
|
||||||
func (s *sqlitePersistence) GetCurrentKeyForGroup(groupID []byte) (uint32, error) {
|
func (s *sqlitePersistence) GetCurrentKeyForGroup(groupID []byte) (uint32, error) {
|
||||||
|
@ -845,7 +845,7 @@ func (s *sqlitePersistence) GetKeyIDsForGroup(groupID []byte) ([]uint32, error)
|
||||||
return keyIDs, nil
|
return keyIDs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SaveHashRachetKeyHash saves a hash ratchet key cache data
|
// SaveHashRatchetKeyHash saves a hash ratchet key cache data
|
||||||
func (s *sqlitePersistence) SaveHashRatchetKeyHash(
|
func (s *sqlitePersistence) SaveHashRatchetKeyHash(
|
||||||
groupID []byte,
|
groupID []byte,
|
||||||
keyID uint32,
|
keyID uint32,
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
_errors "errors"
|
_errors "errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
@ -1238,15 +1239,6 @@ func (m *Messenger) CreateCommunity(request *requests.CreateCommunity, createDef
|
||||||
response.AddChat(chatResponse.Chats()[0])
|
response.AddChat(chatResponse.Chats()[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
if request.Encrypted {
|
|
||||||
// Init hash ratchet for community
|
|
||||||
_, err = m.encryptor.GenerateHashRatchetKey(community.ID())
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
response.AddCommunity(community)
|
response.AddCommunity(community)
|
||||||
response.AddCommunitySettings(&communitySettings)
|
response.AddCommunitySettings(&communitySettings)
|
||||||
err = m.syncCommunity(context.Background(), community, m.dispatchMessage)
|
err = m.syncCommunity(context.Background(), community, m.dispatchMessage)
|
||||||
|
@ -1271,8 +1263,16 @@ func (m *Messenger) CreateCommunityTokenPermission(request *requests.CreateCommu
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
response := &MessengerResponse{}
|
response, err := m.UpdateCommunityEncryption(community)
|
||||||
response.AddCommunity(community)
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if response == nil {
|
||||||
|
response = &MessengerResponse{}
|
||||||
|
response.AddCommunity(community)
|
||||||
|
}
|
||||||
|
|
||||||
response.CommunityChanges = []*communities.CommunityChanges{changes}
|
response.CommunityChanges = []*communities.CommunityChanges{changes}
|
||||||
|
|
||||||
return response, nil
|
return response, nil
|
||||||
|
@ -1288,8 +1288,16 @@ func (m *Messenger) EditCommunityTokenPermission(request *requests.EditCommunity
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
response := &MessengerResponse{}
|
response, err := m.UpdateCommunityEncryption(community)
|
||||||
response.AddCommunity(community)
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if response == nil {
|
||||||
|
response = &MessengerResponse{}
|
||||||
|
response.AddCommunity(community)
|
||||||
|
}
|
||||||
|
|
||||||
response.CommunityChanges = []*communities.CommunityChanges{changes}
|
response.CommunityChanges = []*communities.CommunityChanges{changes}
|
||||||
|
|
||||||
return response, nil
|
return response, nil
|
||||||
|
@ -1305,8 +1313,16 @@ func (m *Messenger) DeleteCommunityTokenPermission(request *requests.DeleteCommu
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
response := &MessengerResponse{}
|
response, err := m.UpdateCommunityEncryption(community)
|
||||||
response.AddCommunity(community)
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if response == nil {
|
||||||
|
response = &MessengerResponse{}
|
||||||
|
response.AddCommunity(community)
|
||||||
|
}
|
||||||
|
|
||||||
response.CommunityChanges = []*communities.CommunityChanges{changes}
|
response.CommunityChanges = []*communities.CommunityChanges{changes}
|
||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
@ -2624,19 +2640,6 @@ func (m *Messenger) RequestImportDiscordCommunity(request *requests.ImportDiscor
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if createCommunityRequest.Encrypted {
|
|
||||||
// Init hash ratchet for community
|
|
||||||
_, err = m.encryptor.GenerateHashRatchetKey(discordCommunity.ID())
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
m.cleanUpImport(discordCommunity.IDString())
|
|
||||||
importProgress.AddTaskError(discord.CommunityCreationTask, discord.Error(err.Error()))
|
|
||||||
importProgress.StopTask(discord.CommunityCreationTask)
|
|
||||||
progressUpdates <- importProgress
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
communityID := discordCommunity.IDString()
|
communityID := discordCommunity.IDString()
|
||||||
|
|
||||||
// marking import as not cancelled
|
// marking import as not cancelled
|
||||||
|
@ -3536,3 +3539,52 @@ func (m *Messenger) AddCommunityToken(token *communities.CommunityToken) (*commu
|
||||||
func (m *Messenger) UpdateCommunityTokenState(contractAddress string, deployState communities.DeployState) error {
|
func (m *Messenger) UpdateCommunityTokenState(contractAddress string, deployState communities.DeployState) error {
|
||||||
return m.communitiesManager.UpdateCommunityTokenState(contractAddress, deployState)
|
return m.communitiesManager.UpdateCommunityTokenState(contractAddress, deployState)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateCommunityEncryption takes a community and encrypts / decrypts the community
|
||||||
|
// based on TokenPermission. 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) (*MessengerResponse, error) {
|
||||||
|
if community == nil {
|
||||||
|
return nil, errors.New("community is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
becomeMemberPermissions := community.TokenPermissionsByType(protobuf.CommunityTokenPermission_BECOME_MEMBER)
|
||||||
|
isEncrypted := len(becomeMemberPermissions) > 0
|
||||||
|
|
||||||
|
// Check isEncrypted is different to Community's value
|
||||||
|
// If not different return
|
||||||
|
if community.Encrypted() == isEncrypted {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if isEncrypted {
|
||||||
|
// 🪄 The magic that encrypts a community
|
||||||
|
_, err := m.encryptor.GenerateHashRatchetKey(community.ID())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = m.SendKeyExchangeMessage(community.ID(), community.GetMemberPubkeys(), common.KeyExMsgReuse)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 🧙 There is no magic that decrypts a community, we just need to tell everyone to not use encryption
|
||||||
|
|
||||||
|
// Republish the community.
|
||||||
|
community.SetEncrypted(isEncrypted)
|
||||||
|
err := m.communitiesManager.UpdateCommunity(community)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
response := &MessengerResponse{}
|
||||||
|
response.AddCommunity(community)
|
||||||
|
|
||||||
|
return response, nil
|
||||||
|
}
|
||||||
|
|
|
@ -42,7 +42,6 @@ type CreateCommunity struct {
|
||||||
Banner images.CroppedImage `json:"banner"`
|
Banner images.CroppedImage `json:"banner"`
|
||||||
HistoryArchiveSupportEnabled bool `json:"historyArchiveSupportEnabled,omitempty"`
|
HistoryArchiveSupportEnabled bool `json:"historyArchiveSupportEnabled,omitempty"`
|
||||||
PinMessageAllMembersEnabled bool `json:"pinMessageAllMembersEnabled,omitempty"`
|
PinMessageAllMembersEnabled bool `json:"pinMessageAllMembersEnabled,omitempty"`
|
||||||
Encrypted bool `json:"encrypted,omitempty"`
|
|
||||||
Tags []string `json:"tags,omitempty"`
|
Tags []string `json:"tags,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,7 +128,7 @@ func (c *CreateCommunity) ToCommunityDescription() (*protobuf.CommunityDescripti
|
||||||
},
|
},
|
||||||
IntroMessage: c.IntroMessage,
|
IntroMessage: c.IntroMessage,
|
||||||
OutroMessage: c.OutroMessage,
|
OutroMessage: c.OutroMessage,
|
||||||
Encrypted: c.Encrypted,
|
Encrypted: false,
|
||||||
Tags: c.Tags,
|
Tags: c.Tags,
|
||||||
}
|
}
|
||||||
return description, nil
|
return description, nil
|
||||||
|
|
Loading…
Reference in New Issue