2020-07-22 07:41:40 +00:00
|
|
|
package pushnotificationclient
|
2020-06-30 07:50:59 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"crypto/ecdsa"
|
2020-07-10 13:26:06 +00:00
|
|
|
"io/ioutil"
|
2020-06-30 07:50:59 +00:00
|
|
|
"math/rand"
|
2020-07-10 13:26:06 +00:00
|
|
|
"os"
|
2020-06-30 07:50:59 +00:00
|
|
|
"testing"
|
|
|
|
|
|
|
|
"github.com/google/uuid"
|
2020-07-22 07:41:40 +00:00
|
|
|
"github.com/stretchr/testify/suite"
|
|
|
|
|
2020-06-30 07:50:59 +00:00
|
|
|
"github.com/status-im/status-go/eth-node/crypto"
|
|
|
|
"github.com/status-im/status-go/eth-node/crypto/ecies"
|
2020-07-22 07:41:40 +00:00
|
|
|
"github.com/status-im/status-go/eth-node/types"
|
2020-07-10 07:45:40 +00:00
|
|
|
"github.com/status-im/status-go/protocol/common"
|
2020-06-30 07:50:59 +00:00
|
|
|
"github.com/status-im/status-go/protocol/protobuf"
|
2020-07-10 13:26:06 +00:00
|
|
|
"github.com/status-im/status-go/protocol/sqlite"
|
|
|
|
"github.com/status-im/status-go/protocol/tt"
|
2020-06-30 07:50:59 +00:00
|
|
|
)
|
|
|
|
|
2020-07-22 07:41:40 +00:00
|
|
|
const testDeviceToken = "test-token"
|
|
|
|
|
2020-07-10 13:26:06 +00:00
|
|
|
type ClientSuite struct {
|
|
|
|
suite.Suite
|
|
|
|
tmpFile *os.File
|
|
|
|
persistence *Persistence
|
|
|
|
identity *ecdsa.PrivateKey
|
|
|
|
installationID string
|
|
|
|
client *Client
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestClientSuite(t *testing.T) {
|
|
|
|
s := new(ClientSuite)
|
|
|
|
s.installationID = "c6ae4fde-bb65-11ea-b3de-0242ac130004"
|
|
|
|
|
|
|
|
suite.Run(t, s)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *ClientSuite) SetupTest() {
|
|
|
|
tmpFile, err := ioutil.TempFile("", "")
|
|
|
|
s.Require().NoError(err)
|
|
|
|
s.tmpFile = tmpFile
|
|
|
|
|
2022-09-27 20:27:20 +00:00
|
|
|
database, err := sqlite.Open(s.tmpFile.Name(), "", sqlite.ReducedKDFIterationsNumber)
|
2020-07-10 13:26:06 +00:00
|
|
|
s.Require().NoError(err)
|
|
|
|
s.persistence = NewPersistence(database)
|
|
|
|
|
|
|
|
identity, err := crypto.GenerateKey()
|
|
|
|
s.Require().NoError(err)
|
|
|
|
s.identity = identity
|
|
|
|
|
|
|
|
config := &Config{
|
|
|
|
Identity: identity,
|
|
|
|
Logger: tt.MustCreateTestLogger(),
|
|
|
|
RemoteNotificationsEnabled: true,
|
|
|
|
InstallationID: s.installationID,
|
|
|
|
}
|
|
|
|
|
2020-09-02 14:11:16 +00:00
|
|
|
s.client = New(s.persistence, config, nil, nil)
|
2020-07-10 13:26:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *ClientSuite) TestBuildPushNotificationRegisterMessage() {
|
2020-06-30 07:50:59 +00:00
|
|
|
mutedChatList := []string{"a", "b"}
|
2023-06-22 05:06:32 +00:00
|
|
|
blockedChatList := []string{"c", "d"}
|
2020-06-30 07:50:59 +00:00
|
|
|
|
|
|
|
// build chat lish hashes
|
|
|
|
var mutedChatListHashes [][]byte
|
|
|
|
for _, chatID := range mutedChatList {
|
2020-07-10 07:45:40 +00:00
|
|
|
mutedChatListHashes = append(mutedChatListHashes, common.Shake256([]byte(chatID)))
|
2020-06-30 07:50:59 +00:00
|
|
|
}
|
2023-06-22 05:06:32 +00:00
|
|
|
// Build Blocked chat list hashes
|
|
|
|
var blockedChatListHashes [][]byte
|
|
|
|
for _, chatID := range blockedChatList {
|
|
|
|
blockedChatListHashes = append(blockedChatListHashes, common.Shake256([]byte(chatID)))
|
|
|
|
}
|
2020-06-30 07:50:59 +00:00
|
|
|
|
|
|
|
contactKey, err := crypto.GenerateKey()
|
2020-07-10 13:26:06 +00:00
|
|
|
s.Require().NoError(err)
|
2020-06-30 07:50:59 +00:00
|
|
|
contactIDs := []*ecdsa.PublicKey{&contactKey.PublicKey}
|
|
|
|
|
2020-09-02 14:11:16 +00:00
|
|
|
options := &RegistrationOptions{
|
2023-06-22 05:06:32 +00:00
|
|
|
ContactIDs: contactIDs,
|
|
|
|
MutedChatIDs: mutedChatList,
|
|
|
|
BlockedChatIDs: blockedChatList,
|
2020-09-02 14:11:16 +00:00
|
|
|
}
|
|
|
|
|
2020-06-30 07:50:59 +00:00
|
|
|
// Set random generator for uuid
|
|
|
|
var seed int64 = 1
|
2020-12-28 09:09:45 +00:00
|
|
|
uuid.SetRand(rand.New(rand.NewSource(seed))) // nolint: gosec
|
2020-06-30 07:50:59 +00:00
|
|
|
|
|
|
|
// Get token
|
|
|
|
expectedUUID := uuid.New().String()
|
|
|
|
|
2020-07-17 12:29:51 +00:00
|
|
|
// Reset random generator
|
2020-12-28 09:09:45 +00:00
|
|
|
uuid.SetRand(rand.New(rand.NewSource(seed))) // nolint: gosec
|
2020-07-17 12:29:51 +00:00
|
|
|
|
2020-07-22 07:41:40 +00:00
|
|
|
s.client.deviceToken = testDeviceToken
|
2020-07-17 12:29:51 +00:00
|
|
|
// Set reader
|
|
|
|
s.client.reader = bytes.NewReader([]byte(expectedUUID))
|
|
|
|
|
2020-09-02 14:11:16 +00:00
|
|
|
registration := &protobuf.PushNotificationRegistration{
|
2020-07-17 12:29:51 +00:00
|
|
|
Version: 1,
|
|
|
|
AccessToken: expectedUUID,
|
2020-07-22 07:41:40 +00:00
|
|
|
DeviceToken: testDeviceToken,
|
2020-07-17 12:29:51 +00:00
|
|
|
InstallationId: s.installationID,
|
|
|
|
Enabled: true,
|
2023-06-22 05:06:32 +00:00
|
|
|
MutedChatList: mutedChatListHashes,
|
|
|
|
BlockedChatList: blockedChatListHashes,
|
2020-07-17 12:29:51 +00:00
|
|
|
}
|
|
|
|
|
2020-09-02 14:11:16 +00:00
|
|
|
actualMessage, err := s.client.buildPushNotificationRegistrationMessage(options)
|
2020-07-17 12:29:51 +00:00
|
|
|
s.Require().NoError(err)
|
|
|
|
|
2020-09-02 14:11:16 +00:00
|
|
|
s.Require().Equal(registration, actualMessage)
|
2020-07-17 12:29:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *ClientSuite) TestBuildPushNotificationRegisterMessageAllowFromContactsOnly() {
|
|
|
|
mutedChatList := []string{"a", "b"}
|
2020-09-02 14:11:16 +00:00
|
|
|
publicChatList := []string{"c", "d"}
|
2023-06-22 05:06:32 +00:00
|
|
|
blockedChatList := []string{"e", "f"}
|
2020-07-17 12:29:51 +00:00
|
|
|
|
2020-09-02 14:11:16 +00:00
|
|
|
// build muted chat lish hashes
|
2020-07-17 12:29:51 +00:00
|
|
|
var mutedChatListHashes [][]byte
|
|
|
|
for _, chatID := range mutedChatList {
|
|
|
|
mutedChatListHashes = append(mutedChatListHashes, common.Shake256([]byte(chatID)))
|
|
|
|
}
|
|
|
|
|
2023-06-22 05:06:32 +00:00
|
|
|
// build blocked chat lish hashes
|
|
|
|
var blockedChatListHashes [][]byte
|
|
|
|
for _, chatID := range blockedChatList {
|
|
|
|
blockedChatListHashes = append(blockedChatListHashes, common.Shake256([]byte(chatID)))
|
|
|
|
}
|
|
|
|
|
2020-09-02 14:11:16 +00:00
|
|
|
// build public chat lish hashes
|
|
|
|
var publicChatListHashes [][]byte
|
|
|
|
for _, chatID := range publicChatList {
|
|
|
|
publicChatListHashes = append(publicChatListHashes, common.Shake256([]byte(chatID)))
|
|
|
|
}
|
|
|
|
|
2020-07-17 12:29:51 +00:00
|
|
|
contactKey, err := crypto.GenerateKey()
|
|
|
|
s.Require().NoError(err)
|
|
|
|
contactIDs := []*ecdsa.PublicKey{&contactKey.PublicKey}
|
2020-09-02 14:11:16 +00:00
|
|
|
options := &RegistrationOptions{
|
2023-06-22 05:06:32 +00:00
|
|
|
ContactIDs: contactIDs,
|
|
|
|
MutedChatIDs: mutedChatList,
|
|
|
|
BlockedChatIDs: blockedChatList,
|
|
|
|
PublicChatIDs: publicChatList,
|
2020-09-02 14:11:16 +00:00
|
|
|
}
|
2020-07-17 12:29:51 +00:00
|
|
|
|
|
|
|
// Set random generator for uuid
|
|
|
|
var seed int64 = 1
|
2020-12-28 09:09:45 +00:00
|
|
|
uuid.SetRand(rand.New(rand.NewSource(seed))) // nolint: gosec
|
2020-07-17 12:29:51 +00:00
|
|
|
|
|
|
|
// Get token
|
|
|
|
expectedUUID := uuid.New().String()
|
|
|
|
|
2020-06-30 07:50:59 +00:00
|
|
|
// set up reader
|
|
|
|
reader := bytes.NewReader([]byte(expectedUUID))
|
|
|
|
|
2020-07-10 13:26:06 +00:00
|
|
|
sharedKey, err := ecies.ImportECDSA(s.identity).GenerateShared(
|
2020-06-30 07:50:59 +00:00
|
|
|
ecies.ImportECDSAPublic(&contactKey.PublicKey),
|
|
|
|
accessTokenKeyLength,
|
|
|
|
accessTokenKeyLength,
|
|
|
|
)
|
2020-07-10 13:26:06 +00:00
|
|
|
s.Require().NoError(err)
|
2020-06-30 07:50:59 +00:00
|
|
|
// build encrypted token
|
|
|
|
encryptedToken, err := encryptAccessToken([]byte(expectedUUID), sharedKey, reader)
|
2020-07-10 13:26:06 +00:00
|
|
|
s.Require().NoError(err)
|
2020-06-30 07:50:59 +00:00
|
|
|
|
|
|
|
// Reset random generator
|
2020-12-28 09:09:45 +00:00
|
|
|
uuid.SetRand(rand.New(rand.NewSource(seed))) // nolint: gosec
|
2020-06-30 07:50:59 +00:00
|
|
|
|
2020-07-22 07:41:40 +00:00
|
|
|
s.client.config.AllowFromContactsOnly = true
|
|
|
|
s.client.deviceToken = testDeviceToken
|
2020-06-30 07:50:59 +00:00
|
|
|
// Set reader
|
2020-07-10 13:26:06 +00:00
|
|
|
s.client.reader = bytes.NewReader([]byte(expectedUUID))
|
2020-06-30 07:50:59 +00:00
|
|
|
|
2020-09-02 14:11:16 +00:00
|
|
|
registration := &protobuf.PushNotificationRegistration{
|
|
|
|
Version: 1,
|
|
|
|
AccessToken: expectedUUID,
|
|
|
|
DeviceToken: testDeviceToken,
|
|
|
|
InstallationId: s.installationID,
|
|
|
|
AllowFromContactsOnly: true,
|
|
|
|
Enabled: true,
|
2023-06-22 05:06:32 +00:00
|
|
|
BlockedChatList: blockedChatListHashes,
|
|
|
|
MutedChatList: mutedChatListHashes,
|
2020-09-02 14:11:16 +00:00
|
|
|
AllowedKeyList: [][]byte{encryptedToken},
|
|
|
|
AllowedMentionsChatList: publicChatListHashes,
|
2020-06-30 07:50:59 +00:00
|
|
|
}
|
|
|
|
|
2020-09-02 14:11:16 +00:00
|
|
|
actualMessage, err := s.client.buildPushNotificationRegistrationMessage(options)
|
2020-07-10 13:26:06 +00:00
|
|
|
s.Require().NoError(err)
|
|
|
|
|
2020-09-02 14:11:16 +00:00
|
|
|
s.Require().Equal(registration, actualMessage)
|
2020-07-10 13:26:06 +00:00
|
|
|
}
|
2020-06-30 07:50:59 +00:00
|
|
|
|
2020-07-22 07:41:40 +00:00
|
|
|
func (s *ClientSuite) TestHandleMessageScheduled() {
|
2020-07-10 13:26:06 +00:00
|
|
|
messageID := []byte("message-id")
|
|
|
|
chatID := "chat-id"
|
|
|
|
installationID1 := "1"
|
|
|
|
installationID2 := "2"
|
2020-07-22 07:41:40 +00:00
|
|
|
rawMessage := &common.RawMessage{
|
|
|
|
ID: types.EncodeHex(messageID),
|
|
|
|
SendPushNotification: true,
|
|
|
|
LocalChatID: chatID,
|
|
|
|
}
|
2020-07-10 13:26:06 +00:00
|
|
|
|
2022-07-15 13:19:37 +00:00
|
|
|
event := &common.MessageEvent{
|
|
|
|
RawMessage: rawMessage,
|
|
|
|
}
|
|
|
|
|
|
|
|
s.Require().NoError(s.client.handleMessageScheduled(event))
|
2020-07-10 13:26:06 +00:00
|
|
|
|
|
|
|
key1, err := crypto.GenerateKey()
|
|
|
|
s.Require().NoError(err)
|
|
|
|
|
|
|
|
// First time, should notify
|
|
|
|
response, err := s.client.shouldNotifyOn(&key1.PublicKey, installationID1, messageID)
|
|
|
|
s.Require().NoError(err)
|
|
|
|
s.Require().True(response)
|
|
|
|
|
|
|
|
// Save notification
|
2020-09-02 14:11:16 +00:00
|
|
|
s.Require().NoError(s.client.notifiedOn(&key1.PublicKey, installationID1, messageID, chatID, protobuf.PushNotification_MESSAGE))
|
2020-07-10 13:26:06 +00:00
|
|
|
|
|
|
|
// Second time, should not notify
|
|
|
|
response, err = s.client.shouldNotifyOn(&key1.PublicKey, installationID1, messageID)
|
|
|
|
s.Require().NoError(err)
|
|
|
|
s.Require().False(response)
|
|
|
|
|
|
|
|
// Different installationID
|
|
|
|
response, err = s.client.shouldNotifyOn(&key1.PublicKey, installationID2, messageID)
|
|
|
|
s.Require().NoError(err)
|
|
|
|
s.Require().True(response)
|
|
|
|
|
|
|
|
key2, err := crypto.GenerateKey()
|
|
|
|
s.Require().NoError(err)
|
|
|
|
// different key, should notify
|
|
|
|
response, err = s.client.shouldNotifyOn(&key2.PublicKey, installationID1, messageID)
|
|
|
|
s.Require().NoError(err)
|
|
|
|
s.Require().True(response)
|
|
|
|
|
|
|
|
// non tracked message id
|
|
|
|
response, err = s.client.shouldNotifyOn(&key1.PublicKey, installationID1, []byte("not-existing"))
|
|
|
|
s.Require().NoError(err)
|
|
|
|
s.Require().False(response)
|
2020-06-30 07:50:59 +00:00
|
|
|
}
|
2020-07-15 07:23:31 +00:00
|
|
|
|
|
|
|
func (s *ClientSuite) TestShouldRefreshToken() {
|
|
|
|
key1, err := crypto.GenerateKey()
|
|
|
|
s.Require().NoError(err)
|
|
|
|
key2, err := crypto.GenerateKey()
|
|
|
|
s.Require().NoError(err)
|
|
|
|
key3, err := crypto.GenerateKey()
|
|
|
|
s.Require().NoError(err)
|
|
|
|
key4, err := crypto.GenerateKey()
|
|
|
|
s.Require().NoError(err)
|
|
|
|
|
|
|
|
// Contacts are added
|
2020-07-30 07:19:34 +00:00
|
|
|
s.Require().False(s.client.shouldRefreshToken([]*ecdsa.PublicKey{&key1.PublicKey, &key2.PublicKey}, []*ecdsa.PublicKey{&key1.PublicKey, &key2.PublicKey, &key3.PublicKey, &key4.PublicKey}, true, true))
|
2020-07-15 07:23:31 +00:00
|
|
|
|
|
|
|
// everything the same
|
2020-07-30 07:19:34 +00:00
|
|
|
s.Require().False(s.client.shouldRefreshToken([]*ecdsa.PublicKey{&key1.PublicKey, &key2.PublicKey}, []*ecdsa.PublicKey{&key2.PublicKey, &key1.PublicKey}, true, true))
|
2020-07-15 07:23:31 +00:00
|
|
|
|
|
|
|
// A contact is removed
|
2020-07-30 07:19:34 +00:00
|
|
|
s.Require().True(s.client.shouldRefreshToken([]*ecdsa.PublicKey{&key1.PublicKey, &key2.PublicKey}, []*ecdsa.PublicKey{&key2.PublicKey}, true, true))
|
2020-07-15 07:23:31 +00:00
|
|
|
|
2020-07-30 07:19:34 +00:00
|
|
|
// allow from contacts only is disabled
|
|
|
|
s.Require().False(s.client.shouldRefreshToken([]*ecdsa.PublicKey{&key1.PublicKey, &key2.PublicKey}, []*ecdsa.PublicKey{&key2.PublicKey, &key1.PublicKey}, true, false))
|
|
|
|
|
|
|
|
// allow from contacts only is enabled
|
|
|
|
s.Require().True(s.client.shouldRefreshToken([]*ecdsa.PublicKey{&key1.PublicKey, &key2.PublicKey}, []*ecdsa.PublicKey{&key2.PublicKey, &key1.PublicKey}, false, true))
|
2020-07-15 07:23:31 +00:00
|
|
|
}
|
2022-07-15 13:19:37 +00:00
|
|
|
|
|
|
|
func (s *ClientSuite) TestHandleMessageScheduledFromPairedDevice() {
|
|
|
|
messageID := []byte("message-id")
|
|
|
|
installationID1 := "1"
|
|
|
|
|
|
|
|
// Should return nil
|
|
|
|
response, err := s.client.shouldNotifyOn(&s.identity.PublicKey, installationID1, messageID)
|
|
|
|
s.Require().NoError(err)
|
|
|
|
s.Require().False(response)
|
|
|
|
}
|