feat: make any member able to add new users to group chat
closes: #2823, #2825
This commit is contained in:
parent
eee112738b
commit
c01316524c
|
@ -255,3 +255,12 @@ func buildContact(publicKeyString string, publicKey *ecdsa.PublicKey) (*Contact,
|
|||
func contactIDFromPublicKey(key *ecdsa.PublicKey) string {
|
||||
return types.EncodeHex(crypto.FromECDSAPub(key))
|
||||
}
|
||||
|
||||
func contactIDFromPublicKeyString(key string) (string, error) {
|
||||
pubKey, err := common.HexToPubkey(key)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return contactIDFromPublicKey(pubKey), nil
|
||||
}
|
||||
|
|
|
@ -379,6 +379,8 @@ func (s *MessengerEditMessageSuite) TestEditGroupChatMessage() {
|
|||
err = s.m.SaveChat(ourChat)
|
||||
s.NoError(err)
|
||||
|
||||
s.Require().NoError(makeMutualContact(s.m, &theirMessenger.identity.PublicKey))
|
||||
|
||||
members := []string{common.PubkeyToHex(&theirMessenger.identity.PublicKey)}
|
||||
_, err = s.m.AddMembersToGroupChat(context.Background(), ourChat.ID, members)
|
||||
s.NoError(err)
|
||||
|
|
|
@ -153,6 +153,8 @@ func (s *MessengerEmojiSuite) TestEmojiPrivateGroup() {
|
|||
response, err := bob.CreateGroupChatWithMembers(context.Background(), "test", []string{})
|
||||
s.NoError(err)
|
||||
|
||||
s.Require().NoError(makeMutualContact(bob, &alice.identity.PublicKey))
|
||||
|
||||
chat := response.Chats()[0]
|
||||
members := []string{types.EncodeHex(crypto.FromECDSAPub(&alice.identity.PublicKey))}
|
||||
_, err = bob.AddMembersToGroupChat(context.Background(), chat.ID, members)
|
||||
|
|
|
@ -3,6 +3,7 @@ package protocol
|
|||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"go.uber.org/zap"
|
||||
|
@ -15,7 +16,28 @@ import (
|
|||
v1protocol "github.com/status-im/status-go/protocol/v1"
|
||||
)
|
||||
|
||||
var ErrGroupChatAddedContacts = errors.New("group-chat: can't add members who are not mutual contacts")
|
||||
|
||||
func (m *Messenger) validateAddedGroupMembers(members []string) error {
|
||||
for _, memberPubkey := range members {
|
||||
contactID, err := contactIDFromPublicKeyString(memberPubkey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
contact, _ := m.allContacts.Load(contactID)
|
||||
if contact == nil || !(contact.Added && contact.HasAddedUs) {
|
||||
return ErrGroupChatAddedContacts
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Messenger) CreateGroupChatWithMembers(ctx context.Context, name string, members []string) (*MessengerResponse, error) {
|
||||
if err := m.validateAddedGroupMembers(members); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var response MessengerResponse
|
||||
logger := m.logger.With(zap.String("site", "CreateGroupChatWithMembers"))
|
||||
logger.Info("Creating group chat", zap.String("name", name), zap.Any("members", members))
|
||||
|
@ -147,6 +169,10 @@ func (m *Messenger) RemoveMemberFromGroupChat(ctx context.Context, chatID string
|
|||
}
|
||||
|
||||
func (m *Messenger) AddMembersToGroupChat(ctx context.Context, chatID string, members []string) (*MessengerResponse, error) {
|
||||
if err := m.validateAddedGroupMembers(members); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var response MessengerResponse
|
||||
logger := m.logger.With(zap.String("site", "AddMembersFromGroupChat"))
|
||||
logger.Info("Adding members form group chat", zap.String("chatID", chatID), zap.Any("members", members))
|
||||
|
|
|
@ -0,0 +1,250 @@
|
|||
package protocol
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
"go.uber.org/zap"
|
||||
|
||||
gethbridge "github.com/status-im/status-go/eth-node/bridge/geth"
|
||||
"github.com/status-im/status-go/eth-node/crypto"
|
||||
"github.com/status-im/status-go/eth-node/types"
|
||||
"github.com/status-im/status-go/protocol/common"
|
||||
"github.com/status-im/status-go/protocol/tt"
|
||||
"github.com/status-im/status-go/waku"
|
||||
)
|
||||
|
||||
func TestGroupChatSuite(t *testing.T) {
|
||||
suite.Run(t, new(MessengerGroupChatSuite))
|
||||
}
|
||||
|
||||
type MessengerGroupChatSuite struct {
|
||||
suite.Suite
|
||||
|
||||
// If one wants to send messages between different instances of Messenger,
|
||||
// a single Waku service should be shared.
|
||||
shh types.Waku
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
func (s *MessengerGroupChatSuite) newMessenger() *Messenger {
|
||||
privateKey, err := crypto.GenerateKey()
|
||||
s.Require().NoError(err)
|
||||
|
||||
messenger, err := newMessengerWithKey(s.shh, privateKey, s.logger, []Option{})
|
||||
s.Require().NoError(err)
|
||||
|
||||
return messenger
|
||||
}
|
||||
|
||||
func (s *MessengerGroupChatSuite) startNewMessenger() *Messenger {
|
||||
messenger := s.newMessenger()
|
||||
|
||||
_, err := messenger.Start()
|
||||
s.Require().NoError(err)
|
||||
|
||||
return messenger
|
||||
}
|
||||
|
||||
func (s *MessengerGroupChatSuite) SetupTest() {
|
||||
s.logger = tt.MustCreateTestLogger()
|
||||
|
||||
config := waku.DefaultConfig
|
||||
config.MinimumAcceptedPoW = 0
|
||||
shh := waku.New(&config, s.logger)
|
||||
s.shh = gethbridge.NewGethWakuWrapper(shh)
|
||||
s.Require().NoError(shh.Start())
|
||||
}
|
||||
|
||||
func (s *MessengerGroupChatSuite) TearDownTest() {
|
||||
}
|
||||
|
||||
func (s *MessengerGroupChatSuite) createGroupChat(creator *Messenger, name string, members []string) *Chat {
|
||||
response, err := creator.CreateGroupChatWithMembers(context.Background(), name, members)
|
||||
s.Require().NoError(err)
|
||||
s.Require().Len(response.Chats(), 1)
|
||||
|
||||
chat := response.Chats()[0]
|
||||
err = creator.SaveChat(chat)
|
||||
s.Require().NoError(err)
|
||||
|
||||
return chat
|
||||
}
|
||||
|
||||
func (s *MessengerGroupChatSuite) createEmptyGroupChat(creator *Messenger, name string) *Chat {
|
||||
return s.createGroupChat(creator, name, []string{})
|
||||
}
|
||||
|
||||
func (s *MessengerGroupChatSuite) verifyGroupChatCreated(member *Messenger, expectedChatActive bool) {
|
||||
response, err := WaitOnMessengerResponse(
|
||||
member,
|
||||
func(r *MessengerResponse) bool { return len(r.Chats()) > 0 },
|
||||
"chat invitation not received",
|
||||
)
|
||||
s.Require().NoError(err)
|
||||
s.Require().Len(response.Chats(), 1)
|
||||
s.Require().True(response.Chats()[0].Active == expectedChatActive)
|
||||
}
|
||||
|
||||
func makeMutualContact(origin *Messenger, contactPubkey *ecdsa.PublicKey) error {
|
||||
contact, err := BuildContactFromPublicKey(contactPubkey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
contact.Added = true
|
||||
contact.HasAddedUs = true
|
||||
origin.allContacts.Store(contact.ID, contact)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *MessengerGroupChatSuite) makeContact(origin *Messenger, toAdd *Messenger) {
|
||||
s.Require().NoError(makeMutualContact(origin, &toAdd.identity.PublicKey))
|
||||
}
|
||||
|
||||
func (s *MessengerGroupChatSuite) TestGroupChatCreation() {
|
||||
testCases := []struct {
|
||||
name string
|
||||
creatorAddedMemberAsContact bool
|
||||
memberAddedCreatorAsContact bool
|
||||
expectedCreationSuccess bool
|
||||
expectedAddedMemberChatActive bool
|
||||
}{
|
||||
{
|
||||
name: "not added - not added",
|
||||
creatorAddedMemberAsContact: false,
|
||||
memberAddedCreatorAsContact: false,
|
||||
expectedCreationSuccess: false,
|
||||
expectedAddedMemberChatActive: false,
|
||||
},
|
||||
{
|
||||
name: "added - not added",
|
||||
creatorAddedMemberAsContact: true,
|
||||
memberAddedCreatorAsContact: false,
|
||||
expectedCreationSuccess: true,
|
||||
expectedAddedMemberChatActive: false,
|
||||
},
|
||||
{
|
||||
name: "not added - added",
|
||||
creatorAddedMemberAsContact: false,
|
||||
memberAddedCreatorAsContact: true,
|
||||
expectedCreationSuccess: false,
|
||||
expectedAddedMemberChatActive: false,
|
||||
},
|
||||
{
|
||||
name: "added - added",
|
||||
creatorAddedMemberAsContact: true,
|
||||
memberAddedCreatorAsContact: true,
|
||||
expectedCreationSuccess: true,
|
||||
expectedAddedMemberChatActive: true,
|
||||
},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
creator := s.startNewMessenger()
|
||||
member := s.startNewMessenger()
|
||||
members := []string{common.PubkeyToHex(&member.identity.PublicKey)}
|
||||
|
||||
if testCase.creatorAddedMemberAsContact {
|
||||
s.makeContact(creator, member)
|
||||
}
|
||||
if testCase.memberAddedCreatorAsContact {
|
||||
s.makeContact(member, creator)
|
||||
}
|
||||
|
||||
_, err := creator.CreateGroupChatWithMembers(context.Background(), fmt.Sprintf("test_group_chat_%d", i), members)
|
||||
if testCase.creatorAddedMemberAsContact {
|
||||
s.Require().NoError(err)
|
||||
s.verifyGroupChatCreated(member, testCase.expectedAddedMemberChatActive)
|
||||
} else {
|
||||
s.Require().EqualError(err, "group-chat: can't add members who are not mutual contacts")
|
||||
}
|
||||
|
||||
defer creator.Shutdown()
|
||||
defer member.Shutdown()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *MessengerGroupChatSuite) TestGroupChatMembersAddition() {
|
||||
testCases := []struct {
|
||||
name string
|
||||
inviterAddedMemberAsContact bool
|
||||
memberAddedInviterAsContact bool
|
||||
expectedAdditionSuccess bool
|
||||
expectedAddedMemberChatActive bool
|
||||
}{
|
||||
{
|
||||
name: "not added - not added",
|
||||
inviterAddedMemberAsContact: false,
|
||||
memberAddedInviterAsContact: false,
|
||||
expectedAdditionSuccess: false,
|
||||
expectedAddedMemberChatActive: false,
|
||||
},
|
||||
{
|
||||
name: "added - not added",
|
||||
inviterAddedMemberAsContact: true,
|
||||
memberAddedInviterAsContact: false,
|
||||
expectedAdditionSuccess: true,
|
||||
expectedAddedMemberChatActive: false,
|
||||
},
|
||||
{
|
||||
name: "not added - added",
|
||||
inviterAddedMemberAsContact: false,
|
||||
memberAddedInviterAsContact: true,
|
||||
expectedAdditionSuccess: false,
|
||||
expectedAddedMemberChatActive: false,
|
||||
},
|
||||
{
|
||||
name: "added - added",
|
||||
inviterAddedMemberAsContact: true,
|
||||
memberAddedInviterAsContact: true,
|
||||
expectedAdditionSuccess: true,
|
||||
expectedAddedMemberChatActive: true,
|
||||
},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
admin := s.startNewMessenger()
|
||||
inviter := s.startNewMessenger()
|
||||
member := s.startNewMessenger()
|
||||
members := []string{common.PubkeyToHex(&member.identity.PublicKey)}
|
||||
|
||||
if testCase.inviterAddedMemberAsContact {
|
||||
s.makeContact(inviter, member)
|
||||
}
|
||||
if testCase.memberAddedInviterAsContact {
|
||||
s.makeContact(member, inviter)
|
||||
}
|
||||
|
||||
for j, inviterIsAlsoGroupCreator := range []bool{true, false} {
|
||||
var groupChat *Chat
|
||||
if inviterIsAlsoGroupCreator {
|
||||
groupChat = s.createEmptyGroupChat(inviter, fmt.Sprintf("test_group_chat_%d_%d", i, j))
|
||||
} else {
|
||||
s.makeContact(admin, inviter)
|
||||
groupChat = s.createGroupChat(admin, fmt.Sprintf("test_group_chat_%d_%d", i, j), []string{common.PubkeyToHex(&inviter.identity.PublicKey)})
|
||||
err := inviter.SaveChat(groupChat)
|
||||
s.Require().NoError(err)
|
||||
}
|
||||
|
||||
_, err := inviter.AddMembersToGroupChat(context.Background(), groupChat.ID, members)
|
||||
if testCase.inviterAddedMemberAsContact {
|
||||
s.Require().NoError(err)
|
||||
s.verifyGroupChatCreated(member, testCase.expectedAddedMemberChatActive)
|
||||
} else {
|
||||
s.Require().EqualError(err, "group-chat: can't add members who are not mutual contacts")
|
||||
}
|
||||
}
|
||||
|
||||
defer admin.Shutdown()
|
||||
defer inviter.Shutdown()
|
||||
defer member.Shutdown()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *MessengerGroupChatSuite) TestGroupChatEdit() {
|
||||
|
||||
}
|
|
@ -499,6 +499,9 @@ func (s *MessengerSuite) TestSendPrivateGroup() {
|
|||
chat := response.Chats()[0]
|
||||
key, err := crypto.GenerateKey()
|
||||
s.NoError(err)
|
||||
|
||||
s.Require().NoError(makeMutualContact(s.m, &key.PublicKey))
|
||||
|
||||
members := []string{"0x" + hex.EncodeToString(crypto.FromECDSAPub(&key.PublicKey))}
|
||||
_, err = s.m.AddMembersToGroupChat(context.Background(), chat.ID, members)
|
||||
s.NoError(err)
|
||||
|
@ -906,6 +909,8 @@ func (s *MessengerSuite) TestRetrieveTheirPrivateGroupChat() {
|
|||
err = s.m.SaveChat(ourChat)
|
||||
s.NoError(err)
|
||||
|
||||
s.Require().NoError(makeMutualContact(s.m, &theirMessenger.identity.PublicKey))
|
||||
|
||||
members := []string{"0x" + hex.EncodeToString(crypto.FromECDSAPub(&theirMessenger.identity.PublicKey))}
|
||||
_, err = s.m.AddMembersToGroupChat(context.Background(), ourChat.ID, members)
|
||||
s.NoError(err)
|
||||
|
@ -972,6 +977,8 @@ func (s *MessengerSuite) TestChangeNameGroupChat() {
|
|||
err = s.m.SaveChat(ourChat)
|
||||
s.NoError(err)
|
||||
|
||||
s.Require().NoError(makeMutualContact(s.m, &theirMessenger.identity.PublicKey))
|
||||
|
||||
members := []string{"0x" + hex.EncodeToString(crypto.FromECDSAPub(&theirMessenger.identity.PublicKey))}
|
||||
_, err = s.m.AddMembersToGroupChat(context.Background(), ourChat.ID, members)
|
||||
s.NoError(err)
|
||||
|
@ -1027,6 +1034,8 @@ func (s *MessengerSuite) TestReInvitedToGroupChat() {
|
|||
err = s.m.SaveChat(ourChat)
|
||||
s.NoError(err)
|
||||
|
||||
s.Require().NoError(makeMutualContact(s.m, &theirMessenger.identity.PublicKey))
|
||||
|
||||
members := []string{"0x" + hex.EncodeToString(crypto.FromECDSAPub(&theirMessenger.identity.PublicKey))}
|
||||
_, err = s.m.AddMembersToGroupChat(context.Background(), ourChat.ID, members)
|
||||
s.NoError(err)
|
||||
|
@ -1486,6 +1495,11 @@ func (s *MessengerSuite) TestSharedSecretHandler() {
|
|||
|
||||
func (s *MessengerSuite) TestCreateGroupChatWithMembers() {
|
||||
members := []string{testPK}
|
||||
|
||||
pubKey, err := common.HexToPubkey(testPK)
|
||||
s.Require().NoError(err)
|
||||
s.Require().NoError(makeMutualContact(s.m, pubKey))
|
||||
|
||||
response, err := s.m.CreateGroupChatWithMembers(context.Background(), "test", members)
|
||||
s.NoError(err)
|
||||
s.Require().Len(response.Chats(), 1)
|
||||
|
@ -1510,6 +1524,8 @@ func (s *MessengerSuite) TestAddMembersToChat() {
|
|||
s.Require().NoError(err)
|
||||
members := []string{"0x" + hex.EncodeToString(crypto.FromECDSAPub(&key.PublicKey))}
|
||||
|
||||
s.Require().NoError(makeMutualContact(s.m, &key.PublicKey))
|
||||
|
||||
response, err = s.m.AddMembersToGroupChat(context.Background(), chat.ID, members)
|
||||
s.Require().NoError(err)
|
||||
s.Require().Len(response.Chats(), 1)
|
||||
|
|
|
@ -506,7 +506,8 @@ func (g Group) validateEvent(event MembershipUpdateEvent) bool {
|
|||
case protobuf.MembershipUpdateEvent_IMAGE_CHANGED:
|
||||
return g.admins.Has(event.From) && len(event.Image) > 0
|
||||
case protobuf.MembershipUpdateEvent_MEMBERS_ADDED:
|
||||
return g.admins.Has(event.From)
|
||||
// Admins and members can add new members
|
||||
return g.admins.Has(event.From) || g.members.Has(event.From)
|
||||
case protobuf.MembershipUpdateEvent_MEMBER_JOINED:
|
||||
return g.members.Has(event.From)
|
||||
case protobuf.MembershipUpdateEvent_MEMBER_REMOVED:
|
||||
|
|
Loading…
Reference in New Issue