status-go/protocol/messenger_pin_messages.go
Pascal Precht 714c03c635 feat: introduce CommunityAdminSettings in CommunityDescription
This allows to store community admin settings that are meant to be propagated
to community members (as opposed to the already existing
`CommunitySettings` which are considered local to every account).

The first setting introduced as part of this commit is one that enables
community admins to configure whether or not members of the community
are allowed to pin messages in community channels.

Prior to this commit, this was not restricted at all on the protocol
level and only enforced by clients via UI (e.g. members don't see an
option to pin messages, although they could).

This config setting now ensures that:

1. If turned off, members cannot send a pin message
2. If turned off, pin messages from members are not handled/processed

This is needed by https://github.com/status-im/status-desktop/issues/5662
2022-05-18 09:58:11 +02:00

123 lines
3.2 KiB
Go

package protocol
import (
"bytes"
"context"
"crypto/ecdsa"
"crypto/sha256"
"errors"
"fmt"
gethcommon "github.com/ethereum/go-ethereum/common"
"github.com/status-im/status-go/eth-node/crypto"
"github.com/status-im/status-go/protocol/common"
"github.com/status-im/status-go/protocol/protobuf"
)
// SendPinMessage sends the PinMessage to the corresponding chat
func (m *Messenger) SendPinMessage(ctx context.Context, message *common.PinMessage) (*MessengerResponse, error) {
m.mutex.Lock()
defer m.mutex.Unlock()
return m.sendPinMessage(ctx, message)
}
func (m *Messenger) sendPinMessage(ctx context.Context, message *common.PinMessage) (*MessengerResponse, error) {
var response MessengerResponse
// A valid added chat is required.
chat, ok := m.allChats.Load(message.ChatId)
if !ok {
return nil, errors.New("chat not found")
}
if chat.CommunityChat() {
community, err := m.communitiesManager.GetByIDString(chat.CommunityID)
if err != nil {
return nil, err
}
isMemberAdmin := community.IsMemberAdmin(&m.identity.PublicKey)
pinMessageAllowed := community.AllowsAllMembersToPinMessage()
if !pinMessageAllowed && !isMemberAdmin {
return nil, errors.New("member can't pin message")
}
}
err := m.handleStandaloneChatIdentity(chat)
if err != nil {
return nil, err
}
err = extendPinMessageFromChat(message, chat, &m.identity.PublicKey, m.getTimesource())
if err != nil {
return nil, err
}
message.ID, err = generatePinMessageID(&m.identity.PublicKey, message, chat)
if err != nil {
return nil, err
}
encodedMessage, err := m.encodeChatEntity(chat, message)
if err != nil {
return nil, err
}
rawMessage := common.RawMessage{
LocalChatID: chat.ID,
Payload: encodedMessage,
MessageType: protobuf.ApplicationMetadataMessage_PIN_MESSAGE,
SkipGroupMessageWrap: true,
ResendAutomatically: true,
}
_, err = m.dispatchMessage(ctx, rawMessage)
if err != nil {
return nil, err
}
err = m.persistence.SavePinMessages([]*common.PinMessage{message})
if err != nil {
return nil, err
}
response.AddPinMessage(message)
response.AddChat(chat)
return &response, m.saveChat(chat)
}
func (m *Messenger) PinnedMessageByChatID(chatID, cursor string, limit int) ([]*common.PinnedMessage, string, error) {
return m.persistence.PinnedMessageByChatID(chatID, cursor, limit)
}
func (m *Messenger) SavePinMessages(messages []*common.PinMessage) error {
return m.persistence.SavePinMessages(messages)
}
func generatePinMessageID(pubKey *ecdsa.PublicKey, pm *common.PinMessage, chat *Chat) (string, error) {
data := gethcommon.FromHex(pm.MessageId)
switch {
case chat.ChatType == ChatTypeOneToOne:
ourPubKey := crypto.FromECDSAPub(pubKey)
tmpPubKey, err := chat.PublicKey()
if err != nil {
return "", err
}
theirPubKey := crypto.FromECDSAPub(tmpPubKey)
if bytes.Compare(ourPubKey, theirPubKey) < 0 {
data = append(data, ourPubKey...) // our key
data = append(data, theirPubKey...) // their key
} else {
data = append(data, theirPubKey...) // their key
data = append(data, ourPubKey...) // our key
}
default:
data = append(data, []byte(chat.ID)...)
}
id := sha256.Sum256(data)
idString := fmt.Sprintf("%x", id)
return idString, nil
}